From 0019f795f890787e05f1cb9b3b87c0ca24e8892b Mon Sep 17 00:00:00 2001 From: Tian jianyong <11429339@qq.com> Date: Tue, 25 Feb 2025 12:18:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=BF=80=E5=85=89=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 13 + .../LaserSemiActiveGuidanceSystemCodeTests.cs | 233 ++++++++++++++ .../src/Indicator/LaserDesignatorCodeTests.cs | 150 +++++++++ .../LaserSemiActiveGuidedMissileCodeTests.cs | 283 +++++++++++++++++ .../src/Utils/LaserCodeTests.cs | 155 +++++++++ .../src/Guidance/BasicGuidanceSystem.cs | 58 ++-- .../Guidance/InfraredCommandGuidanceSystem.cs | 7 +- .../Guidance/InfraredImagingGuidanceSystem.cs | 16 +- .../Guidance/LaserBeamRiderGuidanceSystem.cs | 6 +- .../Guidance/LaserSemiActiveGuidanceSystem.cs | 299 +++++++++++++++++- .../Guidance/MillimeterWaveGuidanceSystem.cs | 16 +- ThreatSource/src/Indicator/LaserDesignator.cs | 127 +++++++- .../MIssile/InfraredCommandGuidedMissile.cs | 6 +- .../InfraredImagingTerminalGuidedMissile.cs | 6 +- .../src/MIssile/LaserBeamRiderMissile.cs | 6 +- .../MIssile/LaserSemiActiveGuidedMissile.cs | 117 ++++++- .../MillimeterWaveTerminalGuidedMissile.cs | 7 +- .../MIssile/TerminalSensitiveSubmunition.cs | 20 +- .../src/Simulation/SimulationEvents.cs | 114 ++++++- .../Testing/TestSimulationAdapter.cs | 10 + ThreatSource/src/Utils/LaserCode.cs | 139 ++++++++ VERSION | 2 +- docs/articles/threat-source-spec.md | 4 +- docs/project/develop_log.md | 4 + 24 files changed, 1719 insertions(+), 79 deletions(-) create mode 100644 ThreatSource.Tests/src/Guidance/LaserSemiActiveGuidanceSystemCodeTests.cs create mode 100644 ThreatSource.Tests/src/Indicator/LaserDesignatorCodeTests.cs create mode 100644 ThreatSource.Tests/src/Missile/LaserSemiActiveGuidedMissileCodeTests.cs create mode 100644 ThreatSource.Tests/src/Utils/LaserCodeTests.cs create mode 100644 ThreatSource/src/Utils/LaserCode.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 857a9ec..31d5b2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,19 @@ - 实现实时仿真数据同步 - 处理不同时间步长的协调 +## [0.2.4] - 2025-02-25 + +### 功能优化 +- 增加了激光半主动导弹和激光目标指示器的激光编码功能 +- 增加了对应的测试用例 +- 支持六种激光编码类型 + - PRF:脉冲重复频率编码 + - PPM:脉冲位置调制 + - PWM:脉冲宽度调制 + - PseudoRandom:伪随机编码 + - Frequency:频率编码 + - Phase:相位编码 + ## [0.2.3] - 2025-02-17 ### 功能优化 diff --git a/ThreatSource.Tests/src/Guidance/LaserSemiActiveGuidanceSystemCodeTests.cs b/ThreatSource.Tests/src/Guidance/LaserSemiActiveGuidanceSystemCodeTests.cs new file mode 100644 index 0000000..4a22a5a --- /dev/null +++ b/ThreatSource.Tests/src/Guidance/LaserSemiActiveGuidanceSystemCodeTests.cs @@ -0,0 +1,233 @@ +using Xunit; +using ThreatSource.Guidance; +using ThreatSource.Simulation; +using ThreatSource.Utils; +using ThreatSource.Simulation.Testing; + +namespace ThreatSource.Tests.Guidance +{ + public class LaserSemiActiveGuidanceSystemCodeTests + { + private readonly SimulationManager _simulationManager; + private readonly TestSimulationAdapter _testAdapter; + private readonly LaserSemiActiveGuidanceSystem _guidanceSystem; + + public LaserSemiActiveGuidanceSystemCodeTests() + { + _simulationManager = new SimulationManager(); + _testAdapter = new TestSimulationAdapter(_simulationManager); + _simulationManager.SetSimulationAdapter(_testAdapter); + + _guidanceSystem = new LaserSemiActiveGuidanceSystem( + "guidance1", + 50, // maxAcceleration + 3, // guidanceCoefficient + _simulationManager + ); + _guidanceSystem.ParentId = "missile1"; + _simulationManager.RegisterEntity("guidance1", _guidanceSystem); + } + + [Fact] + public void SetExpectedLaserCode_SetsCodeCorrectly() + { + // Act + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the ProcessLaserIlluminationEvent test + } + + [Fact] + public void AddExpectedCodeParameter_AddsParameterCorrectly() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PPM, 5678); + + // Act + _guidanceSystem.AddExpectedCodeParameter("PulseWidth", 0.001); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the ProcessLaserIlluminationEvent test + } + + [Fact] + public void SetCodeMatchRequired_SetsRequirementCorrectly() + { + // Act + _guidanceSystem.SetCodeMatchRequired(true); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the ProcessLaserIlluminationEvent test + } + + [Fact] + public void ProcessLaserIlluminationEvent_WithMatchingCode_PublishesMatchEvent() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + _guidanceSystem.SetCodeMatchRequired(true); + + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + } + }; + + // Act + _guidanceSystem.ProcessLaserIlluminationEvent(illuminationEvent); + + // Assert + var matchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(matchEvents); + + var lastEvent = matchEvents[matchEvents.Count - 1]; + Assert.Equal("missile1", lastEvent.MissileId); + Assert.Equal("laser1", lastEvent.DesignatorId); + Assert.NotNull(lastEvent.MatchedCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.MatchedCode.CodeType); + Assert.Equal(1234, lastEvent.MatchedCode.CodeValue); + } + + [Fact] + public void ProcessLaserIlluminationEvent_WithMismatchingCode_PublishesMismatchEvent() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + _guidanceSystem.SetCodeMatchRequired(true); + + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 5678 // Different code value + } + }; + + // Act + _guidanceSystem.ProcessLaserIlluminationEvent(illuminationEvent); + + // Assert + var mismatchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(mismatchEvents); + + var lastEvent = mismatchEvents[mismatchEvents.Count - 1]; + Assert.Equal("missile1", lastEvent.MissileId); + Assert.Equal("laser1", lastEvent.DesignatorId); + Assert.NotNull(lastEvent.ExpectedCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.ExpectedCode.CodeType); + Assert.Equal(1234, lastEvent.ExpectedCode.CodeValue); + Assert.NotNull(lastEvent.ReceivedCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.ReceivedCode.CodeType); + Assert.Equal(5678, lastEvent.ReceivedCode.CodeValue); + } + + [Fact] + public void ProcessLaserIlluminationEvent_WithDifferentCodeType_PublishesMismatchEvent() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + _guidanceSystem.SetCodeMatchRequired(true); + + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PPM, // Different code type + CodeValue = 1234 + } + }; + + // Act + _guidanceSystem.ProcessLaserIlluminationEvent(illuminationEvent); + + // Assert + var mismatchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(mismatchEvents); + + var lastEvent = mismatchEvents[mismatchEvents.Count - 1]; + Assert.Equal("missile1", lastEvent.MissileId); + Assert.Equal("laser1", lastEvent.DesignatorId); + Assert.NotNull(lastEvent.ExpectedCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.ExpectedCode.CodeType); + Assert.Equal(1234, lastEvent.ExpectedCode.CodeValue); + Assert.NotNull(lastEvent.ReceivedCode); + Assert.Equal(LaserCodeType.PPM, lastEvent.ReceivedCode.CodeType); + Assert.Equal(1234, lastEvent.ReceivedCode.CodeValue); + } + + [Fact] + public void ProcessLaserIlluminationEvent_WithCodeDisabled_IgnoresCode() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + _guidanceSystem.SetCodeMatchRequired(false); // Not required + + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = false, // Code disabled + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 5678 // Different code value + } + }; + + // Act + _guidanceSystem.ProcessLaserIlluminationEvent(illuminationEvent); + + // Assert - No mismatch events should be published + var mismatchEvents = _testAdapter.GetPublishedEvents(); + Assert.Empty(mismatchEvents); + } + + [Fact] + public void ProcessLaserIlluminationUpdateEvent_WithMatchingCode_PublishesMatchEvent() + { + // Arrange + _guidanceSystem.SetExpectedLaserCode(LaserCodeType.PRF, 1234); + _guidanceSystem.SetCodeMatchRequired(true); + + var illuminationEvent = new LaserIlluminationUpdateEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + } + }; + + // Act + _guidanceSystem.ProcessLaserIlluminationUpdateEvent(illuminationEvent); + + // Assert + var matchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(matchEvents); + + var lastEvent = matchEvents[matchEvents.Count - 1]; + Assert.Equal("missile1", lastEvent.MissileId); + Assert.Equal("laser1", lastEvent.DesignatorId); + Assert.NotNull(lastEvent.MatchedCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.MatchedCode.CodeType); + Assert.Equal(1234, lastEvent.MatchedCode.CodeValue); + } + } +} \ No newline at end of file diff --git a/ThreatSource.Tests/src/Indicator/LaserDesignatorCodeTests.cs b/ThreatSource.Tests/src/Indicator/LaserDesignatorCodeTests.cs new file mode 100644 index 0000000..ecb54b9 --- /dev/null +++ b/ThreatSource.Tests/src/Indicator/LaserDesignatorCodeTests.cs @@ -0,0 +1,150 @@ +using Xunit; +using ThreatSource.Indicator; +using ThreatSource.Simulation; +using ThreatSource.Utils; +using ThreatSource.Simulation.Testing; +using ThreatSource.Target; + +namespace ThreatSource.Tests.Indicator +{ + public class LaserDesignatorCodeTests + { + private readonly SimulationManager _simulationManager; + private readonly TestSimulationAdapter _testAdapter; + private readonly LaserDesignator _laserDesignator; + private readonly LaserDesignatorConfig _config; + private readonly Tank _tank; + + public LaserDesignatorCodeTests() + { + _simulationManager = new SimulationManager(); + _testAdapter = new TestSimulationAdapter(_simulationManager); + _simulationManager.SetSimulationAdapter(_testAdapter); + + _config = new LaserDesignatorConfig + { + Id = "laser1", + InitialPosition = new Vector3D(0, 0, 0), + LaserPower = 1000, + LaserDivergenceAngle = 0.001 + }; + + _tank = new Tank("target1", new Vector3D(100, 0, 0), 0, _simulationManager); + _simulationManager.RegisterEntity("target1", _tank); + + _laserDesignator = new LaserDesignator( + "laser1", + "target1", + "missile1", + 10, + _config, + _simulationManager + ); + _simulationManager.RegisterEntity("laser1", _laserDesignator); + } + + [Fact] + public void SetLaserCode_SetsCodeCorrectly() + { + // Arrange + _laserDesignator.Activate(); + + // Act + _laserDesignator.SetLaserCode(LaserCodeType.PRF, 1234); + _laserDesignator.Update(0.1); + + // Assert + var events = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(events); + + var lastEvent = events[events.Count - 1]; + Assert.True(lastEvent.IsCodeEnabled); + Assert.NotNull(lastEvent.LaserCode); + Assert.Equal(LaserCodeType.PRF, lastEvent.LaserCode.CodeType); + Assert.Equal(1234, lastEvent.LaserCode.CodeValue); + } + + [Fact] + public void AddCodeParameter_AddsParameterCorrectly() + { + // Arrange + _laserDesignator.Activate(); + _laserDesignator.SetLaserCode(LaserCodeType.PPM, 5678); + + // Act + _laserDesignator.AddCodeParameter("PulseWidth", 0.001); + _laserDesignator.Update(0.1); + + // Assert + var events = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(events); + + var lastEvent = events[events.Count - 1]; + Assert.True(lastEvent.IsCodeEnabled); + Assert.NotNull(lastEvent.LaserCode); + Assert.Equal(LaserCodeType.PPM, lastEvent.LaserCode.CodeType); + Assert.Equal(5678, lastEvent.LaserCode.CodeValue); + Assert.True(lastEvent.LaserCode.Parameters.ContainsKey("PulseWidth")); + Assert.Equal(0.001, lastEvent.LaserCode.Parameters["PulseWidth"]); + } + + [Fact] + public void EnableCode_EnablesAndDisablesCodeCorrectly() + { + // Arrange + _laserDesignator.Activate(); + _laserDesignator.SetLaserCode(LaserCodeType.PRF, 1234); + + // Act - Disable code + _laserDesignator.EnableCode(false); + _laserDesignator.Update(0.1); + + // Assert - Code should be disabled + var eventsAfterDisable = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(eventsAfterDisable); + var lastEventAfterDisable = eventsAfterDisable[eventsAfterDisable.Count - 1]; + Assert.False(lastEventAfterDisable.IsCodeEnabled); + + // Act - Enable code again + _laserDesignator.EnableCode(true); + _laserDesignator.Update(0.1); + + // Assert - Code should be enabled + var eventsAfterEnable = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(eventsAfterEnable); + var lastEventAfterEnable = eventsAfterEnable[eventsAfterEnable.Count - 1]; + Assert.True(lastEventAfterEnable.IsCodeEnabled); + } + + [Fact] + public void LaserIlluminationEvents_IncludeCodeInformation() + { + // Arrange + _laserDesignator.SetLaserCode(LaserCodeType.PRF, 1234); + + // Act - Start illumination + _laserDesignator.Activate(); + + // Assert - Start event should include code + var startEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(startEvents); + var startEvent = startEvents[startEvents.Count - 1]; + Assert.True(startEvent.IsCodeEnabled); + Assert.NotNull(startEvent.LaserCode); + Assert.Equal(LaserCodeType.PRF, startEvent.LaserCode.CodeType); + Assert.Equal(1234, startEvent.LaserCode.CodeValue); + + // Act - Update illumination + _laserDesignator.Update(0.1); + + // Assert - Update event should include code + var updateEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(updateEvents); + var updateEvent = updateEvents[updateEvents.Count - 1]; + Assert.True(updateEvent.IsCodeEnabled); + Assert.NotNull(updateEvent.LaserCode); + Assert.Equal(LaserCodeType.PRF, updateEvent.LaserCode.CodeType); + Assert.Equal(1234, updateEvent.LaserCode.CodeValue); + } + } +} \ No newline at end of file diff --git a/ThreatSource.Tests/src/Missile/LaserSemiActiveGuidedMissileCodeTests.cs b/ThreatSource.Tests/src/Missile/LaserSemiActiveGuidedMissileCodeTests.cs new file mode 100644 index 0000000..fe37ffb --- /dev/null +++ b/ThreatSource.Tests/src/Missile/LaserSemiActiveGuidedMissileCodeTests.cs @@ -0,0 +1,283 @@ +using Xunit; +using ThreatSource.Missile; +using ThreatSource.Simulation; +using ThreatSource.Utils; +using ThreatSource.Simulation.Testing; +using ThreatSource.Guidance; +using ThreatSource.Indicator; +using ThreatSource.Target; + +namespace ThreatSource.Tests.Missile +{ + public class LaserSemiActiveGuidedMissileCodeTests + { + private readonly SimulationManager _simulationManager; + private readonly TestSimulationAdapter _testAdapter; + private readonly LaserSemiActiveGuidedMissile _missile; + private readonly MissileProperties _properties; + private readonly MockLaserDesignator _laserDesignator; + private readonly MockTarget _target; + + public LaserSemiActiveGuidedMissileCodeTests() + { + _simulationManager = new SimulationManager(); + _testAdapter = new TestSimulationAdapter(_simulationManager); + _simulationManager.SetSimulationAdapter(_testAdapter); + + _properties = new MissileProperties + { + Id = "missile1", + InitialPosition = new Vector3D(0, 0, 0), + InitialOrientation = new Orientation(0, 0, 0), + InitialSpeed = 100, + MaxSpeed = 1000, + MaxFlightTime = 100, + MaxFlightDistance = 10000, + MaxAcceleration = 50, + ProportionalNavigationCoefficient = 3, + Mass = 100, + ExplosionRadius = 10, + HitProbability = 0.9, + Type = MissileType.LaserSemiActiveGuidance + }; + + // 创建并注册模拟的激光指示器 + _laserDesignator = new MockLaserDesignator("laser1", _simulationManager); + _testAdapter.AddTestEntity("laser1", _laserDesignator); + + // 创建并注册模拟的目标 + _target = new MockTarget("target1", _simulationManager); + _testAdapter.AddTestEntity("target1", _target); + + // 创建导弹并注册到仿真管理器 + _missile = new LaserSemiActiveGuidedMissile(_properties, _simulationManager); + _simulationManager.RegisterEntity("missile1", _missile); + } + + // 模拟的激光指示器类 + private class MockLaserDesignator : LaserDesignator + { + public MockLaserDesignator(string id, ISimulationManager simulationManager) + : base(id, "target1", "missile1", 0, new LaserDesignatorConfig + { + Id = id, + InitialPosition = new Vector3D(100, 0, 0), + LaserPower = 100, + LaserDivergenceAngle = 0.001 + }, simulationManager) + { + // 不需要额外设置属性,因为基类构造函数已经设置了 + } + } + + // 模拟的目标类 + private class MockTarget : Tank + { + public MockTarget(string id, ISimulationManager simulationManager) + : base(id, new Vector3D(1000, 0, 0), 10, simulationManager) + { + // 不需要额外设置属性,因为基类构造函数已经设置了 + } + } + + [Fact] + public void SetLaserCode_SetsCodeCorrectly() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + + // Act + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the LaserIllumination test + } + + [Fact] + public void AddLaserCodeParameter_AddsParameterCorrectly() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PPM, 5678); + + // Act + _missile.AddLaserCodeParameter("PulseWidth", 0.001); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the LaserIllumination test + } + + [Fact] + public void SetCodeMatchRequired_SetsRequirementCorrectly() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + + // Act + _missile.SetCodeMatchRequired(true); + + // Assert - We can't directly check the private field, but we can test the behavior + // This will be tested in the LaserIllumination test + } + + [Fact] + public void LaserIllumination_WithMatchingCode_EnablesGuidance() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + _missile.SetCodeMatchRequired(true); + _missile.Update(0.1); // Move past launch stage + + // Act - Send matching code illumination + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + } + }; + _simulationManager.PublishEvent(illuminationEvent); + _missile.Update(0.1); + + // Assert + var matchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(matchEvents); + + // Guidance should be enabled with matching code + Assert.True(_missile.IsGuidance); + } + + [Fact] + public void LaserIllumination_WithMismatchingCode_DisablesGuidance() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + _missile.SetCodeMatchRequired(true); + _missile.Update(0.1); // Move past launch stage + + // Act - Send mismatching code illumination + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 5678 // Different code + } + }; + _simulationManager.PublishEvent(illuminationEvent); + _missile.Update(0.1); + + // Assert + var mismatchEvents = _testAdapter.GetPublishedEvents(); + Assert.NotEmpty(mismatchEvents); + + // Guidance should be disabled with mismatching code + Assert.False(_missile.IsGuidance); + } + + [Fact] + public void LaserIllumination_WithCodeDisabled_EnablesGuidanceIfNotRequired() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + _missile.SetCodeMatchRequired(false); // Not required + _missile.Update(0.1); // Move past launch stage + + // Act - Send illumination with code disabled + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = false, + LaserCode = null + }; + _simulationManager.PublishEvent(illuminationEvent); + _missile.Update(0.1); + + // Assert + // Guidance should be enabled even without code when not required + Assert.True(_missile.IsGuidance); + } + + [Fact] + public void LaserIllumination_WithCodeDisabled_DisablesGuidanceIfRequired() + { + // Arrange + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + _missile.SetCodeMatchRequired(true); // Required + _missile.Update(0.1); // Move past launch stage + + // Act - Send illumination with code disabled + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = false, + LaserCode = null + }; + _simulationManager.PublishEvent(illuminationEvent); + _missile.Update(0.1); + + // Assert + // Guidance should be disabled without code when required + Assert.False(_missile.IsGuidance); + } + + [Fact] + public void LaserIlluminationStop_DisablesGuidance() + { + // Arrange - First enable guidance with matching code + _missile.Fire(); + _missile.Activate(); + _missile.SetLaserCode(LaserCodeType.PRF, 1234); + _missile.Update(0.1); // Move past launch stage + + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1", + IsCodeEnabled = true, + LaserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + } + }; + _simulationManager.PublishEvent(illuminationEvent); + _missile.Update(0.1); + + // Verify guidance is enabled + Assert.True(_missile.IsGuidance); + + // Act - Stop illumination + var stopEvent = new LaserIlluminationStopEvent + { + LaserDesignatorId = "laser1", + TargetId = "target1" + }; + _simulationManager.PublishEvent(stopEvent); + _missile.Update(0.1); + + // Assert + Assert.False(_missile.IsGuidance); + } + } +} \ No newline at end of file diff --git a/ThreatSource.Tests/src/Utils/LaserCodeTests.cs b/ThreatSource.Tests/src/Utils/LaserCodeTests.cs new file mode 100644 index 0000000..bce9d0c --- /dev/null +++ b/ThreatSource.Tests/src/Utils/LaserCodeTests.cs @@ -0,0 +1,155 @@ +using Xunit; +using ThreatSource.Utils; + +namespace ThreatSource.Tests.Utils +{ + public class LaserCodeTests + { + [Fact] + public void Constructor_InitializesPropertiesCorrectly() + { + // Arrange & Act + var laserCode = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + // Assert + Assert.Equal(LaserCodeType.PRF, laserCode.CodeType); + Assert.Equal(1234, laserCode.CodeValue); + Assert.Empty(laserCode.Parameters); + } + + [Fact] + public void Parameters_CanAddAndRetrieveValues() + { + // Arrange + var laserCode = new LaserCode + { + CodeType = LaserCodeType.PPM, + CodeValue = 5678 + }; + + // Act + laserCode.Parameters["PulseWidth"] = 0.001; + laserCode.Parameters["PulseCount"] = 10; + + // Assert + Assert.Equal(0.001, laserCode.Parameters["PulseWidth"]); + Assert.Equal(10, laserCode.Parameters["PulseCount"]); + Assert.Equal(2, laserCode.Parameters.Count); + } + + [Fact] + public void Matches_ReturnsTrueForMatchingCodes() + { + // Arrange + var code1 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + var code2 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + // Act + bool result = code1.Matches(code2); + + // Assert + Assert.True(result); + } + + [Fact] + public void Matches_ReturnsFalseForDifferentCodeTypes() + { + // Arrange + var code1 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + var code2 = new LaserCode + { + CodeType = LaserCodeType.PPM, + CodeValue = 1234 + }; + + // Act + bool result = code1.Matches(code2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Matches_ReturnsFalseForDifferentCodeValues() + { + // Arrange + var code1 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + var code2 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 5678 + }; + + // Act + bool result = code1.Matches(code2); + + // Assert + Assert.False(result); + } + + [Fact] + public void Matches_ReturnsFalseForNullCode() + { + // Arrange + var code = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + + // Act + bool result = code.Matches(null); + + // Assert + Assert.False(result); + } + + [Fact] + public void Matches_IgnoresParameters() + { + // Arrange + var code1 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + code1.Parameters["PulseWidth"] = 0.001; + + var code2 = new LaserCode + { + CodeType = LaserCodeType.PRF, + CodeValue = 1234 + }; + code2.Parameters["PulseWidth"] = 0.002; // Different parameter value + + // Act + bool result = code1.Matches(code2); + + // Assert + Assert.True(result); // Should still match because parameters are ignored + } + } +} \ No newline at end of file diff --git a/ThreatSource/src/Guidance/BasicGuidanceSystem.cs b/ThreatSource/src/Guidance/BasicGuidanceSystem.cs index 7ab9ee7..bf3db9b 100644 --- a/ThreatSource/src/Guidance/BasicGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/BasicGuidanceSystem.cs @@ -1,5 +1,6 @@ using System; using ThreatSource.Utils; +using ThreatSource.Simulation; namespace ThreatSource.Guidance { @@ -12,10 +13,20 @@ namespace ThreatSource.Guidance /// - 运动参数记录 /// - 加速度限制 /// - 比例导引系数 + /// - 事件发布 /// 是其他具体制导系统的基类 /// - public class BasicGuidanceSystem : IGuidanceSystem + public class BasicGuidanceSystem : SimulationElement, IGuidanceSystem { + /// + /// 获取或设置父实体ID + /// + /// + /// 记录所属导弹的ID + /// 用于事件发布和状态关联 + /// + public string? ParentId { get; set; } + /// /// 获取或设置是否有有效的制导信息 /// @@ -46,24 +57,6 @@ namespace ThreatSource.Guidance /// public double ProportionalNavigationCoefficient { get; set; } - /// - /// 获取或设置导弹位置,单位:米 - /// - /// - /// 记录导弹的当前位置 - /// 用于制导计算和状态更新 - /// - protected Vector3D Position { get; set; } - - /// - /// 获取或设置导弹速度,单位:米/秒 - /// - /// - /// 记录导弹的当前速度 - /// 用于制导计算和状态更新 - /// - protected Vector3D Velocity { get; set; } - /// /// 获取或设置制导加速度,单位:米/平方秒 /// @@ -76,20 +69,22 @@ namespace ThreatSource.Guidance /// /// 初始化基础制导系统的新实例 /// + /// 制导系统ID /// 最大加速度,单位:米/平方秒 /// 比例导引系数 + /// 仿真管理器实例 /// /// 构造过程: + /// - 初始化基类参数 /// - 初始化制导状态 /// - 设置运动参数 /// - 配置制导参数 /// - public BasicGuidanceSystem(double maxAcceleration, double proportionalNavigationCoefficient) + public BasicGuidanceSystem(string id, double maxAcceleration, double proportionalNavigationCoefficient, ISimulationManager simulationManager) + : base(id, Vector3D.Zero, new Orientation(), 0, simulationManager) { HasGuidance = false; GuidanceAcceleration = Vector3D.Zero; - Position = Vector3D.Zero; - Velocity = Vector3D.Zero; MaxAcceleration = maxAcceleration; ProportionalNavigationCoefficient = proportionalNavigationCoefficient; } @@ -112,6 +107,21 @@ namespace ThreatSource.Guidance Velocity = missileVelocity; } + /// + /// 更新制导系统状态 + /// + /// 时间步长,单位:秒 + /// + /// 实现 SimulationElement 的抽象方法 + /// 在此方法中不执行实际操作,制导系统通过 Update(deltaTime, position, velocity) 更新 + /// + public override void Update(double deltaTime) + { + // 警告:制导系统不应直接由仿真系统调用 + Console.WriteLine("警告:制导系统的Update(deltaTime)方法被直接调用,这可能是一个错误。"); + // 制导系统应通过Update(deltaTime, position, velocity)方法更新 + } + /// /// 获取制导加速度指令 /// @@ -157,9 +167,9 @@ namespace ThreatSource.Guidance /// - 加速度信息 /// 用于状态监控和调试 /// - public virtual string GetStatus() + public override string GetStatus() { - return $" 导引头状态: 有制导={HasGuidance}, 位置={Position}, 速度={Velocity}, 制导加速度={GuidanceAcceleration}"; + return base.GetStatus() + $"\n 导引头状态: 有制导={HasGuidance}, 制导加速度={GuidanceAcceleration}"; } } } diff --git a/ThreatSource/src/Guidance/InfraredCommandGuidanceSystem.cs b/ThreatSource/src/Guidance/InfraredCommandGuidanceSystem.cs index f4f0573..d372a9f 100644 --- a/ThreatSource/src/Guidance/InfraredCommandGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/InfraredCommandGuidanceSystem.cs @@ -1,4 +1,5 @@ using ThreatSource.Utils; +using ThreatSource.Simulation; namespace ThreatSource.Guidance { @@ -74,16 +75,18 @@ namespace ThreatSource.Guidance /// /// 初始化红外指令导引系统的新实例 /// + /// 系统标识 /// 最大加速度,单位:米/平方秒 /// 制导系数 + /// 模拟管理器 /// /// 构造过程: /// - 初始化基类参数 /// - 初始化向量记录 /// - 清零转向速率 /// - public InfraredCommandGuidanceSystem(double maxAcceleration, double guidanceCoefficient) - : base(maxAcceleration, guidanceCoefficient) + public InfraredCommandGuidanceSystem(string id, double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager) + : base(id, maxAcceleration, guidanceCoefficient, simulationManager) { lastTrackerToMissileVector = Vector3D.Zero; lastTrackerToTargetVector = Vector3D.Zero; diff --git a/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs b/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs index 159e0ec..41e58de 100644 --- a/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs @@ -76,15 +76,6 @@ namespace ThreatSource.Guidance /// private readonly Random random = new(); - /// - /// 获取或设置仿真管理器实例 - /// - /// - /// 用于获取场景中的目标信息 - /// 实现目标探测功能 - /// - public ISimulationManager SimulationManager { get; set; } - /// /// 上一次探测到的目标位置 /// @@ -95,8 +86,9 @@ namespace ThreatSource.Guidance private Vector3D lastTargetPosition; /// - /// 初始化红外成像引导系统的新实例 + /// 初始化红外成像制导系统的新实例 /// + /// 系统标识 /// 最大加速度,单位:米/平方秒 /// 比例导引系数 /// 仿真管理器实例 @@ -106,9 +98,9 @@ namespace ThreatSource.Guidance /// - 设置仿真管理器 /// - 初始化目标位置 /// - public InfraredImagingGuidanceSystem(double maxAcceleration, double proportionalNavigationCoefficient, ISimulationManager simulationManager) : base(maxAcceleration, proportionalNavigationCoefficient) + public InfraredImagingGuidanceSystem(string id, double maxAcceleration, double proportionalNavigationCoefficient, ISimulationManager simulationManager) + : base(id, maxAcceleration, proportionalNavigationCoefficient, simulationManager) { - SimulationManager = simulationManager; lastTargetPosition = Vector3D.Zero; } diff --git a/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs b/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs index bbee1e9..2e75628 100644 --- a/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs @@ -1,4 +1,5 @@ using ThreatSource.Utils; +using ThreatSource.Simulation; namespace ThreatSource.Guidance { @@ -111,15 +112,18 @@ namespace ThreatSource.Guidance /// /// 初始化激光驾束制导系统的新实例 /// + /// 制导系统ID /// 最大加速度,单位:米/平方秒 /// 制导系数 + /// 仿真管理器实例 /// /// 构造过程: /// - 初始化基类参数 /// - 初始化激光参数 /// - 初始化控制参数 /// - public LaserBeamRiderGuidanceSystem(double maxAcceleration, double guidanceCoefficient) : base(maxAcceleration, guidanceCoefficient) + public LaserBeamRiderGuidanceSystem(string id, double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager) + : base(id, maxAcceleration, guidanceCoefficient, simulationManager) { LaserSourcePosition = Vector3D.Zero; LaserDirection = Vector3D.Zero; diff --git a/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs b/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs index 6e0b53b..4744d3b 100644 --- a/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs @@ -1,4 +1,5 @@ using ThreatSource.Utils; +using ThreatSource.Simulation; namespace ThreatSource.Guidance { @@ -129,18 +130,54 @@ namespace ThreatSource.Guidance /// private double LaserDivergenceAngle { get; set; } + /// + /// 获取或设置期望的激光编码 + /// + /// + /// 导弹期望接收的编码信息 + /// 用于验证接收到的激光信号 + /// + private LaserCode? expectedLaserCode = null; + + /// + /// 获取或设置是否要求编码匹配 + /// + /// + /// true表示只响应匹配编码的激光信号 + /// false表示响应所有激光信号 + /// 默认为false + /// + private bool isCodeMatchRequired = false; + + /// + /// 获取或设置支持的编码类型列表 + /// + /// + /// 定义导弹支持的编码类型 + /// 默认支持PRF、PPM和PWM编码 + /// + private List supportedCodeTypes = new List + { + LaserCodeType.PRF, + LaserCodeType.PPM, + LaserCodeType.PWM + }; + /// /// 初始化激光半主动制导系统的新实例 /// + /// 制导系统ID /// 最大加速度,单位:米/平方秒 /// 制导系数 + /// 仿真管理器实例 /// /// 构造过程: /// - 初始化基类参数 /// - 初始化目标信息 /// - 初始化激光参数 /// - public LaserSemiActiveGuidanceSystem(double maxAcceleration, double guidanceCoefficient) : base(maxAcceleration, guidanceCoefficient) + public LaserSemiActiveGuidanceSystem(string id, double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager) + : base(id, maxAcceleration, guidanceCoefficient, simulationManager) { TargetPosition = Vector3D.Zero; TargetVelocity = Vector3D.Zero; @@ -212,7 +249,12 @@ namespace ThreatSource.Guidance base.Update(deltaTime, missilePosition, missileVelocity); if (LaserIlluminationOn) { - HasGuidance = CalculateReceivedLaserPower() > LockThreshold; + // 只有当HasGuidance为false时才根据接收功率计算,避免覆盖ProcessLaserIlluminationEvent中设置的值 + if (!HasGuidance) + { + HasGuidance = CalculateReceivedLaserPower() > LockThreshold; + } + if (HasGuidance) { CalculateGuidanceAcceleration(deltaTime); @@ -224,6 +266,7 @@ namespace ThreatSource.Guidance } else { + HasGuidance = false; // 如果没有激光照射,则禁用制导 GuidanceAcceleration = Vector3D.Zero; } } @@ -314,5 +357,257 @@ namespace ThreatSource.Guidance { return base.GetStatus() + $" 接收到的激光功率: {CalculateReceivedLaserPower():E} W 锁定阈值: {LockThreshold:E} W"; } + + /// + /// 设置期望的激光编码 + /// + /// 编码类型 + /// 编码值 + /// + /// 设置过程: + /// - 检查编码类型是否支持 + /// - 创建新的编码对象 + /// - 设置编码类型和值 + /// + public void SetExpectedLaserCode(LaserCodeType codeType, int codeValue) + { + if (supportedCodeTypes.Contains(codeType)) + { + expectedLaserCode = new LaserCode + { + CodeType = codeType, + CodeValue = codeValue + }; + Console.WriteLine($"激光半主动制导系统设置期望编码:类型={codeType},值={codeValue}"); + } + else + { + Console.WriteLine($"激光半主动制导系统不支持编码类型:{codeType}"); + } + } + + /// + /// 添加期望编码参数 + /// + /// 参数名称 + /// 参数值 + /// + /// 添加特定编码类型的额外参数 + /// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等 + /// + public void AddExpectedCodeParameter(string key, object value) + { + if (expectedLaserCode != null) + { + expectedLaserCode.Parameters[key] = value; + Console.WriteLine($"激光半主动制导系统添加期望编码参数:{key}={value}"); + } + } + + /// + /// 设置是否要求编码匹配 + /// + /// 是否要求匹配 + /// + /// true表示只响应匹配编码的激光信号 + /// false表示响应所有激光信号 + /// + public void SetCodeMatchRequired(bool required) + { + isCodeMatchRequired = required; + Console.WriteLine($"激光半主动制导系统{(required ? "要求" : "不要求")}编码匹配"); + } + + /// + /// 处理激光照射事件 + /// + /// 激光照射事件 + /// + /// 处理过程: + /// - 检查编码是否匹配 + /// - 如果要求匹配且不匹配,则忽略信号 + /// - 如果匹配或不要求匹配,则处理信号 + /// - 更新激光照射状态 + /// + public void ProcessLaserIlluminationEvent(LaserIlluminationStartEvent illuminationEvent) + { + // 检查编码是否匹配 + bool codeMatched = CheckCodeMatch(illuminationEvent.LaserCode, illuminationEvent.IsCodeEnabled); + + if (isCodeMatchRequired && !codeMatched) + { + // 发布编码不匹配事件 + PublishCodeMismatchEvent(illuminationEvent.LaserDesignatorId, illuminationEvent.LaserCode); + Console.WriteLine("激光半主动制导系统接收到不匹配的激光编码,忽略信号"); + HasGuidance = false; // 禁用制导 + return; // 不处理不匹配的信号 + } + + // 如果编码匹配或不要求匹配,继续处理 + if (codeMatched) + { + // 发布编码匹配事件 + PublishCodeMatchEvent(illuminationEvent.LaserDesignatorId, illuminationEvent.LaserCode); + Console.WriteLine("激光半主动制导系统接收到匹配的激光编码,处理信号"); + HasGuidance = true; // 启用制导 + } + else if (!isCodeMatchRequired) + { + // 不要求编码匹配,但也没有编码 + HasGuidance = true; // 启用制导 + } + + // 更新激光照射状态 + LaserIlluminationOn = true; + } + + /// + /// 处理激光照射更新事件 + /// + /// 激光照射更新事件 + /// + /// 处理过程: + /// - 检查编码是否匹配 + /// - 如果要求匹配且不匹配,则忽略信号 + /// - 如果匹配或不要求匹配,则处理信号 + /// - 更新激光照射状态 + /// + public void ProcessLaserIlluminationUpdateEvent(LaserIlluminationUpdateEvent illuminationEvent) + { + // 检查编码是否匹配 + bool codeMatched = CheckCodeMatch(illuminationEvent.LaserCode, illuminationEvent.IsCodeEnabled); + + if (isCodeMatchRequired && !codeMatched) + { + // 发布编码不匹配事件 + PublishCodeMismatchEvent(illuminationEvent.LaserDesignatorId, illuminationEvent.LaserCode); + Console.WriteLine("激光半主动制导系统接收到不匹配的激光编码,忽略信号"); + HasGuidance = false; // 禁用制导 + return; // 不处理不匹配的信号 + } + + // 如果编码匹配或不要求匹配,继续处理 + if (codeMatched && !LaserIlluminationOn) + { + // 发布编码匹配事件 + PublishCodeMatchEvent(illuminationEvent.LaserDesignatorId, illuminationEvent.LaserCode); + Console.WriteLine("激光半主动制导系统接收到匹配的激光编码,处理信号"); + HasGuidance = true; // 启用制导 + } + else if (!isCodeMatchRequired) + { + // 不要求编码匹配,但也没有编码 + HasGuidance = true; // 启用制导 + } + + // 更新激光照射状态 + LaserIlluminationOn = true; + } + + /// + /// 处理激光照射停止事件 + /// + /// 激光照射停止事件 + /// + /// 处理过程: + /// - 停止激光照射状态 + /// - 清理相关参数 + /// + public void ProcessLaserIlluminationStopEvent(LaserIlluminationStopEvent illuminationEvent) + { + LaserIlluminationOn = false; + HasGuidance = false; // 禁用制导 + } + + /// + /// 检查编码是否匹配 + /// + /// 接收到的编码 + /// 是否启用编码 + /// 如果匹配返回true,否则返回false + /// + /// 匹配规则: + /// - 如果不要求匹配,返回true + /// - 如果接收到的信号未启用编码,根据配置决定是否匹配 + /// - 如果导弹未设置期望编码,根据配置决定是否匹配 + /// - 检查编码类型是否支持 + /// - 检查编码是否匹配 + /// + private bool CheckCodeMatch(LaserCode? receivedCode, bool isCodeEnabled) + { + // 如果不要求匹配,直接返回true + if (!isCodeMatchRequired) + { + return true; + } + + // 如果接收到的信号未启用编码 + if (!isCodeEnabled || receivedCode == null) + { + return !isCodeMatchRequired; + } + + // 如果导弹未设置期望编码 + if (expectedLaserCode == null) + { + return !isCodeMatchRequired; + } + + // 检查编码类型是否支持 + if (!supportedCodeTypes.Contains(receivedCode.CodeType)) + { + return false; + } + + // 检查编码是否匹配 + return expectedLaserCode.Matches(receivedCode); + } + + /// + /// 发布编码匹配事件 + /// + /// 激光定位器ID + /// 匹配的编码 + /// + /// 发布过程: + /// - 创建事件对象 + /// - 设置事件属性 + /// - 发布到事件系统 + /// + private void PublishCodeMatchEvent(string? designatorId, LaserCode? matchedCode) + { + var matchEvent = new LaserCodeMatchEvent + { + MissileId = ParentId, + DesignatorId = designatorId, + MatchedCode = matchedCode + }; + + PublishEvent(matchEvent); + } + + /// + /// 发布编码不匹配事件 + /// + /// 激光定位器ID + /// 接收到的编码 + /// + /// 发布过程: + /// - 创建事件对象 + /// - 设置事件属性 + /// - 发布到事件系统 + /// + private void PublishCodeMismatchEvent(string? designatorId, LaserCode? receivedCode) + { + var mismatchEvent = new LaserCodeMismatchEvent + { + MissileId = ParentId, + DesignatorId = designatorId, + ExpectedCode = expectedLaserCode, + ReceivedCode = receivedCode + }; + + PublishEvent(mismatchEvent); + } } } diff --git a/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs b/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs index b3afa38..75358e0 100644 --- a/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs @@ -113,17 +113,9 @@ namespace ThreatSource.Guidance private double jammingPower = 0; /// - /// 获取或设置仿真管理器实例 - /// - /// - /// 用于获取场景中的目标信息 - /// 实现目标探测功能 - /// - public ISimulationManager SimulationManager { get; set; } - - /// - /// 初始化毫米波导引头系统的新实例 + /// 初始化毫米波制导系统的新实例 /// + /// 制导系统ID /// 最大加速度,单位:米/平方秒 /// 制导系数 /// 仿真管理器实例 @@ -134,9 +126,9 @@ namespace ThreatSource.Guidance /// - 初始化目标位置 /// - 注册干扰事件处理 /// - public MillimeterWaveGuidanceSystem(double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager) : base(maxAcceleration, guidanceCoefficient) + public MillimeterWaveGuidanceSystem(string id, double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager) + : base(id, maxAcceleration, guidanceCoefficient, simulationManager) { - SimulationManager = simulationManager; lastTargetPosition = Vector3D.Zero; // 订阅干扰事件 diff --git a/ThreatSource/src/Indicator/LaserDesignator.cs b/ThreatSource/src/Indicator/LaserDesignator.cs index dae0eac..6bea554 100644 --- a/ThreatSource/src/Indicator/LaserDesignator.cs +++ b/ThreatSource/src/Indicator/LaserDesignator.cs @@ -83,6 +83,25 @@ namespace ThreatSource.Indicator /// public double LaserDivergenceAngle { get; private set; } = 0; + /// + /// 获取或设置激光编码 + /// + /// + /// 包含激光信号的编码信息 + /// 用于抗干扰和安全识别 + /// + private LaserCode? currentLaserCode = null; + + /// + /// 获取或设置是否启用编码 + /// + /// + /// true表示激光信号使用编码 + /// false表示激光信号不使用编码 + /// 默认为false + /// + private bool isCodeEnabled = false; + /// /// 初始化激光指示器的新实例 /// @@ -261,38 +280,122 @@ namespace ThreatSource.Indicator base.Deactivate(); } + /// + /// 设置激光编码 + /// + /// 编码类型 + /// 编码值 + /// + /// 设置过程: + /// - 创建新的编码对象 + /// - 设置编码类型和值 + /// - 启用编码功能 + /// + public void SetLaserCode(LaserCodeType codeType, int codeValue) + { + currentLaserCode = new LaserCode + { + CodeType = codeType, + CodeValue = codeValue + }; + isCodeEnabled = true; + Console.WriteLine($"激光目标指示器 {Id} 设置编码:类型={codeType},值={codeValue}"); + } + + /// + /// 添加编码参数 + /// + /// 参数名称 + /// 参数值 + /// + /// 添加特定编码类型的额外参数 + /// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等 + /// + public void AddCodeParameter(string key, object value) + { + if (currentLaserCode != null) + { + currentLaserCode.Parameters[key] = value; + Console.WriteLine($"激光目标指示器 {Id} 添加编码参数:{key}={value}"); + } + } + + /// + /// 启用或禁用编码 + /// + /// 是否启用编码 + /// + /// true表示启用编码功能 + /// false表示禁用编码功能 + /// + public void EnableCode(bool enable) + { + isCodeEnabled = enable; + Console.WriteLine($"激光目标指示器 {Id} {(enable ? "启用" : "禁用")}编码功能"); + } + /// /// 发布激光照射开始事件 /// /// /// 发布过程: /// - 创建事件对象 - /// - 设置事件参数 + /// - 设置事件属性 + /// - 添加编码信息 /// - 发布到事件系统 - /// - 记录事件日志 /// private void PublishIlluminationStartEvent() { - var evt = new LaserIlluminationStartEvent { LaserDesignatorId = Id, TargetId = TargetId }; - PublishEvent(evt); - Console.WriteLine($"激光指示器 {Id} 发布激光照射事件"); + var illuminationEvent = new LaserIlluminationStartEvent + { + LaserDesignatorId = Id, + TargetId = TargetId + }; + + // 添加编码信息 + if (isCodeEnabled && currentLaserCode != null) + { + illuminationEvent.LaserCode = currentLaserCode; + illuminationEvent.IsCodeEnabled = true; + } + else + { + illuminationEvent.IsCodeEnabled = false; + } + + PublishEvent(illuminationEvent); } - /// + /// /// 发布激光照射更新事件 /// /// /// 发布过程: - /// - 创建更新事件 - /// - 设置最新参数 + /// - 创建事件对象 + /// - 设置事件属性 + /// - 添加编码信息 /// - 发布到事件系统 - /// - 记录更新日志 /// private void PublishIlluminationUpdateEvent() { - var evt = new LaserIlluminationUpdateEvent { LaserDesignatorId = Id, TargetId = TargetId }; - PublishEvent(evt); - Console.WriteLine($"激光指示器 {Id} 发布激光照射更新事件"); + var illuminationEvent = new LaserIlluminationUpdateEvent + { + LaserDesignatorId = Id, + TargetId = TargetId + }; + + // 添加编码信息 + if (isCodeEnabled && currentLaserCode != null) + { + illuminationEvent.LaserCode = currentLaserCode; + illuminationEvent.IsCodeEnabled = true; + } + else + { + illuminationEvent.IsCodeEnabled = false; + } + + PublishEvent(illuminationEvent); } /// diff --git a/ThreatSource/src/MIssile/InfraredCommandGuidedMissile.cs b/ThreatSource/src/MIssile/InfraredCommandGuidedMissile.cs index 080c7ff..b61d43e 100644 --- a/ThreatSource/src/MIssile/InfraredCommandGuidedMissile.cs +++ b/ThreatSource/src/MIssile/InfraredCommandGuidedMissile.cs @@ -92,8 +92,10 @@ namespace ThreatSource.Missile // 初始化红外指令导引系统,包括自适应 PID 参数 guidanceSystem = new InfraredCommandGuidanceSystem( - maxAcceleration: config.MaxAcceleration, - guidanceCoefficient: config.ProportionalNavigationCoefficient + config.Id + "_guidance", + config.MaxAcceleration, + config.ProportionalNavigationCoefficient, + manager ); } diff --git a/ThreatSource/src/MIssile/InfraredImagingTerminalGuidedMissile.cs b/ThreatSource/src/MIssile/InfraredImagingTerminalGuidedMissile.cs index b7e1a94..c0aa5be 100644 --- a/ThreatSource/src/MIssile/InfraredImagingTerminalGuidedMissile.cs +++ b/ThreatSource/src/MIssile/InfraredImagingTerminalGuidedMissile.cs @@ -86,7 +86,11 @@ namespace ThreatSource.Missile : base(config, manager) { currentStage = IRTG_Stage.Launch; - guidanceSystem = new InfraredImagingGuidanceSystem(config.MaxAcceleration, config.ProportionalNavigationCoefficient, manager); + guidanceSystem = new InfraredImagingGuidanceSystem( + config.Id + "_guidance", + config.MaxAcceleration, + config.ProportionalNavigationCoefficient, + manager); } /// diff --git a/ThreatSource/src/MIssile/LaserBeamRiderMissile.cs b/ThreatSource/src/MIssile/LaserBeamRiderMissile.cs index cec7def..aa93f1f 100644 --- a/ThreatSource/src/MIssile/LaserBeamRiderMissile.cs +++ b/ThreatSource/src/MIssile/LaserBeamRiderMissile.cs @@ -69,7 +69,11 @@ namespace ThreatSource.Missile public LaserBeamRiderMissile(MissileProperties config, ISimulationManager manager) : base(config, manager) { - LaserBeamRiderGuidanceSystem = new LaserBeamRiderGuidanceSystem(config.MaxAcceleration, config.ProportionalNavigationCoefficient); + LaserBeamRiderGuidanceSystem = new LaserBeamRiderGuidanceSystem( + config.Id + "_guidance", + config.MaxAcceleration, + config.ProportionalNavigationCoefficient, + manager); currentStage = LBRM_Stage.Launch; } diff --git a/ThreatSource/src/MIssile/LaserSemiActiveGuidedMissile.cs b/ThreatSource/src/MIssile/LaserSemiActiveGuidedMissile.cs index c476010..d0c6217 100644 --- a/ThreatSource/src/MIssile/LaserSemiActiveGuidedMissile.cs +++ b/ThreatSource/src/MIssile/LaserSemiActiveGuidedMissile.cs @@ -80,8 +80,21 @@ namespace ThreatSource.Missile public LaserSemiActiveGuidedMissile(MissileProperties config, ISimulationManager manager) : base(config, manager) { - LaserGuidanceSystem = new LaserSemiActiveGuidanceSystem(config.MaxAcceleration, config.ProportionalNavigationCoefficient); + LaserGuidanceSystem = new LaserSemiActiveGuidanceSystem( + config.Id + "_guidance", + config.MaxAcceleration, + config.ProportionalNavigationCoefficient, + manager); + + // 设置制导系统的ParentId属性 + LaserGuidanceSystem.ParentId = config.Id; + currentStage = LSAGM_Stage.Launch; + + // 订阅激光照射事件 + SimulationManager.SubscribeToEvent(OnLaserIlluminationStart); + SimulationManager.SubscribeToEvent(OnLaserIlluminationUpdate); + SimulationManager.SubscribeToEvent(OnLaserIlluminationStop); } /// @@ -91,8 +104,8 @@ namespace ThreatSource.Missile /// /// 处理过程: /// - 验证激光指示器和目标 - /// - 更新制导系统参数 - /// - 开始目标跟踪 + /// - 更新目标位置和速度 + /// - 更新制导参数 /// private void OnLaserIlluminationStart(LaserIlluminationStartEvent evt) { @@ -100,8 +113,19 @@ namespace ThreatSource.Missile { LaserDesignator laserDesignator = SimulationManager.GetEntityById(evt.LaserDesignatorId) as LaserDesignator ?? throw new Exception("激光指示器不存在"); SimulationElement target = SimulationManager.GetEntityById(evt.TargetId) as SimulationElement ?? throw new Exception("目标不存在"); + + // 更新激光指示器信息 LaserGuidanceSystem.UpdateLaserDesignator(laserDesignator.Position, target.Position, target.Velocity, laserDesignator.LaserPower, laserDesignator.LaserDivergenceAngle); + + // 处理激光编码 + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.ProcessLaserIlluminationEvent(evt); + + // 直接设置IsGuidance属性 + IsGuidance = laserGuidance.HasGuidance; + } } } @@ -121,8 +145,19 @@ namespace ThreatSource.Missile { LaserDesignator laserDesignator = SimulationManager.GetEntityById(evt.LaserDesignatorId) as LaserDesignator ?? throw new Exception("激光指示器不存在"); SimulationElement target = SimulationManager.GetEntityById(evt.TargetId) as SimulationElement ?? throw new Exception("目标不存在"); + + // 更新激光指示器信息 LaserGuidanceSystem.UpdateLaserDesignator(laserDesignator.Position, target.Position, target.Velocity, laserDesignator.LaserPower, laserDesignator.LaserDivergenceAngle); + + // 处理激光编码 + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.ProcessLaserIlluminationUpdateEvent(evt); + + // 直接设置IsGuidance属性 + IsGuidance = laserGuidance.HasGuidance; + } } } @@ -138,7 +173,17 @@ namespace ThreatSource.Missile /// private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt) { + // 停用激光指示器 LaserGuidanceSystem.DeactivateLaserDesignator(); + + // 处理激光编码 + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.ProcessLaserIlluminationStopEvent(evt); + + // 直接设置IsGuidance属性 + IsGuidance = laserGuidance.HasGuidance; + } } /// @@ -238,9 +283,23 @@ namespace ThreatSource.Missile /// private void UpdateCruiseStage(double deltaTime) { - IsGuidance = true; + // 更新制导系统 LaserGuidanceSystem.Update(deltaTime, Position, Velocity); + + // 根据制导系统的HasGuidance属性设置IsGuidance + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + IsGuidance = laserGuidance.HasGuidance; + } + else + { + IsGuidance = true; // 默认情况下启用制导 + } + + // 获取制导加速度 GuidanceAcceleration = LaserGuidanceSystem.GetGuidanceAcceleration(); + + // 检查自毁条件 if(ShouldSelfDestruct()) { currentStage = LSAGM_Stage.SelfDestruct; @@ -291,5 +350,55 @@ namespace ThreatSource.Missile string additionalStatus = LaserGuidanceSystem.HasGuidance ? LaserGuidanceSystem.GetStatus() : ""; return baseStatus + additionalStatus; } + + /// + /// 设置激光编码 + /// + /// 编码类型 + /// 编码值 + /// + /// 设置导弹期望接收的激光编码类型和值 + /// 用于验证接收到的激光信号 + /// + public void SetLaserCode(LaserCodeType codeType, int codeValue) + { + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.SetExpectedLaserCode(codeType, codeValue); + } + } + + /// + /// 添加激光编码参数 + /// + /// 参数名称 + /// 参数值 + /// + /// 添加特定编码类型的额外参数 + /// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等 + /// + public void AddLaserCodeParameter(string key, object value) + { + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.AddExpectedCodeParameter(key, value); + } + } + + /// + /// 设置是否要求编码匹配 + /// + /// 是否要求匹配 + /// + /// true表示只响应匹配编码的激光信号 + /// false表示响应所有激光信号 + /// + public void SetCodeMatchRequired(bool required) + { + if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance) + { + laserGuidance.SetCodeMatchRequired(required); + } + } } } diff --git a/ThreatSource/src/MIssile/MillimeterWaveTerminalGuidedMissile.cs b/ThreatSource/src/MIssile/MillimeterWaveTerminalGuidedMissile.cs index fa01056..8b8d2f2 100644 --- a/ThreatSource/src/MIssile/MillimeterWaveTerminalGuidedMissile.cs +++ b/ThreatSource/src/MIssile/MillimeterWaveTerminalGuidedMissile.cs @@ -86,7 +86,12 @@ namespace ThreatSource.Missile : base(properties, manager) { currentStage = MWTG_Stage.Launch; - guidanceSystem = new MillimeterWaveGuidanceSystem(properties.MaxAcceleration, properties.ProportionalNavigationCoefficient, manager); + guidanceSystem = new MillimeterWaveGuidanceSystem( + properties.Id + "_guidance", + properties.MaxAcceleration, + properties.ProportionalNavigationCoefficient, + manager + ); } /// diff --git a/ThreatSource/src/MIssile/TerminalSensitiveSubmunition.cs b/ThreatSource/src/MIssile/TerminalSensitiveSubmunition.cs index bd7e496..76a7c0d 100644 --- a/ThreatSource/src/MIssile/TerminalSensitiveSubmunition.cs +++ b/ThreatSource/src/MIssile/TerminalSensitiveSubmunition.cs @@ -587,10 +587,16 @@ namespace ThreatSource.Missile { targetAngle = radiometerAngle.Value; } - else + else if (infraredAngle.HasValue) { targetAngle = infraredAngle.Value; } + else + { + // 如果两个角度都为null,设置一个默认值或抛出异常 + targetAngle = 0; + Console.WriteLine("警告:红外和毫米波探测器都未返回有效角度"); + } // 记录首次探测信息 isTargetDetected = true; @@ -602,9 +608,21 @@ namespace ThreatSource.Missile } else // 已经探测到目标,等待转过一圈进行二次确认 { + // 确保lastDetectionTime不为null + if (!lastDetectionTime.HasValue) + { + lastDetectionTime = FlightTime; + } + double timeSinceLastDetection = FlightTime - lastDetectionTime.Value; if (timeSinceLastDetection >= 0.75 * 2 * Math.PI / SpiralRotationSpeed) // 转过3/4圈以后 { + // 确保firstDetectionAngle不为null + if (!firstDetectionAngle.HasValue) + { + firstDetectionAngle = spiralAngle; + } + // 检查当前角度是否接近记录的目标方位角 double angleDiff = Math.Abs(spiralAngle - firstDetectionAngle.Value); // 根据仿真步长和转速计算允许的角度误差 diff --git a/ThreatSource/src/Simulation/SimulationEvents.cs b/ThreatSource/src/Simulation/SimulationEvents.cs index 19af178..de9aaa7 100644 --- a/ThreatSource/src/Simulation/SimulationEvents.cs +++ b/ThreatSource/src/Simulation/SimulationEvents.cs @@ -75,6 +75,24 @@ namespace ThreatSource.Simulation /// 标识被激光照射的目标实体 /// public string? TargetId { get; set; } + + /// + /// 获取或设置激光编码信息 + /// + /// + /// 包含激光信号的编码类型和编码值 + /// 用于抗干扰和安全识别 + /// + public LaserCode? LaserCode { get; set; } + + /// + /// 获取或设置是否启用编码 + /// + /// + /// true表示激光信号使用编码 + /// false表示激光信号不使用编码 + /// + public bool IsCodeEnabled { get; set; } } /// @@ -98,9 +116,27 @@ namespace ThreatSource.Simulation /// 获取或设置被照射目标的ID /// /// - /// 标识正在被照射的目标实体 + /// 标识被激光照射的目标实体 /// public string? TargetId { get; set; } + + /// + /// 获取或设置激光编码信息 + /// + /// + /// 包含激光信号的编码类型和编码值 + /// 用于抗干扰和安全识别 + /// + public LaserCode? LaserCode { get; set; } + + /// + /// 获取或设置是否启用编码 + /// + /// + /// true表示激光信号使用编码 + /// false表示激光信号不使用编码 + /// + public bool IsCodeEnabled { get; set; } } /// @@ -554,4 +590,80 @@ namespace ThreatSource.Simulation /// public double WavelengthMax { get; set; } } + + /// + /// 激光编码匹配事件,表示导弹成功匹配激光编码 + /// + /// + /// 用于通知系统导弹已成功识别激光编码 + /// 触发时机:导弹接收到匹配的激光编码时 + /// + public class LaserCodeMatchEvent : SimulationEvent + { + /// + /// 获取或设置导弹ID + /// + /// + /// 标识接收激光信号的导弹 + /// + public string? MissileId { get; set; } + + /// + /// 获取或设置激光定位器ID + /// + /// + /// 标识发送激光信号的定位器 + /// + public string? DesignatorId { get; set; } + + /// + /// 获取或设置匹配的激光编码 + /// + /// + /// 包含成功匹配的编码信息 + /// + public LaserCode? MatchedCode { get; set; } + } + + /// + /// 激光编码不匹配事件,表示导弹接收到不匹配的激光编码 + /// + /// + /// 用于通知系统导弹接收到不匹配的激光编码 + /// 触发时机:导弹接收到不匹配的激光编码时 + /// + public class LaserCodeMismatchEvent : SimulationEvent + { + /// + /// 获取或设置导弹ID + /// + /// + /// 标识接收激光信号的导弹 + /// + public string? MissileId { get; set; } + + /// + /// 获取或设置激光定位器ID + /// + /// + /// 标识发送激光信号的定位器 + /// + public string? DesignatorId { get; set; } + + /// + /// 获取或设置期望的激光编码 + /// + /// + /// 导弹期望接收的编码信息 + /// + public LaserCode? ExpectedCode { get; set; } + + /// + /// 获取或设置接收到的激光编码 + /// + /// + /// 导弹实际接收到的编码信息 + /// + public LaserCode? ReceivedCode { get; set; } + } } diff --git a/ThreatSource/src/Simulation/Testing/TestSimulationAdapter.cs b/ThreatSource/src/Simulation/Testing/TestSimulationAdapter.cs index c72ff66..b341e6b 100644 --- a/ThreatSource/src/Simulation/Testing/TestSimulationAdapter.cs +++ b/ThreatSource/src/Simulation/Testing/TestSimulationAdapter.cs @@ -90,6 +90,16 @@ namespace ThreatSource.Simulation.Testing return _publishedEvents; } + /// + /// 获取指定类型的已发布事件 + /// + /// 事件类型 + /// 指定类型的已发布事件列表 + public List GetPublishedEvents() where T : SimulationEvent + { + return _publishedEvents.OfType().ToList(); + } + /// /// 获取已接收的事件 /// diff --git a/ThreatSource/src/Utils/LaserCode.cs b/ThreatSource/src/Utils/LaserCode.cs new file mode 100644 index 0000000..e4ec05b --- /dev/null +++ b/ThreatSource/src/Utils/LaserCode.cs @@ -0,0 +1,139 @@ +using System.Collections.Generic; + +namespace ThreatSource.Utils +{ + /// + /// 激光编码类型枚举,定义支持的不同编码类型 + /// + /// + /// 包含多种常见的激光编码类型: + /// - PRF:脉冲重复频率编码 + /// - PPM:脉冲位置调制 + /// - PWM:脉冲宽度调制 + /// - PseudoRandom:伪随机编码 + /// - Frequency:频率编码 + /// - Phase:相位编码 + /// + public enum LaserCodeType + { + /// + /// 无编码 + /// + None = 0, + + /// + /// 脉冲重复频率编码 + /// + /// + /// 最常用的激光编码方式 + /// NATO标准使用的编码方式 + /// 通常使用4位数字编码(1111-1788) + /// + PRF = 1, + + /// + /// 脉冲位置调制 + /// + /// + /// 通过改变脉冲在时间窗口中的位置来编码信息 + /// 抗干扰能力较强 + /// + PPM = 2, + + /// + /// 脉冲宽度调制 + /// + /// + /// 通过改变脉冲宽度来编码信息 + /// 实现简单,抗干扰能力一般 + /// + PWM = 3, + + /// + /// 伪随机编码 + /// + /// + /// 使用伪随机序列对激光信号进行调制 + /// 抗干扰能力极强 + /// + PseudoRandom = 4, + + /// + /// 频率编码 + /// + /// + /// 使用不同频率的激光进行编码 + /// 可以同时使用多个频率 + /// + Frequency = 5, + + /// + /// 相位编码 + /// + /// + /// 通过调制激光信号的相位来编码信息 + /// 编码容量大,抗干扰能力强 + /// + Phase = 6 + } + + /// + /// 激光编码类,用于表示激光信号的编码信息 + /// + /// + /// 提供了激光编码的基本属性和方法: + /// - 编码类型 + /// - 编码值 + /// - 附加参数 + /// - 编码匹配判断 + /// 用于实现激光信号的编码和解码功能 + /// + public class LaserCode + { + /// + /// 获取或设置编码类型 + /// + /// + /// 指定使用的编码方案 + /// 默认为None(无编码) + /// + public LaserCodeType CodeType { get; set; } = LaserCodeType.None; + + /// + /// 获取或设置编码值 + /// + /// + /// 对于不同类型的编码,解释方式不同 + /// 对于PRF编码,表示1111-1788范围内的编码值 + /// + public int CodeValue { get; set; } + + /// + /// 获取或设置附加参数 + /// + /// + /// 用于存储特定编码类型的额外信息 + /// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等 + /// + public Dictionary Parameters { get; set; } = new Dictionary(); + + /// + /// 判断当前编码是否与指定编码匹配 + /// + /// 要比较的编码 + /// 如果匹配返回true,否则返回false + /// + /// 匹配规则: + /// - 编码类型必须相同 + /// - 编码值必须相同 + /// 不考虑附加参数的匹配 + /// + public bool Matches(LaserCode other) + { + if (other == null) + return false; + + return CodeType == other.CodeType && CodeValue == other.CodeValue; + } + } +} \ No newline at end of file diff --git a/VERSION b/VERSION index 373f8c6..72f9fa8 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.3 \ No newline at end of file +0.2.4 \ No newline at end of file diff --git a/docs/articles/threat-source-spec.md b/docs/articles/threat-source-spec.md index 0850714..0e6733a 100644 --- a/docs/articles/threat-source-spec.md +++ b/docs/articles/threat-source-spec.md @@ -919,11 +919,11 @@ graph TB - 干扰抑制效果 - 跟踪恢复性能 -### 3.6 末敏导弹 +### 3.6 末敏弹 #### 3.6.1 工作原理 -末敏导弹系统由母弹和子弹两部分组成,通过多传感器融合实现精确打击。其基本工作过程如下: +末敏弹系统由母弹和子弹两部分组成,通过多传感器融合实现精确打击。其基本工作过程如下: 1. 母弹飞行阶段 - 母弹发射并飞向预定分离点 diff --git a/docs/project/develop_log.md b/docs/project/develop_log.md index 39dd68c..4b9b8a8 100644 --- a/docs/project/develop_log.md +++ b/docs/project/develop_log.md @@ -7,6 +7,10 @@ - 事件描述 - 分析处理 +## 2025-02-25 给激光半主动导弹和激光目标指示器增加激光编码特性 +- 增加了 6 种激光编码方式 +- 当激光编码方式和编码匹配,才能激活导引 + ## 2025-02-17 末敏弹的探测逻辑优化 - 优化了末敏弹的探测逻辑,完善了几个传感器的逻辑