增加了烟幕对毫米波制导导弹的影响,完善了一些测试

This commit is contained in:
Tian jianyong 2025-04-22 16:27:15 +08:00
parent c8240a075d
commit 6e990f65a4
16 changed files with 311 additions and 694 deletions

View File

@ -15,6 +15,10 @@
- 毫米波跟踪和锁定阶段采用脉冲多普勒制导、目标 RCS 特征矩阵
- 多种发射弹道模式:低平弹道、高抛弹道、俯冲弹道
- 双模、多模制导
- 烟幕弹对毫米波末制导、末敏弹、激光目标指示器、激光驾束仪、红外指令制导指令发射端的干扰
## [0.2.12] - 2025-04-18
- 改进了红外成像制导的目标识别和烟幕弹干扰算法
## [0.2.11] - 2025-04-14
- 增加了激光诱偏目标的干扰功能

View File

@ -60,6 +60,7 @@ namespace ThreatSource.Tests.Indicator
_simulationManager
);
_simulationManager.RegisterEntity("beamRider1", _laserBeamRider);
_simulationManager.RegisterEntity("target1", _tank);
}
[Fact]
@ -78,14 +79,20 @@ namespace ThreatSource.Tests.Indicator
[Fact]
public void Activate_SetsCorrectState()
{
// Arrange
_testAdapter.ClearEvents(); // 确保开始时事件列表是空的
// Act
_laserBeamRider.Activate();
// Assert
Assert.True(_laserBeamRider.IsActive);
Assert.True(_laserBeamRider.IsBeamOn);
var publishedEvents = _testAdapter.GetPublishedEvents();
Assert.Contains(publishedEvents, evt => evt is LaserBeamStartEvent);
var publishedEvents = _testAdapter.GetPublishedEvents(); // 获取 Activate 产生的事件
// 注意: Activate 可能发布 LaserBeamStartEvent 和 IndicatorActivatedEvent
Assert.Contains(publishedEvents, evt => evt is LaserBeamEvent); // 确认发布了 LaserBeamEvent
// 可以选择性地添加对 IndicatorActivatedEvent 的检查
// Assert.Contains(publishedEvents, evt => evt is IndicatorActivatedEvent);
}
[Fact]
@ -93,6 +100,7 @@ namespace ThreatSource.Tests.Indicator
{
// Arrange
_laserBeamRider.Activate();
_testAdapter.ClearEvents(); // 清除 Activate 产生的事件
// Act
_laserBeamRider.Deactivate();
@ -100,7 +108,8 @@ namespace ThreatSource.Tests.Indicator
// Assert
Assert.False(_laserBeamRider.IsActive);
Assert.False(_laserBeamRider.IsBeamOn);
var publishedEvents = _testAdapter.GetPublishedEvents();
var publishedEvents = _testAdapter.GetPublishedEvents(); // 获取 Deactivate 产生的事件
// 注意: Deactivate 可能发布 LaserBeamStopEvent 和 IndicatorDeactivatedEvent
Assert.Contains(publishedEvents, evt => evt is LaserBeamStopEvent);
}
@ -109,14 +118,14 @@ namespace ThreatSource.Tests.Indicator
{
// Arrange
_laserBeamRider.Activate();
_testAdapter.GetPublishedEvents(); // 清除之前的事件
_testAdapter.ClearEvents(); // 清除 Activate 产生的事件
// Act
_laserBeamRider.Update(0.1);
// Assert
var publishedEvents = _testAdapter.GetPublishedEvents();
Assert.Contains(publishedEvents, evt => evt is LaserBeamUpdateEvent);
var publishedEvents = _testAdapter.GetPublishedEvents(); // 获取 Update 产生的事件
Assert.Contains(publishedEvents, evt => evt is LaserBeamEvent); // 用户确认 Update 发布 LaserBeamEvent
}
[Fact]

View File

@ -144,7 +144,7 @@ namespace ThreatSource.Tests.Jamming
// Assert - 干扰清除后激光束应该恢复
Assert.IsFalse(_laserBeamRider.IsJammed, "干扰已清除,激光驾束仪应该恢复正常");
Assert.IsTrue(_laserBeamRider.IsBeamOn, "干扰清除后激光束应该重新开启");
Assert.IsTrue(publishedEvents.Any(evt => evt is LaserBeamStartEvent), "应该发布激光束开始事件");
Assert.IsTrue(publishedEvents.Any(evt => evt is LaserBeamEvent), "应该发布激光束开始事件");
}
[TestMethod]

View File

@ -1,376 +0,0 @@
using Xunit;
using ThreatSource.Missile;
using ThreatSource.Simulation;
using ThreatSource.Utils;
using ThreatSource.Tests.Simulation;
using ThreatSource.Guidance;
using ThreatSource.Indicator;
using ThreatSource.Equipment;
using ThreatSource.Sensor;
using System;
using System.Reflection;
namespace ThreatSource.Tests.Missile
{
/// <summary>
/// 激光半主动导弹集成测试类
/// </summary>
/// <remarks>
/// 该测试类验证激光半主动导弹的整体性能:
/// - 验证导弹能否在正常条件下命中目标
/// - 验证导弹在低功率条件下的行为
/// - 验证导弹在干扰环境下的表现
/// </remarks>
public class LaserSemiActiveGuidedMissileIntegrationTests
{
private readonly SimulationManager _simulationManager;
private readonly TestSimulationAdapter _testAdapter;
private readonly LaserSemiActiveGuidedMissile _missile;
private readonly MissileProperties _properties;
private readonly LaserDesignator _laserDesignator;
private readonly MockTarget _target;
// 定义命中距离阈值(米)
private const double HIT_THRESHOLD = 10.0;
// 定义最大模拟时间(秒)
private const double MAX_SIMULATION_TIME = 30.0;
// 定义模拟时间步长(秒)
private const double TIME_STEP = 0.025;
public LaserSemiActiveGuidedMissileIntegrationTests()
{
_simulationManager = new SimulationManager();
_testAdapter = new TestSimulationAdapter(_simulationManager);
_simulationManager.SetSimulationAdapter(_testAdapter);
_properties = new MissileProperties
{
MaxSpeed = 1000,
MaxFlightTime = 100,
MaxFlightDistance = 10000,
MaxAcceleration = 50,
ProportionalNavigationCoefficient = 3,
Mass = 100,
ExplosionRadius = 10,
HitProbability = 0.9,
Type = MissileType.LaserSemiActiveGuidance
};
var missileInitialMotion = new MotionParameters
{
Position = new Vector3D(0, 0, 0),
Orientation = new Orientation(0, 0, 0),
InitialSpeed = 100
};
var laserDesignatorInitialMotion = new MotionParameters
{
Position = new Vector3D(-100, 0, 0),
Orientation = new Orientation(0, 0, 0),
InitialSpeed = 0
};
// 创建并注册激光指示器位置在导弹后方100米处
var laserDesignatorConfig = new LaserDesignatorConfig
{
LaserPower = 100,
LaserDivergenceAngle = 0.001,
LaserWavelength = 1.06
};
_laserDesignator = new LaserDesignator(
"laser1",
"target1",
"missile1",
laserDesignatorConfig,
laserDesignatorInitialMotion,
_simulationManager
);
_testAdapter.AddTestEntity("laser1", _laserDesignator);
_simulationManager.RegisterEntity("laser1", _laserDesignator);
// 创建并注册模拟的目标位置在导弹前方1000米处
var targetInitialMotion = new MotionParameters
{
Position = new Vector3D(1000, 0, 0),
Orientation = new Orientation(0, 0, 0),
InitialSpeed = 0
};
_target = new MockTarget("target1", targetInitialMotion, _simulationManager);
_testAdapter.AddTestEntity("target1", _target);
// 创建激光代码配置
var laserCodeConfig = new LaserCodeConfig
{
Code = new LaserCode
{
CodeType = LaserCodeType.PRF,
CodeValue = 1234
}
};
// 创建导引系统配置
var guidanceConfig = new LaserSemiActiveGuidanceConfig
{
FieldOfViewAngle = 30,
LensDiameter = 0.1,
SensorDiameter = 0.03,
FocusedSpotDiameter = 0.006,
ReflectionCoefficient = 0.2,
TargetReflectiveArea = 1.0,
LockThreshold = 1e-12,
SpotOffsetSensitivity = 0.5
};
// 创建导弹并注册到仿真管理器
_missile = new LaserSemiActiveGuidedMissile(
"missile1",
_properties,
missileInitialMotion,
laserCodeConfig,
guidanceConfig,
_simulationManager
);
_testAdapter.AddTestEntity("missile1", _missile);
_simulationManager.RegisterEntity("missile1", _missile);
}
// 模拟的目标类
private class MockTarget : Tank
{
public MockTarget(string id, MotionParameters motionParameters, ISimulationManager simulationManager)
: base(id, new EquipmentProperties(), motionParameters, simulationManager)
{
// 不需要额外设置属性,因为基类构造函数已经设置了
}
}
/// <summary>
/// 模拟导弹飞行过程,直到命中目标或超时
/// </summary>
/// <param name="laserPower">激光功率</param>
/// <returns>导弹与目标之间的最小距离</returns>
private double SimulateMissileFlight(double laserPower)
{
_laserDesignator.LaserPower = laserPower;
// 激活激光指示器(如果尚未激活)
if (!_laserDesignator.IsActive)
{
_laserDesignator.Activate(); // Activate方法内部会启动激光照射
}
// 手动创建并发布激光照射开始事件,确保事件被正确传递到制导系统
var illuminationEvent = new LaserIlluminationUpdateEvent
{
LaserDesignatorId = _laserDesignator.Id,
TargetId = _target.Id,
LaserCodeConfig = new LaserCodeConfig
{
Code = new LaserCode
{
CodeType = LaserCodeType.PPM,
CodeValue = 1234
}
}
};
_simulationManager.PublishEvent(illuminationEvent);
// 发射导弹
_missile.Fire();
_missile.Activate();
double minDistance = double.MaxValue;
double simulationTime = 0;
// 模拟导弹飞行,直到命中目标或超时
while (simulationTime < MAX_SIMULATION_TIME)
{
// 更新导弹状态
_missile.Update(TIME_STEP);
_laserDesignator.Update(TIME_STEP); // Update方法内部会发布激光照射更新事件
// 手动发布激光照射更新事件,确保事件被正确传递到制导系统
var updateEvent = new LaserIlluminationUpdateEvent
{
LaserDesignatorId = _laserDesignator.Id,
TargetId = _target.Id,
LaserCodeConfig = new LaserCodeConfig
{
Code = new LaserCode
{
CodeType = LaserCodeType.PPM,
CodeValue = 1234
}
}
};
_simulationManager.PublishEvent(updateEvent);
// 计算导弹与目标之间的距离
double distance = (_missile.Position - _target.Position).Magnitude();
// 更新最小距离
minDistance = Math.Min(minDistance, distance);
// 如果导弹已经命中目标,则结束模拟
if (distance <= HIT_THRESHOLD)
{
Console.WriteLine($"导弹命中目标!时间:{simulationTime:F1}秒,距离:{distance:F2}米");
break;
}
// 获取导弹状态
string status = _missile.GetStatus();
bool isExploded = status.Contains("爆炸阶段");
bool isSelfDestructed = status.Contains("自毁阶段");
// 如果导弹已经爆炸或自毁,则结束模拟
if (isExploded || isSelfDestructed)
{
Console.WriteLine($"导弹已{(isExploded ? "" : "")}!时间:{simulationTime:F1}秒,距离:{distance:F2}米");
break;
}
// 更新模拟时间
simulationTime += TIME_STEP;
}
// 停用激光指示器,这会自动停止激光照射
_laserDesignator.Deactivate();
if (simulationTime >= MAX_SIMULATION_TIME)
{
Console.WriteLine($"模拟超时!最小距离:{minDistance:F2}米");
}
return minDistance;
}
[Fact]
public void Missile_NormalConditions_HitsTarget()
{
// 使用正常激光功率模拟导弹飞行
double minDistance = SimulateMissileFlight(100.0);
// 断言导弹能够命中目标
Assert.True(minDistance <= HIT_THRESHOLD, $"导弹未能命中目标,最小距离:{minDistance:F2}米");
}
[Fact]
public void Missile_LowLaserPower_FailsToHitTarget()
{
// 使用低于锁定阈值的激光功率模拟导弹飞行
// 根据计算锁定阈值对应的激光功率约为0.159瓦特,使用其一半的值
double minDistance = SimulateMissileFlight(0.079);
// 由于导弹初始方向已偏离目标,在低功率条件下应无法校正轨道
// 断言导弹无法命中目标
Assert.True(minDistance > HIT_THRESHOLD, $"导弹在低功率条件下仍然命中了目标,最小距离:{minDistance:F2}米");
}
[Fact]
public void Missile_NoLaserPower_FailsToHitTarget()
{
// 使用零激光功率模拟导弹飞行
double minDistance = SimulateMissileFlight(0.0);
// 输出最小距离
Console.WriteLine($"导弹在无激光信号条件下与目标的最小距离:{minDistance:F2}米");
// 由于导弹初始方向已偏离目标,在无激光信号条件下应无法校正轨道
// 断言导弹无法命中目标
Assert.True(minDistance > HIT_THRESHOLD, $"导弹在无激光信号条件下仍然命中了目标,最小距离:{minDistance:F2}米");
}
[Fact]
public void Missile_IntermittentLaserPower_StillHitsTarget()
{
// 设置初始激光功率
_laserDesignator.LaserPower = 100.0;
// 确保激光指示器处于激活状态
if (!_laserDesignator.IsActive)
{
_laserDesignator.Activate();
}
// 发射导弹
_missile.Fire();
_missile.Activate();
double minDistance = double.MaxValue;
double simulationTime = 0;
// 模拟导弹飞行,激光功率间歇性变化
while (simulationTime < MAX_SIMULATION_TIME)
{
// 每2秒切换一次激光功率
if ((int)(simulationTime / 2) % 2 == 0)
{
_laserDesignator.LaserPower = 100.0; // 高功率
}
else
{
// 使用低于锁定阈值的激光功率约为0.159瓦特的一半)
_laserDesignator.LaserPower = 0.079;
}
// 更新导弹状态
_missile.Update(TIME_STEP);
_laserDesignator.Update(TIME_STEP);
// 手动发布激光照射更新事件,确保事件被正确传递到制导系统
var updateEvent = new LaserIlluminationUpdateEvent
{
LaserDesignatorId = _laserDesignator.Id,
TargetId = _target.Id,
LaserCodeConfig = new LaserCodeConfig
{
Code = new LaserCode
{
CodeType = LaserCodeType.PPM,
CodeValue = 1234
}
}
};
_simulationManager.PublishEvent(updateEvent);
// 计算导弹与目标之间的距离
double distance = (_missile.Position - _target.Position).Magnitude();
// 更新最小距离
minDistance = Math.Min(minDistance, distance);
// 如果导弹已经命中目标,则结束模拟
if (distance <= HIT_THRESHOLD)
{
Console.WriteLine($"导弹命中目标!时间:{simulationTime:F1}秒,距离:{distance:F2}米");
break;
}
// 获取导弹状态
string status = _missile.GetStatus();
bool isExploded = status.Contains("爆炸阶段");
bool isSelfDestructed = status.Contains("自毁阶段");
// 如果导弹已经爆炸或自毁,则结束模拟
if (isExploded || isSelfDestructed)
{
Console.WriteLine($"导弹已{(isExploded ? "" : "")}!时间:{simulationTime:F1}秒,距离:{distance:F2}米");
break;
}
// 更新模拟时间
simulationTime += TIME_STEP;
}
// 停用激光指示器
_laserDesignator.Deactivate();
// 即使在间歇性激光照射下,导弹仍应能够命中目标
// 断言导弹能够在间歇性激光照射下命中目标
Assert.True(minDistance <= HIT_THRESHOLD, $"导弹在间歇性激光照射下未能命中目标,最小距离:{minDistance:F2}米");
}
}
}

View File

@ -18,31 +18,31 @@ namespace ThreatSource.Guidance
public class InfraredCommandGuidanceSystem : BasicGuidanceSystem
{
/// <summary>
/// 跟踪器到导弹的方向向量
/// 跟踪器到导弹的方向向量 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录上一次接收到的跟踪器指向导弹的方向
/// 用于计算导弹的相对位置
/// </remarks>
private Vector3D lastTrackerToMissileVector;
private Vector3D? lastTrackerToMissileVector { get; set; }
/// <summary>
/// 跟踪器到目标的方向向量
/// 跟踪器到目标的方向向量 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录上一次接收到的跟踪器指向目标的方向
/// 用于计算目标的相对位置
/// </remarks>
private Vector3D lastTrackerToTargetVector;
private Vector3D? lastTrackerToTargetVector { get; set; }
/// <summary>
/// 上一次的期望飞行方向
/// 上一次的期望飞行方向 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录上一次计算的期望飞行方向
/// 用于计算转向速率
/// </remarks>
private Vector3D lastDesiredDirection;
private Vector3D? lastDesiredDirection { get; set; }
/// <summary>
/// 当前转向速率,单位:弧度/秒
@ -90,9 +90,6 @@ namespace ThreatSource.Guidance
public InfraredCommandGuidanceSystem(string id, InfraredCommandGuidanceConfig guidanceConfig, double maxAcceleration, double guidanceCoefficient, ISimulationManager simulationManager)
: base(id, maxAcceleration, guidanceCoefficient, simulationManager)
{
lastTrackerToMissileVector = Vector3D.Zero;
lastTrackerToTargetVector = Vector3D.Zero;
lastDesiredDirection = Vector3D.Zero;
turnRate = 0;
InitializeJamming(guidanceConfig.JammingResistanceThreshold, [JammingType.Infrared]);
}
@ -120,12 +117,20 @@ namespace ThreatSource.Guidance
else
{
GuidanceAcceleration = Vector3D.Zero;
lastTrackerToMissileVector = null;
lastTrackerToTargetVector = null;
lastDesiredDirection = null;
turnRate = 0;
}
}
else
{
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
lastTrackerToMissileVector = null;
lastTrackerToTargetVector = null;
lastDesiredDirection = null;
turnRate = 0;
}
}
@ -161,29 +166,34 @@ namespace ThreatSource.Guidance
/// </remarks>
protected void CalculateGuidanceAcceleration(double deltaTime)
{
// 计算期望飞行方向(从导弹指向目标)
if (lastTrackerToMissileVector == null || lastTrackerToTargetVector == null)
{
GuidanceAcceleration = Vector3D.Zero;
HasGuidance = false;
return;
}
Vector3D currentDesiredDirection = (lastTrackerToTargetVector - lastTrackerToMissileVector).Normalize();
// 计算当前飞行方向
Vector3D currentDirection = Velocity.Normalize();
// 更新转向速率
if (lastDesiredDirection != Vector3D.Zero)
if (lastDesiredDirection != null && deltaTime > 0)
{
double instantTurnRate = Vector3D.CrossProduct(lastDesiredDirection, currentDesiredDirection).Magnitude() / deltaTime;
turnRate = turnRate * (1 - TurnRateSmoothingFactor) + instantTurnRate * TurnRateSmoothingFactor;
}
else
{
turnRate = 0;
}
// 计算带有提前量的期望方向
Vector3D leadDirection = Vector3D.CrossProduct(currentDesiredDirection, Vector3D.CrossProduct(currentDesiredDirection, currentDirection).Normalize());
Vector3D desiredDirectionWithLead = (currentDesiredDirection + leadDirection * turnRate * LeadTimeFactor).Normalize();
// 计算转向轴
Vector3D turnAxis = Vector3D.CrossProduct(currentDirection, desiredDirectionWithLead).Normalize();
// 计算所需转向角度
double turnAngle = Vector3D.AngleBetween(currentDirection, desiredDirectionWithLead);
// 计算制导加速度
double accelerationMagnitude = ProportionalNavigationCoefficient * turnAngle * Velocity.Magnitude();
GuidanceAcceleration = Vector3D.CrossProduct(turnAxis, currentDirection) * accelerationMagnitude;
// 限制最大加速度
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
{
GuidanceAcceleration = GuidanceAcceleration.Normalize() * MaxAcceleration;
@ -205,9 +215,15 @@ namespace ThreatSource.Guidance
/// </remarks>
public override string GetStatus()
{
string angleStr = "null";
if (lastTrackerToTargetVector != null && lastTrackerToMissileVector != null)
{
angleStr = Vector3D.AngleBetween(lastTrackerToTargetVector, lastTrackerToMissileVector).ToString("F3");
}
return base.GetStatus() +
$" 转向角度: {Vector3D.AngleBetween(lastTrackerToTargetVector, lastTrackerToMissileVector)} 弧度," +
$" 转向速率: {turnRate} 弧度/秒";
$" 转向角度: {angleStr} 弧度," +
$" 转向速率: {turnRate:F3} 弧度/秒";
}
}
}

View File

@ -372,8 +372,6 @@ namespace ThreatSource.Guidance
if (thermalPattern == null)
{
Console.WriteLine($"警告: 目标 {target.Id} 未提供 ThermalPattern无法生成基于模式的强度。");
// Fallback or return? For now, let's just log and potentially result in a black target area.
// Alternatively, could fallback to the old Gaussian model here.
return;
}
@ -453,68 +451,6 @@ namespace ThreatSource.Guidance
}
Console.WriteLine($"目标基于ThermalPattern强度分布: 像素设置: {pixelsSet}, 最大强度: {maxSetIntensity:F6} W/sr");
// --- 结束: 使用 ThermalPattern 重构 ---
/* --- ---
// 计算目标自身应有的辐射强度(未被遮挡时)
double targetBaseIntensity = target.Properties.InfraredRadiationIntensity * transmittance / Math.Pow(distance, 1.8);
// 计算高斯分布参数
double sigmaX = pixelLength / 6.0; // 标准差设为长度/宽度的1/6覆盖约99.7%范围
double sigmaY = pixelWidth / 6.0;
// 避免除以零
sigmaX = Math.Max(sigmaX, 1e-6);
sigmaY = Math.Max(sigmaY, 1e-6);
int halfLength = pixelLength / 2;
int halfWidth = pixelWidth / 2;
int pixelsSet = 0;
double maxSetIntensity = 0;
// 设置强度分布
for (int dy = -halfWidth; dy <= halfWidth; dy++)
{
int y = centerY + dy;
if (y < 0 || y >= image.Height) continue;
for (int dx = -halfLength; dx <= halfLength; dx++)
{
int x = centerX + dx;
if (x < 0 || x >= image.Width) continue;
double finalIntensity;
double smokeIntensity = smokeIntensityLayer[y, x];
if (smokeIntensity >= 0) // 检查此像素是否被遮蔽烟幕覆盖
{
// 如果被覆盖,使用烟幕的强度
finalIntensity = smokeIntensity;
}
else
{
// 如果未被覆盖,计算目标的强度
// 计算归一化距离 (相对于高斯分布中心)
double normalizedX = dx / sigmaX;
double normalizedY = dy / sigmaY;
double r2 = normalizedX * normalizedX + normalizedY * normalizedY;
// 计算高斯分布下的目标强度
finalIntensity = targetBaseIntensity * Math.Exp(-r2 / 2.0);
// Console.WriteLine($"像素 ({x},{y}) 未被烟幕覆盖,使用目标强度: {finalIntensity:F6}");
}
// 设置最终计算出的强度到图像
image.SetIntensity(y, x, finalIntensity);
pixelsSet++;
maxSetIntensity = Math.Max(maxSetIntensity, finalIntensity);
}
}
Console.WriteLine($"目标/烟幕强度分布: 像素设置: {pixelsSet}, 最大强度: {maxSetIntensity:F6} W/sr");
*/
}
/// <summary>

View File

@ -53,13 +53,13 @@ namespace ThreatSource.Guidance
public bool IsInLockMode => currentMode == WorkMode.Lock;
/// <summary>
/// 上一次探测到的目标位置
/// 上一次探测到的目标位置 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录目标的历史位置
/// 用于计算目标速度
/// </remarks>
private Vector3D lastTargetPosition;
private Vector3D? lastTargetPosition { get; set; } // Changed to nullable Vector3D?
/// <summary>
/// 红外图像生成器
@ -123,10 +123,9 @@ namespace ThreatSource.Guidance
)
: base(id, maxAcceleration, proportionalNavigationCoefficient, simulationManager)
{
lastTargetPosition = Vector3D.Zero;
this.targetType = targetType;
config = guidanceConfig;
targetRecognizer = new InfraredTargetRecognizer(simulationManager);
targetRecognizer = new InfraredTargetRecognizer();
// 首先创建图像生成器
imageGenerator = new InfraredImageGenerator(
@ -192,8 +191,24 @@ namespace ThreatSource.Guidance
{
if (SimulationManager.GetEntityById(parameters.JammerId) is SmokeGrenade smokeGrenade)
{
// 计算烟幕衰减
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, lastTargetPosition, config.WaveLength);
// Check lastTargetPosition != null before using it
if (lastTargetPosition != null)
{
// 计算烟幕衰减 (这里存储衰减系数1.0表示无衰减)
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, lastTargetPosition, config.WaveLength);
Debug.WriteLine($"[烟幕干扰应用 IR] 视线透过率/衰减系数: {SmokeAttenuation:F3}");
}
else
{
// 目标尚未有效跟踪,无法计算特定视线衰减,假定无衰减
SmokeAttenuation = 1.0;
Debug.WriteLine("[烟幕干扰应用 IR] 目标位置无效,暂不计算烟幕衰减。");
}
}
else
{
// 未找到烟幕弹,假定无衰减
SmokeAttenuation = 1.0;
}
}
}
@ -211,6 +226,16 @@ namespace ThreatSource.Guidance
base.HandleJammingCleared(type);
// 干扰清除后的恢复逻辑
}
else if (type == JammingType.SmokeScreen)
{
SmokeAttenuation = 1.0; // Reset smoke attenuation
Debug.WriteLine("[烟幕干扰清除 IR]");
base.HandleJammingCleared(type);
}
else
{
base.HandleJammingCleared(type);
}
}
/// <summary>
@ -234,6 +259,13 @@ namespace ThreatSource.Guidance
base.Deactivate();
SimulationManager.UnsubscribeFromEvent<InfraredJammingEvent>(OnInfraredJamming);
SimulationManager.UnsubscribeFromEvent<SmokeScreenEvent>(OnSmokeScreen);
lastTargetPosition = null; // Add reset to null
HasTarget = false;
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
// Optionally reset timers?
// targetLostTimer = 0;
// lockConfirmationTimer = 0;
}
/// <summary>
@ -289,15 +321,28 @@ namespace ThreatSource.Guidance
base.Update(deltaTime, missilePosition, missileVelocity);
if (!IsHardJammed)
{
if (TryDetectAndIdentifyTarget(missilePosition, missileVelocity, deltaTime, out Vector3D tankPosition))
if (TryDetectAndIdentifyTarget(missilePosition, missileVelocity, deltaTime, out Vector3D currentTargetPosition))
{
targetLostTimer = 0; // 重置丢失计时器
//根据目标当前位置和上一次位置计算目标速度
Vector3D targetVelocity = (tankPosition - lastTargetPosition) / deltaTime;
lastTargetPosition = tankPosition;
Vector3D? currentTargetVelocity = null; // Initialize as nullable
// Check lastTargetPosition != null before calculating velocity
if(lastTargetPosition != null && deltaTime > 0)
{
// Remove cast, use lastTargetPosition directly
currentTargetVelocity = (currentTargetPosition - lastTargetPosition) / deltaTime;
}
// Update last known position (assign non-nullable to nullable)
lastTargetPosition = currentTargetPosition;
// 使用比例控制算法
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(ProportionalNavigationCoefficient, missilePosition, missileVelocity, tankPosition, targetVelocity);
// Use proportional navigation, provide default if velocity is null
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
missilePosition,
missileVelocity,
currentTargetPosition, // Non-nullable from out parameter
currentTargetVelocity ?? Vector3D.Zero // Provide default if null
);
// 限制最大加速度
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
{
@ -330,6 +375,7 @@ namespace ThreatSource.Guidance
else
{
GuidanceAcceleration = Vector3D.Zero;
HasGuidance = false;
}
}
@ -347,6 +393,7 @@ namespace ThreatSource.Guidance
currentMode = WorkMode.Search;
HasTarget = false;
targetLostTimer = 0; // 重置丢失计时器
lastTargetPosition = null; // Add reset to null
// 创建图像生成器
imageGenerator = new InfraredImageGenerator(
@ -529,8 +576,10 @@ namespace ThreatSource.Guidance
/// </remarks>
public override string GetStatus()
{
// Check for null before calling ToString()
string lastPosStr = lastTargetPosition != null ? lastTargetPosition.ToString() : "null";
return base.GetStatus() + $", GuidanceAcceleration: {GuidanceAcceleration}, " +
$"Target Position: {lastTargetPosition}";
$"Target Position: {lastPosStr}";
}
}
}

View File

@ -58,32 +58,16 @@ namespace ThreatSource.Guidance
/// </remarks>
public class InfraredTargetRecognizer
{
/// <summary>
/// 仿真管理器实例
/// </summary>
private readonly ISimulationManager simulationManager;
/// <summary>
/// 目标特征数据库
/// </summary>
private readonly Dictionary<EquipmentType, TargetFeature> targetFeatures;
/// <summary>
/// 识别阈值
/// </summary>
private const double RECOGNITION_THRESHOLD = 0.7;
/// <summary>
/// 背景辐射强度,用于阈值计算
/// </summary>
private const double BACKGROUND_INTENSITY = 0.0001;
/// <summary>
/// 初始化红外图像目标识别器
/// </summary>
public InfraredTargetRecognizer(ISimulationManager simulationManager)
public InfraredTargetRecognizer()
{
this.simulationManager = simulationManager;
// 初始化目标特征数据库 - 使用相对特征值
targetFeatures = new Dictionary<EquipmentType, TargetFeature>
{
@ -324,13 +308,10 @@ namespace ThreatSource.Guidance
double mean = sum / count;
// --- 修改为基于动态范围的阈值计算 ---
double thresholdFactor = 0.05; // 基于动态范围的因子 (初始值设为0.15, 从 0.05 改为 0.01)
double thresholdFactor = 0.05; // 基于动态范围的因子
double dynamicRange = maxIntensity - minIntensity;
double threshold = minIntensity + dynamicRange * thresholdFactor;
// 添加一个小的偏移量,以确保阈值严格大于最小强度,避免将所有背景像素包含进来
// double epsilon = 1e-9;
// threshold = Math.Max(threshold, minIntensity + epsilon); // 确保阈值至少比最小值大一点点
Console.WriteLine($"图像统计:");
Console.WriteLine($" 最大强度: {maxIntensity:F6}");
@ -339,7 +320,6 @@ namespace ThreatSource.Guidance
Console.WriteLine($" 动态范围: {dynamicRange:F6}");
Console.WriteLine($" 动态阈值因子: {thresholdFactor:P1}");
Console.WriteLine($" 最终阈值: {threshold:F6}");
// --- 结束修改 ---
return threshold;
}

View File

@ -20,22 +20,22 @@ namespace ThreatSource.Guidance
public class LaserBeamRiderGuidanceSystem : BasicGuidanceSystem
{
/// <summary>
/// 获取或设置激光源位置
/// 获取或设置激光源位置 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录激光发射源的三维位置
/// 用于计算导弹与激光束的相对位置
/// </remarks>
private Vector3D LaserSourcePosition { get; set; }
private Vector3D? LaserSourcePosition { get; set; }
/// <summary>
/// 获取或设置激光方向
/// 获取或设置激光方向 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录激光束的传播方向
/// 用于计算导弹与激光束的偏差
/// </remarks>
private Vector3D LaserDirection { get; set; }
private Vector3D? LaserDirection { get; set; }
/// <summary>
/// 获取或设置激光指示器激光功率,单位:瓦特
@ -46,36 +46,6 @@ namespace ThreatSource.Guidance
/// </remarks>
public double LaserPower { get; set; }
/// <summary>
/// 最小可探测功率,单位:瓦特
/// </summary>
/// <remarks>
/// 定义了探测器的灵敏度阈值
/// 低于此值时无法有效探测
/// 典型值为1e-10瓦
/// </remarks>
private const double MinDetectablePower = 1e-10;
/// <summary>
/// 探测器直径,单位:米
/// </summary>
/// <remarks>
/// 定义了探测器的物理尺寸
/// 影响系统的接收能力
/// 典型值为0.03米
/// </remarks>
private const double DetectorDiameter = 0.03;
/// <summary>
/// 控制场直径,单位:米
/// </summary>
/// <remarks>
/// 定义了有效制导范围
/// 超出此范围将失去制导
/// 典型值为6米
/// </remarks>
private const double ControlFieldDiameter = 6.0;
/// <summary>
/// 上一次的误差向量
/// </summary>
@ -175,8 +145,6 @@ namespace ThreatSource.Guidance
: base(id, maxAcceleration, guidanceCoefficient, simulationManager)
{
config = guidanceConfig;
LaserSourcePosition = Vector3D.Zero;
LaserDirection = Vector3D.Zero;
LaserPower = 0;
LaserIlluminationOn = false;
LastError = Vector3D.Zero;
@ -205,7 +173,7 @@ namespace ThreatSource.Guidance
base.Activate();
// 在这里订阅事件,确保只订阅一次
SimulationManager.SubscribeToEvent<LaserJammingEvent>(OnLaserJamming);
SimulationManager.SubscribeToEvent<LaserBeamStartEvent>(OnLaserBeamStart);
SimulationManager.SubscribeToEvent<LaserBeamEvent>(OnLaserBeamStart);
SimulationManager.SubscribeToEvent<LaserBeamStopEvent>(OnLaserBeamStop);
}
@ -217,7 +185,7 @@ namespace ThreatSource.Guidance
base.Deactivate();
// 取消订阅事件
SimulationManager.UnsubscribeFromEvent<LaserJammingEvent>(OnLaserJamming);
SimulationManager.UnsubscribeFromEvent<LaserBeamStartEvent>(OnLaserBeamStart);
SimulationManager.UnsubscribeFromEvent<LaserBeamEvent>(OnLaserBeamStart);
SimulationManager.UnsubscribeFromEvent<LaserBeamStopEvent>(OnLaserBeamStop);
}
@ -350,10 +318,11 @@ namespace ThreatSource.Guidance
/// </remarks>
public void DeactivateLaserBeam()
{
LaserSourcePosition = Vector3D.Zero;
LaserDirection = Vector3D.Zero;
LaserSourcePosition = null;
LaserDirection = null;
LaserPower = 0;
HasGuidance = false;
LaserIlluminationOn = false;
}
/// <summary>
@ -411,11 +380,16 @@ namespace ThreatSource.Guidance
/// </remarks>
private void UpdateGuidanceStatus()
{
// 计算导弹到激光束的最短距离
if (LaserSourcePosition == null)
{
HasGuidance = false;
Trace.WriteLine($"激光驾束引导系统: 失去引导, 原因: 激光源位置未知");
return;
}
Vector3D shortestDistanceVector = CalculateShortestDistanceToLaserBeam();
double shortestDistance = shortestDistanceVector.Magnitude();
// 检查导弹是否在控制场内
if (shortestDistance > config.ControlFieldDiameter / 2)
{
HasGuidance = false;
@ -425,11 +399,9 @@ namespace ThreatSource.Guidance
Debug.WriteLine($"激光驾束引导系统: 在控制场内, 距离: {shortestDistance}");
// 计算导弹到激光源的距离
Vector3D missileToSource = LaserSourcePosition - Position;
double distance = missileToSource.Magnitude();
// 计算接收到的激光功率
double receivedPower = CalculateReceivedLaserPower(distance);
Debug.WriteLine($"激光驾束引导系统: 接收到的激光功率: {receivedPower:E} W");
@ -498,53 +470,48 @@ namespace ThreatSource.Guidance
/// - 合成最终制导指令
/// - 应用低通滤波
/// </remarks>
protected void CalculateGuidanceAcceleration(double deltaTime)
protected void CalculateGuidanceAcceleration(double deltaTime)
{
// 计算导弹到激光束的最短距离
if (LaserDirection == null)
{
GuidanceAcceleration = Vector3D.Zero;
LastError = Vector3D.Zero;
IntegralError = Vector3D.Zero;
return;
}
Vector3D shortestDistanceVector = CalculateShortestDistanceToLaserBeam();
// PID控制
Vector3D error = shortestDistanceVector;
// 积分项
IntegralError += error * deltaTime;
// 微分项
Vector3D derivativeError = (error - LastError) / deltaTime;
// 计算PID输出
Vector3D pidOutput = error * config.ProportionalGain +
IntegralError * config.IntegralGain +
derivativeError * config.DerivativeGain;
// 非线性增益
double distance = shortestDistanceVector.Magnitude();
double nonLinearGain = Math.Tanh(distance / config.NonlinearGain);
// 计算横向加速度
Vector3D lateralAcceleration = pidOutput * nonLinearGain;
// 限制最大加速度
if (lateralAcceleration.Magnitude() > config.MaxGuidanceAcceleration)
{
lateralAcceleration = lateralAcceleration.Normalize() * config.MaxGuidanceAcceleration;
}
// 计算前向加速度
Vector3D forwardDirection = LaserDirection;
Vector3D currentDirection = Velocity.Normalize();
Vector3D rotationAxis = Vector3D.CrossProduct(currentDirection, forwardDirection);
double rotationAngle = Math.Acos(Vector3D.DotProduct(currentDirection, forwardDirection));
Vector3D forwardAcceleration = Vector3D.CrossProduct(rotationAxis, Velocity) * rotationAngle * ProportionalNavigationCoefficient;
// 合并横向和前向加速度
GuidanceAcceleration = lateralAcceleration + forwardAcceleration;
// 低通滤波
GuidanceAcceleration = GuidanceAcceleration * config.LowPassFilterCoefficient +
LastGuidanceAcceleration * (1 - config.LowPassFilterCoefficient);
// 更新上一次的误差和制导加速度
LastError = error;
LastGuidanceAcceleration = GuidanceAcceleration;
}
@ -572,7 +539,6 @@ namespace ThreatSource.Guidance
Debug.WriteLine($"激光驾束制导系统干扰状态 - IsJammed: {IsJammed}, 干扰功率: {evt.JammingPower}W, 抗性阈值: {config.JammingResistanceThreshold}W");
}
/// <summary>
/// 处理系统被干扰的事件
/// </summary>
@ -621,7 +587,10 @@ namespace ThreatSource.Guidance
/// </remarks>
public override string GetStatus()
{
return base.GetStatus() + $"激光指示器功率: {LaserPower:E} W," + $" 激光照射状态: {LaserIlluminationOn}";
string sourcePosStr = LaserSourcePosition != null ? LaserSourcePosition.ToString() : "null";
string directionStr = LaserDirection != null ? LaserDirection.ToString() : "null";
return base.GetStatus() + $", SourcePos: {sourcePosStr}, Direction: {directionStr}, " +
$"LaserPower: {LaserPower:E} W, Illumination: {LaserIlluminationOn}";
}
/// <summary>
@ -637,16 +606,14 @@ namespace ThreatSource.Guidance
/// </remarks>
private Vector3D CalculateShortestDistanceToLaserBeam()
{
// 计算导弹到激光源的向量
if (LaserSourcePosition == null || LaserDirection == null)
{
return Vector3D.Zero;
}
Vector3D missileToSource = LaserSourcePosition - Position;
// 计算导弹在激光方向上的投影长度
double projectionLength = Vector3D.DotProduct(missileToSource, LaserDirection);
// 计算激光束上最接近导弹的点
Vector3D closestPointOnBeam = LaserSourcePosition - LaserDirection * projectionLength;
// 计算导弹到激光束最近点的向量(即最短距离向量)
Vector3D shortestDistanceVector = closestPointOnBeam - Position;
return shortestDistanceVector;
@ -656,22 +623,22 @@ namespace ThreatSource.Guidance
/// 处理激光波束开始事件
/// </summary>
/// <param name="evt">激光波束开始事件</param>
private void OnLaserBeamStart(LaserBeamStartEvent evt)
private void OnLaserBeamStart(LaserBeamEvent evt)
{
if(evt?.LaserBeamRiderId != null && evt.LaserCodeConfig != null)
{
if(evt.LaserCodeConfig.IsCodeEnabled)
{
// 检查编码是否匹配
bool codeMatched = CheckLaserCode(evt.LaserCodeConfig);
if (!codeMatched)
{
// 发布编码不匹配事件
PublishCodeMismatchEvent(evt.LaserBeamRiderId, evt.LaserCodeConfig);
Debug.WriteLine("激光驾束制导系统接收到不匹配的激光编码,忽略信号");
HasGuidance = false;
LaserIlluminationOn = false;
LaserSourcePosition = null;
LaserDirection = null;
return;
}
}
@ -691,6 +658,8 @@ namespace ThreatSource.Guidance
{
LaserIlluminationOn = false;
HasGuidance = false;
LaserSourcePosition = null;
LaserDirection = null;
}
}
}

View File

@ -24,13 +24,13 @@ namespace ThreatSource.Guidance
private readonly LaserSemiActiveGuidanceConfig config;
/// <summary>
/// 获取或设置目标位置
/// 获取或设置目标位置 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录当前跟踪目标的三维位置
/// 用于制导计算
/// </remarks>
private Vector3D TargetPosition { get; set; }
private Vector3D? TargetPosition { get; set; }
/// <summary>
/// 获取或设置接收到的激光功率
@ -150,7 +150,6 @@ namespace ThreatSource.Guidance
{
config = guidanceConfig;
TargetPosition = Vector3D.Zero;
LaserIlluminationOn = false;
InternalLaserCodeConfig = laserCodeConfig;
@ -217,6 +216,7 @@ namespace ThreatSource.Guidance
// 取消订阅诱偏目标照射事件
SimulationManager.UnsubscribeFromEvent<LaserDecoyEvent>(OnLaserDecoy);
SimulationManager.UnsubscribeFromEvent<LaserDecoyStopEvent>(OnLaserDecoyStop);
TargetPosition = null;
}
/// <summary>
@ -261,6 +261,7 @@ namespace ThreatSource.Guidance
LaserIlluminationOn = false;
HasGuidance = false; // 禁用制导
PreviousGuidanceAcceleration = Vector3D.Zero; // 重置历史加速度
TargetPosition = null;
}
/// <summary>
@ -320,7 +321,7 @@ namespace ThreatSource.Guidance
if (IsHardJammed)
{
// 清除目标信息
TargetPosition = Vector3D.Zero;
TargetPosition = null;
LaserIlluminationOn = false;
// 记录干扰信息
Trace.WriteLine($"激光半主动制导系统被硬干扰 - 功率: {evt.JammingPower}W, 位置: {evt.JammingSourcePosition}, 方向: {evt.JammingDirection}");
@ -381,11 +382,24 @@ namespace ThreatSource.Guidance
{
if (SimulationManager.GetEntityById(parameters.JammerId) is SmokeGrenade smokeGrenade)
{
// 计算烟幕衰减
if (LaserIlluminationOn)
// 检查目标位置是否有效,并且激光照射是否开启 (烟幕只影响接收到的信号)
if (TargetPosition != null && LaserIlluminationOn)
{
// 计算烟幕衰减 (1.0 表示无衰减)
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, TargetPosition, config.LaserWavelength);
Console.WriteLine($"[烟幕干扰应用 Laser] 视线透过率/衰减系数: {SmokeAttenuation:F3}");
}
else
{
// 目标无效或激光未照射,暂不计算特定视线衰减
SmokeAttenuation = 1.0;
Console.WriteLine("[烟幕干扰应用 Laser] 目标位置无效或激光未照射,暂不计算烟幕衰减。");
}
}
else
{
// 未找到烟幕弹,假定无衰减
SmokeAttenuation = 1.0;
}
}
}
@ -623,7 +637,13 @@ namespace ThreatSource.Guidance
/// </remarks>
private Vector2D CalculateSpotOffset()
{
// 计算理想指向方向(从导弹到目标)
// Check TargetPosition != null before using it
if (TargetPosition == null)
{
return Vector2D.Zero;
}
// No cast needed, just use TargetPosition directly
Vector3D idealDirection = (TargetPosition - Position).Normalize();
// 计算当前导弹前向方向

View File

@ -1,4 +1,3 @@
using ThreatSource.Utils;
using ThreatSource.Equipment;
using System.Diagnostics;
@ -51,22 +50,22 @@ namespace ThreatSource.Guidance
private readonly Random random = new();
/// <summary>
/// 上一次探测到的目标位置
/// 上一次探测到的目标位置 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录目标的历史位置
/// 用于计算目标速度
/// </remarks>
private Vector3D lastTargetPosition;
private Vector3D? lastTargetPosition { get; set; }
/// <summary>
/// 上一次探测到的目标速度
/// 上一次探测到的目标速度 (使用可空类型)
/// </summary>
/// <remarks>
/// 记录目标的历史速度
/// 用于计算目标速度
/// </remarks>
private Vector3D lastTargetVelocity;
private Vector3D? lastTargetVelocity { get; set; }
/// <summary>
/// 目标丢失计时器
@ -134,6 +133,10 @@ namespace ThreatSource.Guidance
/// </summary>
private double maxScanRadius => config.FieldOfViewAngle * Math.PI / 180.0 / 2;
/// <summary>
/// 当前视线的烟幕透过率 (0.0 - 1.0)
/// </summary>
private double _currentSmokeTransmittance = 1.0;
/// <summary>
@ -161,10 +164,7 @@ namespace ThreatSource.Guidance
: base(id, maxAcceleration, guidanceCoefficient, simulationManager)
{
this.config = config;
lastTargetPosition = Vector3D.Zero;
lastTargetVelocity = Vector3D.Zero;
InitializeJamming(config.JammingResistanceThreshold, [JammingType.MillimeterWave]);
InitializeJamming(config.JammingResistanceThreshold, [JammingType.MillimeterWave, JammingType.SmokeScreen]);
SwitchToSearchMode(); // 初始化为搜索模式
}
@ -180,6 +180,8 @@ namespace ThreatSource.Guidance
lockConfirmationTimer = 0;
currentScanAngle = 0;
currentScanRadius = config.SearchBeamWidth * Math.PI / 720.0; // 从半个波束宽度的一半开始
lastTargetPosition = null;
lastTargetVelocity = null;
Trace.WriteLine($"切换到搜索模式,波束宽度: {config.SearchBeamWidth}度");
}
@ -214,7 +216,9 @@ namespace ThreatSource.Guidance
IsActive = true;
// 在这里订阅事件,确保只订阅一次
SimulationManager.SubscribeToEvent<MillimeterWaveJammingEvent>(OnMillimeterWaveJamming);
}
// 添加对烟幕事件的订阅
SimulationManager.SubscribeToEvent<SmokeScreenEvent>(OnSmokeScreen);
}
base.Activate();
SwitchToSearchMode();
}
@ -228,11 +232,15 @@ namespace ThreatSource.Guidance
{
IsActive = false;
SimulationManager.UnsubscribeFromEvent<MillimeterWaveJammingEvent>(OnMillimeterWaveJamming);
// 添加取消对烟幕事件的订阅
SimulationManager.UnsubscribeFromEvent<SmokeScreenEvent>(OnSmokeScreen);
}
base.Deactivate();
HasTarget = false;
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
lastTargetPosition = null;
lastTargetVelocity = null;
}
/// <summary>
@ -265,22 +273,77 @@ namespace ThreatSource.Guidance
Debug.WriteLine($"毫米波导引头系统干扰状态 - IsJammed: {IsJammed}, 干扰功率: {evt.JammingPower}W, 抗性阈值: {config.JammingResistanceThreshold}W");
}
/// <summary>
/// 处理烟幕事件,触发干扰应用
/// </summary>
/// <param name="evt">烟幕事件</param>
private void OnSmokeScreen(SmokeScreenEvent evt)
{
if (evt != null && evt.SmokeGrenadeId != null)
{
// 获取烟幕弹实例以创建干扰参数
if (SimulationManager.GetEntityById(evt.SmokeGrenadeId) is SmokeGrenade smokeGrenade)
{
var parameters = new JammingParameters
{
Type = JammingType.SmokeScreen,
JammerId = smokeGrenade.Id,
SourcePosition = smokeGrenade.Position,
Direction = smokeGrenade.Orientation.ToVector(),
Mode = JammingMode.Obscuration // 表明是遮蔽干扰
// JammableComponent可能需要其他烟幕属性如果需要则从smokeGrenade.CurrentParameters获取
};
// 应用干扰效果这将调用HandleJammingApplied
ApplyJamming(parameters);
}
}
}
/// <summary>
/// 处理系统被干扰的事件
/// </summary>
/// <param name="parameters">干扰参数</param>
protected override void HandleJammingApplied(JammingParameters parameters)
{
// 1. 让基类处理通用硬干扰逻辑 (如果不是SmokeScreen, 会设置 IsHardJammed 和 HasGuidance = false)
base.HandleJammingApplied(parameters);
// 2. 处理特定于毫米波的硬干扰响应
if (parameters.Type == JammingType.MillimeterWave)
{
Debug.WriteLine($"毫米波导引头系统受到毫米波干扰,功率:{parameters.Power}瓦特");
// 确保基类的处理逻辑被调用设置HasGuidance = false
base.HandleJammingApplied(parameters);
{
Debug.WriteLine($"毫米波导引头系统受到毫米波干扰,功率:{parameters.Power}瓦特");
// 在硬干扰下切换到搜索模式 (即使基类已设置HasGuidance=false, 仍可切换模式)
if (currentMode != WorkMode.Search)
{
SwitchToSearchMode();
}
}
// 在强干扰下切换到搜索模式
if (currentMode != WorkMode.Search)
// 3. 处理烟幕干扰 (计算透过率,不直接影响 IsHardJammed 或 HasGuidance)
else if (parameters.Type == JammingType.SmokeScreen)
{
SwitchToSearchMode();
if (SimulationManager.GetEntityById(parameters.JammerId) is SmokeGrenade smokeGrenade)
{
// Check lastTargetPosition != null before using it
if (lastTargetPosition != null)
{
// 计算波长
double wavelength = 3e8 / config.WaveFrequency;
// 计算视线透过率
_currentSmokeTransmittance = smokeGrenade.GetSmokeTransmittanceOnLine(Position, lastTargetPosition, wavelength);
Debug.WriteLine($"[烟幕干扰应用 MMW] 视线透过率: {_currentSmokeTransmittance:F3}");
}
else
{
// 目标尚未有效跟踪,无法计算特定视线衰减,假定无衰减
_currentSmokeTransmittance = 1.0;
Debug.WriteLine("[烟幕干扰应用 MMW] 目标位置无效,暂不计算烟幕衰减。");
}
}
else
{
// 未找到烟幕弹,假定无衰减
_currentSmokeTransmittance = 1.0;
}
}
}
@ -290,11 +353,21 @@ namespace ThreatSource.Guidance
/// <param name="type">被清除的干扰类型</param>
protected override void HandleJammingCleared(JammingType type)
{
if (type == JammingType.MillimeterWave)
// 1. 处理特定于烟幕的清除逻辑
if (type == JammingType.SmokeScreen)
{
Debug.WriteLine("毫米波导引头系统干扰被清除");
base.HandleJammingCleared(type);
_currentSmokeTransmittance = 1.0; // 重置透过率
Debug.WriteLine("[烟幕干扰清除 MMW]");
}
else if (type == JammingType.MillimeterWave)
{
Debug.WriteLine("毫米波导引头系统干扰被清除");
// 毫米波硬干扰清除的具体行为由基类处理
}
// 2. 最后调用基类方法处理通用清除逻辑
// 基类会检查是否所有干扰都已清除,并相应地更新 IsHardJammed 和 HasGuidance
base.HandleJammingCleared(type);
}
/// <summary>
@ -452,25 +525,28 @@ namespace ThreatSource.Guidance
base.Update(deltaTime, missilePosition, missileVelocity);
if (!IsJammed)
if (!IsHardJammed)
{
if (TryDetectAndTrackTarget(missilePosition, missileVelocity, deltaTime, out Vector3D targetPosition))
if (TryDetectAndTrackTarget(missilePosition, missileVelocity, deltaTime, out Vector3D currentTargetPosition))
{
targetLostTimer = 0;
Vector3D targetVelocity = (targetPosition - lastTargetPosition) / deltaTime;
lastTargetPosition = targetPosition;
lastTargetVelocity = targetVelocity;
Vector3D? currentTargetVelocity = null;
if (lastTargetPosition != null && deltaTime > 0)
{
currentTargetVelocity = (currentTargetPosition - lastTargetPosition) / deltaTime;
}
lastTargetPosition = currentTargetPosition;
lastTargetVelocity = currentTargetVelocity;
// 使用比例导引计算制导加速度
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
missilePosition,
missileVelocity,
targetPosition,
targetVelocity
currentTargetPosition,
currentTargetVelocity ?? Vector3D.Zero
);
// 限制最大加速度
if (GuidanceAcceleration.Magnitude() > MaxAcceleration)
{
GuidanceAcceleration = GuidanceAcceleration.Normalize() * MaxAcceleration;
@ -484,7 +560,6 @@ namespace ThreatSource.Guidance
if (currentMode != WorkMode.Search)
{
targetLostTimer += deltaTime;
// 如果目标丢失时间达到阈值,切换回搜索模式
if (targetLostTimer >= config.TargetLostTolerance)
{
HasGuidance = false;
@ -528,7 +603,6 @@ namespace ThreatSource.Guidance
bool foundTarget = false;
double currentSNR = double.MinValue;
// 如果被干扰直接返回false
if (isJammed)
{
Trace.WriteLine($"毫米波导引头受到干扰,干扰功率: {jammingPower:F2}W");
@ -545,13 +619,11 @@ namespace ThreatSource.Guidance
if (distance <= config.MaxDetectionRange)
{
// 计算信噪比
double snr = CalculateSNR(distance, target.Properties.RadarCrossSection);
Console.WriteLine($"信噪比: {snr:F2}dB");
if (currentMode == WorkMode.Search)
{
// 在搜索模式下进行波束判断
if (IsTargetInBeam(missileVelocity, toTarget) && snr >= config.RecognitionSNRThreshold)
{
targetPosition = target.Position;
@ -562,11 +634,10 @@ namespace ThreatSource.Guidance
}
else if (currentMode == WorkMode.Track || currentMode == WorkMode.Lock)
{
// 波束内判断
if (IsTargetInBeam(missileVelocity, toTarget) &&
lastTargetPosition != null &&
Vector3D.Distance(target.Position, lastTargetPosition) < 100.0)
{
// 在跟踪模式下SNR至少要达到识别阈值
double requiredSNR = (currentMode == WorkMode.Track) ?
config.RecognitionSNRThreshold : config.LockSNRThreshold;
@ -588,9 +659,7 @@ namespace ThreatSource.Guidance
}
}
// 根据当前模式和探测结果更新系统状态
UpdateSystemState(foundTarget, currentSNR, deltaTime);
HasTarget = foundTarget;
return foundTarget;
}
@ -657,10 +726,13 @@ namespace ThreatSource.Guidance
/// </remarks>
public override string GetStatus()
{
string lastPosStr = lastTargetPosition != null ? lastTargetPosition.ToString() : "null";
string lastVelStr = lastTargetVelocity != null ? lastTargetVelocity.ToString() : "null";
return base.GetStatus() +
$"\n当前模式: {currentMode}" +
$"\n目标位置: {lastTargetPosition}" +
$"\n目标速度: {lastTargetVelocity}" +
$"\n最后目标位置: {lastPosStr}" +
$"\n最后目标速度: {lastVelStr}" +
$"\n是否有目标: {HasTarget}";
}
@ -713,6 +785,9 @@ namespace ThreatSource.Guidance
signalPower *= atmosphericTransmittance;
// 应用当前生效的烟幕衰减 (通过存储的透过率)
signalPower *= _currentSmokeTransmittance;
// 计算噪声功率
double noisePower = k * T0 * bandwidth * noiseFigure;

View File

@ -182,6 +182,7 @@ namespace ThreatSource.Indicator
Vector3D targetPosition = target.Position;
LaserDirection = (targetPosition - Position).Normalize();
Debug.WriteLine($"激光驾束仪 {Id} 更新激光指向: {LaserDirection}");
PublishLaserBeamEvent();
}
}
}
@ -287,7 +288,7 @@ namespace ThreatSource.Indicator
{
LaserDirection = (target.Position - Position).Normalize();
IsBeamOn = true;
PublishLaserBeamStartEvent();
PublishLaserBeamEvent();
}
}
}
@ -318,9 +319,9 @@ namespace ThreatSource.Indicator
/// 通知仿真系统激光波束开始工作
/// 包含制导器ID等关键信息
/// </remarks>
private void PublishLaserBeamStartEvent()
private void PublishLaserBeamEvent()
{
PublishEvent(new LaserBeamStartEvent
PublishEvent(new LaserBeamEvent
{
LaserBeamRiderId = Id
});

View File

@ -96,26 +96,7 @@ namespace ThreatSource.Missile
/// - 更新波束参数
/// - 开始波束跟踪
/// </remarks>
private void OnLaserBeamStart(LaserBeamStartEvent evt)
{
if (evt?.LaserBeamRiderId != null)
{
LaserBeamRider laserBeamRider = SimulationManager.GetEntityById(evt.LaserBeamRiderId) as LaserBeamRider ?? throw new Exception("激光驾束仪不存在");
guidanceSystem?.UpdateLaserBeamRider(laserBeamRider.Position, laserBeamRider.LaserDirection, laserBeamRider.LaserPower);
}
}
/// <summary>
/// 处理激光束更新事件
/// </summary>
/// <param name="evt">激光束更新事件数据</param>
/// <remarks>
/// 处理过程:
/// - 验证激光驾束仪
/// - 更新波束位置和方向
/// - 更新制导参数
/// </remarks>
private void OnLaserBeamUpdate(LaserBeamUpdateEvent evt)
private void OnLaserBeamEvent(LaserBeamEvent evt)
{
if (evt?.LaserBeamRiderId != null)
{
@ -235,9 +216,8 @@ namespace ThreatSource.Missile
// 激活制导系统
guidanceSystem.Activate();
// 订阅激光波束事件
SimulationManager.SubscribeToEvent<LaserBeamStartEvent>(OnLaserBeamStart);
SimulationManager.SubscribeToEvent<LaserBeamEvent>(OnLaserBeamEvent);
SimulationManager.SubscribeToEvent<LaserBeamStopEvent>(OnLaserBeamStop);
SimulationManager.SubscribeToEvent<LaserBeamUpdateEvent>(OnLaserBeamUpdate);
}
/// <summary>
@ -255,9 +235,8 @@ namespace ThreatSource.Missile
// 停用制导系统
guidanceSystem.Deactivate();
// 取消订阅波束事件
SimulationManager.UnsubscribeFromEvent<LaserBeamStartEvent>(OnLaserBeamStart);
SimulationManager.UnsubscribeFromEvent<LaserBeamEvent>(OnLaserBeamEvent);
SimulationManager.UnsubscribeFromEvent<LaserBeamStopEvent>(OnLaserBeamStop);
SimulationManager.UnsubscribeFromEvent<LaserBeamUpdateEvent>(OnLaserBeamUpdate);
}
/// <summary>

View File

@ -242,42 +242,7 @@ namespace ThreatSource.Simulation
/// 用于通知系统激光波束开始照射
/// 触发时机:激光驾束仪开始工作时
/// </remarks>
public class LaserBeamStartEvent : SimulationEvent
{
/// <summary>
/// 获取或设置激光驾束仪的ID
/// </summary>
/// <remarks>
/// 标识发出激光波束的驾束仪设备
/// </remarks>
public string? LaserBeamRiderId { get; set; }
/// <summary>
/// 获取或设置波束功率
/// </summary>
/// <remarks>
/// 激光波束的发射功率
/// </remarks>
public double BeamPower { get; set; }
/// <summary>
/// 获取或设置激光编码配置
/// </summary>
/// <remarks>
/// 包含激光信号的编码信息
/// 用于抗干扰和安全识别
/// </remarks>
public LaserCodeConfig? LaserCodeConfig { get; set; }
}
/// <summary>
/// 激光波束更新事件,表示激光波束状态的更新
/// </summary>
/// <remarks>
/// 用于实时更新激光波束的状态
/// 在波束照射过程中周期性触发
/// </remarks>
public class LaserBeamUpdateEvent : SimulationEvent
public class LaserBeamEvent : SimulationEvent
{
/// <summary>
/// 获取或设置激光驾束仪的ID

View File

@ -1 +1 @@
0.2.11
0.2.12

View File

@ -250,7 +250,7 @@ namespace ThreatSource.Tools.MissileSimulation
{
var motionParameters = new MotionParameters
{
Position = new Vector3D(2000, 1, 50),
Position = new Vector3D(2000, 1, 30),
Orientation = new Orientation(Math.PI, 0.02, 0),
InitialSpeed = 300
};
@ -268,7 +268,7 @@ namespace ThreatSource.Tools.MissileSimulation
{
var motionParameters = new MotionParameters
{
Position = new Vector3D(3000, 1, 20),
Position = new Vector3D(2000, 1, 20),
Orientation = new Orientation(Math.PI, 0.01, 0),
InitialSpeed = 300
};
@ -383,16 +383,6 @@ namespace ThreatSource.Tools.MissileSimulation
indicators["LD_1"].Activate();
}
Console.WriteLine($"干扰器数量 {jammers.Count}");
// if (jammers.ContainsKey("LDY_1"))
// {
// jammers["LDY_1"].Activate();
// Console.WriteLine($"激活激光诱偏目标 {jammers["LDY_1"].Id}");
// }
if (jammers.ContainsKey("SG_1"))
{
//jammers["SG_1"].Activate();
Console.WriteLine($"激活烟幕弹 {jammers["SG_1"].Id}");
}
break;
case "LBRM_1": // 激光驾束制导导弹
if (indicators.ContainsKey("LBR_1"))