增加激光编码功能

This commit is contained in:
Tian jianyong 2025-02-25 12:18:39 +08:00
parent 43e4549c6a
commit 0019f795f8
24 changed files with 1719 additions and 79 deletions

View File

@ -13,6 +13,19 @@
- 实现实时仿真数据同步
- 处理不同时间步长的协调
## [0.2.4] - 2025-02-25
### 功能优化
- 增加了激光半主动导弹和激光目标指示器的激光编码功能
- 增加了对应的测试用例
- 支持六种激光编码类型
- PRF脉冲重复频率编码
- PPM脉冲位置调制
- PWM脉冲宽度调制
- PseudoRandom伪随机编码
- Frequency频率编码
- Phase相位编码
## [0.2.3] - 2025-02-17
### 功能优化

View File

@ -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<LaserCodeMatchEvent>();
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<LaserCodeMismatchEvent>();
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<LaserCodeMismatchEvent>();
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<LaserCodeMismatchEvent>();
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<LaserCodeMatchEvent>();
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);
}
}
}

View File

@ -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<LaserIlluminationUpdateEvent>();
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<LaserIlluminationUpdateEvent>();
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<LaserIlluminationUpdateEvent>();
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<LaserIlluminationUpdateEvent>();
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<LaserIlluminationStartEvent>();
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<LaserIlluminationUpdateEvent>();
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);
}
}
}

View File

@ -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<LaserCodeMatchEvent>();
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<LaserCodeMismatchEvent>();
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);
}
}
}

View File

@ -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
}
}
}

View File

@ -1,5 +1,6 @@
using System;
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Guidance
{
@ -12,10 +13,20 @@ namespace ThreatSource.Guidance
/// - 运动参数记录
/// - 加速度限制
/// - 比例导引系数
/// - 事件发布
/// 是其他具体制导系统的基类
/// </remarks>
public class BasicGuidanceSystem : IGuidanceSystem
public class BasicGuidanceSystem : SimulationElement, IGuidanceSystem
{
/// <summary>
/// 获取或设置父实体ID
/// </summary>
/// <remarks>
/// 记录所属导弹的ID
/// 用于事件发布和状态关联
/// </remarks>
public string? ParentId { get; set; }
/// <summary>
/// 获取或设置是否有有效的制导信息
/// </summary>
@ -46,24 +57,6 @@ namespace ThreatSource.Guidance
/// </remarks>
public double ProportionalNavigationCoefficient { get; set; }
/// <summary>
/// 获取或设置导弹位置,单位:米
/// </summary>
/// <remarks>
/// 记录导弹的当前位置
/// 用于制导计算和状态更新
/// </remarks>
protected Vector3D Position { get; set; }
/// <summary>
/// 获取或设置导弹速度,单位:米/秒
/// </summary>
/// <remarks>
/// 记录导弹的当前速度
/// 用于制导计算和状态更新
/// </remarks>
protected Vector3D Velocity { get; set; }
/// <summary>
/// 获取或设置制导加速度,单位:米/平方秒
/// </summary>
@ -76,20 +69,22 @@ namespace ThreatSource.Guidance
/// <summary>
/// 初始化基础制导系统的新实例
/// </summary>
/// <param name="id">制导系统ID</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="proportionalNavigationCoefficient">比例导引系数</param>
/// <param name="simulationManager">仿真管理器实例</param>
/// <remarks>
/// 构造过程:
/// - 初始化基类参数
/// - 初始化制导状态
/// - 设置运动参数
/// - 配置制导参数
/// </remarks>
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;
}
/// <summary>
/// 更新制导系统状态
/// </summary>
/// <param name="deltaTime">时间步长,单位:秒</param>
/// <remarks>
/// 实现 SimulationElement 的抽象方法
/// 在此方法中不执行实际操作,制导系统通过 Update(deltaTime, position, velocity) 更新
/// </remarks>
public override void Update(double deltaTime)
{
// 警告:制导系统不应直接由仿真系统调用
Console.WriteLine("警告制导系统的Update(deltaTime)方法被直接调用,这可能是一个错误。");
// 制导系统应通过Update(deltaTime, position, velocity)方法更新
}
/// <summary>
/// 获取制导加速度指令
/// </summary>
@ -157,9 +167,9 @@ namespace ThreatSource.Guidance
/// - 加速度信息
/// 用于状态监控和调试
/// </remarks>
public virtual string GetStatus()
public override string GetStatus()
{
return $" 导引头状态: 有制导={HasGuidance}, 位置={Position}, 速度={Velocity}, 制导加速度={GuidanceAcceleration}";
return base.GetStatus() + $"\n 导引头状态: 有制导={HasGuidance}, 制导加速度={GuidanceAcceleration}";
}
}
}

View File

@ -1,4 +1,5 @@
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Guidance
{
@ -74,16 +75,18 @@ namespace ThreatSource.Guidance
/// <summary>
/// 初始化红外指令导引系统的新实例
/// </summary>
/// <param name="id">系统标识</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="guidanceCoefficient">制导系数</param>
/// <param name="simulationManager">模拟管理器</param>
/// <remarks>
/// 构造过程:
/// - 初始化基类参数
/// - 初始化向量记录
/// - 清零转向速率
/// </remarks>
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;

View File

@ -76,15 +76,6 @@ namespace ThreatSource.Guidance
/// </remarks>
private readonly Random random = new();
/// <summary>
/// 获取或设置仿真管理器实例
/// </summary>
/// <remarks>
/// 用于获取场景中的目标信息
/// 实现目标探测功能
/// </remarks>
public ISimulationManager SimulationManager { get; set; }
/// <summary>
/// 上一次探测到的目标位置
/// </summary>
@ -95,8 +86,9 @@ namespace ThreatSource.Guidance
private Vector3D lastTargetPosition;
/// <summary>
/// 初始化红外成像导系统的新实例
/// 初始化红外成像导系统的新实例
/// </summary>
/// <param name="id">系统标识</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="proportionalNavigationCoefficient">比例导引系数</param>
/// <param name="simulationManager">仿真管理器实例</param>
@ -106,9 +98,9 @@ namespace ThreatSource.Guidance
/// - 设置仿真管理器
/// - 初始化目标位置
/// </remarks>
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;
}

View File

@ -1,4 +1,5 @@
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Guidance
{
@ -111,15 +112,18 @@ namespace ThreatSource.Guidance
/// <summary>
/// 初始化激光驾束制导系统的新实例
/// </summary>
/// <param name="id">制导系统ID</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="guidanceCoefficient">制导系数</param>
/// <param name="simulationManager">仿真管理器实例</param>
/// <remarks>
/// 构造过程:
/// - 初始化基类参数
/// - 初始化激光参数
/// - 初始化控制参数
/// </remarks>
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;

View File

@ -1,4 +1,5 @@
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Guidance
{
@ -129,18 +130,54 @@ namespace ThreatSource.Guidance
/// </remarks>
private double LaserDivergenceAngle { get; set; }
/// <summary>
/// 获取或设置期望的激光编码
/// </summary>
/// <remarks>
/// 导弹期望接收的编码信息
/// 用于验证接收到的激光信号
/// </remarks>
private LaserCode? expectedLaserCode = null;
/// <summary>
/// 获取或设置是否要求编码匹配
/// </summary>
/// <remarks>
/// true表示只响应匹配编码的激光信号
/// false表示响应所有激光信号
/// 默认为false
/// </remarks>
private bool isCodeMatchRequired = false;
/// <summary>
/// 获取或设置支持的编码类型列表
/// </summary>
/// <remarks>
/// 定义导弹支持的编码类型
/// 默认支持PRF、PPM和PWM编码
/// </remarks>
private List<LaserCodeType> supportedCodeTypes = new List<LaserCodeType>
{
LaserCodeType.PRF,
LaserCodeType.PPM,
LaserCodeType.PWM
};
/// <summary>
/// 初始化激光半主动制导系统的新实例
/// </summary>
/// <param name="id">制导系统ID</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="guidanceCoefficient">制导系数</param>
/// <param name="simulationManager">仿真管理器实例</param>
/// <remarks>
/// 构造过程:
/// - 初始化基类参数
/// - 初始化目标信息
/// - 初始化激光参数
/// </remarks>
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";
}
/// <summary>
/// 设置期望的激光编码
/// </summary>
/// <param name="codeType">编码类型</param>
/// <param name="codeValue">编码值</param>
/// <remarks>
/// 设置过程:
/// - 检查编码类型是否支持
/// - 创建新的编码对象
/// - 设置编码类型和值
/// </remarks>
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}");
}
}
/// <summary>
/// 添加期望编码参数
/// </summary>
/// <param name="key">参数名称</param>
/// <param name="value">参数值</param>
/// <remarks>
/// 添加特定编码类型的额外参数
/// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等
/// </remarks>
public void AddExpectedCodeParameter(string key, object value)
{
if (expectedLaserCode != null)
{
expectedLaserCode.Parameters[key] = value;
Console.WriteLine($"激光半主动制导系统添加期望编码参数:{key}={value}");
}
}
/// <summary>
/// 设置是否要求编码匹配
/// </summary>
/// <param name="required">是否要求匹配</param>
/// <remarks>
/// true表示只响应匹配编码的激光信号
/// false表示响应所有激光信号
/// </remarks>
public void SetCodeMatchRequired(bool required)
{
isCodeMatchRequired = required;
Console.WriteLine($"激光半主动制导系统{(required ? "" : "")}编码匹配");
}
/// <summary>
/// 处理激光照射事件
/// </summary>
/// <param name="illuminationEvent">激光照射事件</param>
/// <remarks>
/// 处理过程:
/// - 检查编码是否匹配
/// - 如果要求匹配且不匹配,则忽略信号
/// - 如果匹配或不要求匹配,则处理信号
/// - 更新激光照射状态
/// </remarks>
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;
}
/// <summary>
/// 处理激光照射更新事件
/// </summary>
/// <param name="illuminationEvent">激光照射更新事件</param>
/// <remarks>
/// 处理过程:
/// - 检查编码是否匹配
/// - 如果要求匹配且不匹配,则忽略信号
/// - 如果匹配或不要求匹配,则处理信号
/// - 更新激光照射状态
/// </remarks>
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;
}
/// <summary>
/// 处理激光照射停止事件
/// </summary>
/// <param name="illuminationEvent">激光照射停止事件</param>
/// <remarks>
/// 处理过程:
/// - 停止激光照射状态
/// - 清理相关参数
/// </remarks>
public void ProcessLaserIlluminationStopEvent(LaserIlluminationStopEvent illuminationEvent)
{
LaserIlluminationOn = false;
HasGuidance = false; // 禁用制导
}
/// <summary>
/// 检查编码是否匹配
/// </summary>
/// <param name="receivedCode">接收到的编码</param>
/// <param name="isCodeEnabled">是否启用编码</param>
/// <returns>如果匹配返回true否则返回false</returns>
/// <remarks>
/// 匹配规则:
/// - 如果不要求匹配返回true
/// - 如果接收到的信号未启用编码,根据配置决定是否匹配
/// - 如果导弹未设置期望编码,根据配置决定是否匹配
/// - 检查编码类型是否支持
/// - 检查编码是否匹配
/// </remarks>
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);
}
/// <summary>
/// 发布编码匹配事件
/// </summary>
/// <param name="designatorId">激光定位器ID</param>
/// <param name="matchedCode">匹配的编码</param>
/// <remarks>
/// 发布过程:
/// - 创建事件对象
/// - 设置事件属性
/// - 发布到事件系统
/// </remarks>
private void PublishCodeMatchEvent(string? designatorId, LaserCode? matchedCode)
{
var matchEvent = new LaserCodeMatchEvent
{
MissileId = ParentId,
DesignatorId = designatorId,
MatchedCode = matchedCode
};
PublishEvent(matchEvent);
}
/// <summary>
/// 发布编码不匹配事件
/// </summary>
/// <param name="designatorId">激光定位器ID</param>
/// <param name="receivedCode">接收到的编码</param>
/// <remarks>
/// 发布过程:
/// - 创建事件对象
/// - 设置事件属性
/// - 发布到事件系统
/// </remarks>
private void PublishCodeMismatchEvent(string? designatorId, LaserCode? receivedCode)
{
var mismatchEvent = new LaserCodeMismatchEvent
{
MissileId = ParentId,
DesignatorId = designatorId,
ExpectedCode = expectedLaserCode,
ReceivedCode = receivedCode
};
PublishEvent(mismatchEvent);
}
}
}

View File

@ -113,17 +113,9 @@ namespace ThreatSource.Guidance
private double jammingPower = 0;
/// <summary>
/// 获取或设置仿真管理器实例
/// </summary>
/// <remarks>
/// 用于获取场景中的目标信息
/// 实现目标探测功能
/// </remarks>
public ISimulationManager SimulationManager { get; set; }
/// <summary>
/// 初始化毫米波导引头系统的新实例
/// 初始化毫米波制导系统的新实例
/// </summary>
/// <param name="id">制导系统ID</param>
/// <param name="maxAcceleration">最大加速度,单位:米/平方秒</param>
/// <param name="guidanceCoefficient">制导系数</param>
/// <param name="simulationManager">仿真管理器实例</param>
@ -134,9 +126,9 @@ namespace ThreatSource.Guidance
/// - 初始化目标位置
/// - 注册干扰事件处理
/// </remarks>
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;
// 订阅干扰事件

View File

@ -83,6 +83,25 @@ namespace ThreatSource.Indicator
/// </remarks>
public double LaserDivergenceAngle { get; private set; } = 0;
/// <summary>
/// 获取或设置激光编码
/// </summary>
/// <remarks>
/// 包含激光信号的编码信息
/// 用于抗干扰和安全识别
/// </remarks>
private LaserCode? currentLaserCode = null;
/// <summary>
/// 获取或设置是否启用编码
/// </summary>
/// <remarks>
/// true表示激光信号使用编码
/// false表示激光信号不使用编码
/// 默认为false
/// </remarks>
private bool isCodeEnabled = false;
/// <summary>
/// 初始化激光指示器的新实例
/// </summary>
@ -261,38 +280,122 @@ namespace ThreatSource.Indicator
base.Deactivate();
}
/// <summary>
/// 设置激光编码
/// </summary>
/// <param name="codeType">编码类型</param>
/// <param name="codeValue">编码值</param>
/// <remarks>
/// 设置过程:
/// - 创建新的编码对象
/// - 设置编码类型和值
/// - 启用编码功能
/// </remarks>
public void SetLaserCode(LaserCodeType codeType, int codeValue)
{
currentLaserCode = new LaserCode
{
CodeType = codeType,
CodeValue = codeValue
};
isCodeEnabled = true;
Console.WriteLine($"激光目标指示器 {Id} 设置编码:类型={codeType},值={codeValue}");
}
/// <summary>
/// 添加编码参数
/// </summary>
/// <param name="key">参数名称</param>
/// <param name="value">参数值</param>
/// <remarks>
/// 添加特定编码类型的额外参数
/// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等
/// </remarks>
public void AddCodeParameter(string key, object value)
{
if (currentLaserCode != null)
{
currentLaserCode.Parameters[key] = value;
Console.WriteLine($"激光目标指示器 {Id} 添加编码参数:{key}={value}");
}
}
/// <summary>
/// 启用或禁用编码
/// </summary>
/// <param name="enable">是否启用编码</param>
/// <remarks>
/// true表示启用编码功能
/// false表示禁用编码功能
/// </remarks>
public void EnableCode(bool enable)
{
isCodeEnabled = enable;
Console.WriteLine($"激光目标指示器 {Id} {(enable ? "" : "")}编码功能");
}
/// <summary>
/// 发布激光照射开始事件
/// </summary>
/// <remarks>
/// 发布过程:
/// - 创建事件对象
/// - 设置事件参数
/// - 设置事件属性
/// - 添加编码信息
/// - 发布到事件系统
/// - 记录事件日志
/// </remarks>
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);
}
/// <summary>
/// <summary>
/// 发布激光照射更新事件
/// </summary>
/// <remarks>
/// 发布过程:
/// - 创建更新事件
/// - 设置最新参数
/// - 创建事件对象
/// - 设置事件属性
/// - 添加编码信息
/// - 发布到事件系统
/// - 记录更新日志
/// </remarks>
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);
}
/// <summary>

View File

@ -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
);
}

View File

@ -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);
}
/// <summary>

View File

@ -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;
}

View File

@ -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<LaserIlluminationStartEvent>(OnLaserIlluminationStart);
SimulationManager.SubscribeToEvent<LaserIlluminationUpdateEvent>(OnLaserIlluminationUpdate);
SimulationManager.SubscribeToEvent<LaserIlluminationStopEvent>(OnLaserIlluminationStop);
}
/// <summary>
@ -91,8 +104,8 @@ namespace ThreatSource.Missile
/// <remarks>
/// 处理过程:
/// - 验证激光指示器和目标
/// - 更新制导系统参数
/// - 开始目标跟踪
/// - 更新目标位置和速度
/// - 更新制导参数
/// </remarks>
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
/// </remarks>
private void OnLaserIlluminationStop(LaserIlluminationStopEvent evt)
{
// 停用激光指示器
LaserGuidanceSystem.DeactivateLaserDesignator();
// 处理激光编码
if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance)
{
laserGuidance.ProcessLaserIlluminationStopEvent(evt);
// 直接设置IsGuidance属性
IsGuidance = laserGuidance.HasGuidance;
}
}
/// <summary>
@ -238,9 +283,23 @@ namespace ThreatSource.Missile
/// </remarks>
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;
}
/// <summary>
/// 设置激光编码
/// </summary>
/// <param name="codeType">编码类型</param>
/// <param name="codeValue">编码值</param>
/// <remarks>
/// 设置导弹期望接收的激光编码类型和值
/// 用于验证接收到的激光信号
/// </remarks>
public void SetLaserCode(LaserCodeType codeType, int codeValue)
{
if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance)
{
laserGuidance.SetExpectedLaserCode(codeType, codeValue);
}
}
/// <summary>
/// 添加激光编码参数
/// </summary>
/// <param name="key">参数名称</param>
/// <param name="value">参数值</param>
/// <remarks>
/// 添加特定编码类型的额外参数
/// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等
/// </remarks>
public void AddLaserCodeParameter(string key, object value)
{
if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance)
{
laserGuidance.AddExpectedCodeParameter(key, value);
}
}
/// <summary>
/// 设置是否要求编码匹配
/// </summary>
/// <param name="required">是否要求匹配</param>
/// <remarks>
/// true表示只响应匹配编码的激光信号
/// false表示响应所有激光信号
/// </remarks>
public void SetCodeMatchRequired(bool required)
{
if (LaserGuidanceSystem is LaserSemiActiveGuidanceSystem laserGuidance)
{
laserGuidance.SetCodeMatchRequired(required);
}
}
}
}

View File

@ -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
);
}
/// <summary>

View File

@ -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);
// 根据仿真步长和转速计算允许的角度误差

View File

@ -75,6 +75,24 @@ namespace ThreatSource.Simulation
/// 标识被激光照射的目标实体
/// </remarks>
public string? TargetId { get; set; }
/// <summary>
/// 获取或设置激光编码信息
/// </summary>
/// <remarks>
/// 包含激光信号的编码类型和编码值
/// 用于抗干扰和安全识别
/// </remarks>
public LaserCode? LaserCode { get; set; }
/// <summary>
/// 获取或设置是否启用编码
/// </summary>
/// <remarks>
/// true表示激光信号使用编码
/// false表示激光信号不使用编码
/// </remarks>
public bool IsCodeEnabled { get; set; }
}
/// <summary>
@ -98,9 +116,27 @@ namespace ThreatSource.Simulation
/// 获取或设置被照射目标的ID
/// </summary>
/// <remarks>
/// 标识正在被照射的目标实体
/// 标识激光照射的目标实体
/// </remarks>
public string? TargetId { get; set; }
/// <summary>
/// 获取或设置激光编码信息
/// </summary>
/// <remarks>
/// 包含激光信号的编码类型和编码值
/// 用于抗干扰和安全识别
/// </remarks>
public LaserCode? LaserCode { get; set; }
/// <summary>
/// 获取或设置是否启用编码
/// </summary>
/// <remarks>
/// true表示激光信号使用编码
/// false表示激光信号不使用编码
/// </remarks>
public bool IsCodeEnabled { get; set; }
}
/// <summary>
@ -554,4 +590,80 @@ namespace ThreatSource.Simulation
/// </summary>
public double WavelengthMax { get; set; }
}
/// <summary>
/// 激光编码匹配事件,表示导弹成功匹配激光编码
/// </summary>
/// <remarks>
/// 用于通知系统导弹已成功识别激光编码
/// 触发时机:导弹接收到匹配的激光编码时
/// </remarks>
public class LaserCodeMatchEvent : SimulationEvent
{
/// <summary>
/// 获取或设置导弹ID
/// </summary>
/// <remarks>
/// 标识接收激光信号的导弹
/// </remarks>
public string? MissileId { get; set; }
/// <summary>
/// 获取或设置激光定位器ID
/// </summary>
/// <remarks>
/// 标识发送激光信号的定位器
/// </remarks>
public string? DesignatorId { get; set; }
/// <summary>
/// 获取或设置匹配的激光编码
/// </summary>
/// <remarks>
/// 包含成功匹配的编码信息
/// </remarks>
public LaserCode? MatchedCode { get; set; }
}
/// <summary>
/// 激光编码不匹配事件,表示导弹接收到不匹配的激光编码
/// </summary>
/// <remarks>
/// 用于通知系统导弹接收到不匹配的激光编码
/// 触发时机:导弹接收到不匹配的激光编码时
/// </remarks>
public class LaserCodeMismatchEvent : SimulationEvent
{
/// <summary>
/// 获取或设置导弹ID
/// </summary>
/// <remarks>
/// 标识接收激光信号的导弹
/// </remarks>
public string? MissileId { get; set; }
/// <summary>
/// 获取或设置激光定位器ID
/// </summary>
/// <remarks>
/// 标识发送激光信号的定位器
/// </remarks>
public string? DesignatorId { get; set; }
/// <summary>
/// 获取或设置期望的激光编码
/// </summary>
/// <remarks>
/// 导弹期望接收的编码信息
/// </remarks>
public LaserCode? ExpectedCode { get; set; }
/// <summary>
/// 获取或设置接收到的激光编码
/// </summary>
/// <remarks>
/// 导弹实际接收到的编码信息
/// </remarks>
public LaserCode? ReceivedCode { get; set; }
}
}

View File

@ -90,6 +90,16 @@ namespace ThreatSource.Simulation.Testing
return _publishedEvents;
}
/// <summary>
/// 获取指定类型的已发布事件
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <returns>指定类型的已发布事件列表</returns>
public List<T> GetPublishedEvents<T>() where T : SimulationEvent
{
return _publishedEvents.OfType<T>().ToList();
}
/// <summary>
/// 获取已接收的事件
/// </summary>

View File

@ -0,0 +1,139 @@
using System.Collections.Generic;
namespace ThreatSource.Utils
{
/// <summary>
/// 激光编码类型枚举,定义支持的不同编码类型
/// </summary>
/// <remarks>
/// 包含多种常见的激光编码类型:
/// - PRF脉冲重复频率编码
/// - PPM脉冲位置调制
/// - PWM脉冲宽度调制
/// - PseudoRandom伪随机编码
/// - Frequency频率编码
/// - Phase相位编码
/// </remarks>
public enum LaserCodeType
{
/// <summary>
/// 无编码
/// </summary>
None = 0,
/// <summary>
/// 脉冲重复频率编码
/// </summary>
/// <remarks>
/// 最常用的激光编码方式
/// NATO标准使用的编码方式
/// 通常使用4位数字编码1111-1788
/// </remarks>
PRF = 1,
/// <summary>
/// 脉冲位置调制
/// </summary>
/// <remarks>
/// 通过改变脉冲在时间窗口中的位置来编码信息
/// 抗干扰能力较强
/// </remarks>
PPM = 2,
/// <summary>
/// 脉冲宽度调制
/// </summary>
/// <remarks>
/// 通过改变脉冲宽度来编码信息
/// 实现简单,抗干扰能力一般
/// </remarks>
PWM = 3,
/// <summary>
/// 伪随机编码
/// </summary>
/// <remarks>
/// 使用伪随机序列对激光信号进行调制
/// 抗干扰能力极强
/// </remarks>
PseudoRandom = 4,
/// <summary>
/// 频率编码
/// </summary>
/// <remarks>
/// 使用不同频率的激光进行编码
/// 可以同时使用多个频率
/// </remarks>
Frequency = 5,
/// <summary>
/// 相位编码
/// </summary>
/// <remarks>
/// 通过调制激光信号的相位来编码信息
/// 编码容量大,抗干扰能力强
/// </remarks>
Phase = 6
}
/// <summary>
/// 激光编码类,用于表示激光信号的编码信息
/// </summary>
/// <remarks>
/// 提供了激光编码的基本属性和方法:
/// - 编码类型
/// - 编码值
/// - 附加参数
/// - 编码匹配判断
/// 用于实现激光信号的编码和解码功能
/// </remarks>
public class LaserCode
{
/// <summary>
/// 获取或设置编码类型
/// </summary>
/// <remarks>
/// 指定使用的编码方案
/// 默认为None无编码
/// </remarks>
public LaserCodeType CodeType { get; set; } = LaserCodeType.None;
/// <summary>
/// 获取或设置编码值
/// </summary>
/// <remarks>
/// 对于不同类型的编码,解释方式不同
/// 对于PRF编码表示1111-1788范围内的编码值
/// </remarks>
public int CodeValue { get; set; }
/// <summary>
/// 获取或设置附加参数
/// </summary>
/// <remarks>
/// 用于存储特定编码类型的额外信息
/// 如PPM编码的脉冲位置模式、PWM编码的脉冲宽度等
/// </remarks>
public Dictionary<string, object> Parameters { get; set; } = new Dictionary<string, object>();
/// <summary>
/// 判断当前编码是否与指定编码匹配
/// </summary>
/// <param name="other">要比较的编码</param>
/// <returns>如果匹配返回true否则返回false</returns>
/// <remarks>
/// 匹配规则:
/// - 编码类型必须相同
/// - 编码值必须相同
/// 不考虑附加参数的匹配
/// </remarks>
public bool Matches(LaserCode other)
{
if (other == null)
return false;
return CodeType == other.CodeType && CodeValue == other.CodeValue;
}
}
}

View File

@ -1 +1 @@
0.2.3
0.2.4

View File

@ -919,11 +919,11 @@ graph TB
- 干扰抑制效果
- 跟踪恢复性能
### 3.6 末敏
### 3.6 末敏弹
#### 3.6.1 工作原理
末敏弹系统由母弹和子弹两部分组成,通过多传感器融合实现精确打击。其基本工作过程如下:
末敏弹系统由母弹和子弹两部分组成,通过多传感器融合实现精确打击。其基本工作过程如下:
1. 母弹飞行阶段
- 母弹发射并飞向预定分离点

View File

@ -7,6 +7,10 @@
- 事件描述
- 分析处理
## 2025-02-25 给激光半主动导弹和激光目标指示器增加激光编码特性
- 增加了 6 种激光编码方式
- 当激光编码方式和编码匹配,才能激活导引
## 2025-02-17 末敏弹的探测逻辑优化
- 优化了末敏弹的探测逻辑,完善了几个传感器的逻辑