完善各组件的干扰处理

This commit is contained in:
Tian jianyong 2025-05-09 00:44:31 +08:00
parent fedd54d999
commit c0c8c4b3a6
33 changed files with 635 additions and 488 deletions

View File

@ -16,7 +16,13 @@
- 多种发射弹道模式:低平弹道、高抛弹道、俯冲弹道
- 双模、多模制导
- Orientation 坐标系的调整(前向方向从 X 轴改为 Z 轴)
- 末敏弹各探测组件的干扰处理,增加毫米波补偿干扰
## [0.2.16] - 2025-05-09
- 支持每个组件同时处理多种干扰
- 把各组件的烟幕透过率计算移到 update 方法中
- 完善了指示器的烟幕遮挡计算逻辑
- 完善了激光半主动导弹的落点计算逻辑
- 完善了配置文件格式
## [0.2.15] - 2025-05-07
- 增加了毫米波补偿干扰器

View File

@ -6,7 +6,7 @@
"type": "LaserDecoy",
"LaserDecoyConfig": {
"Mode": "Deception",
"Power": 25.0,
"Power": 200.0,
"Wavelength": 1.06,
"DivergenceAngle": 0.001,
"ReflectiveArea": 1.2,

View File

@ -0,0 +1,20 @@
{
"name": {
"zh": "周边烟幕弹",
"en": "Surround Smoke Grenade"
},
"type": "SmokeGrenade",
"SmokeGrenadeConfig": {
"smokeType": "Wall",
"IsObscuring": true,
"RadiationTemperature": 173.15,
"concentration": 0.2,
"duration": 60.0,
"wallWidth": 50.0,
"wallHeight": 10.0,
"cloudDiameter": 0.0,
"thickness": 5.0,
"formationDelay": 2.0,
"expansionRate": 5.0
}
}

View File

@ -5,6 +5,7 @@
},
"type": "InfraredCommandGuidance",
"properties": {
"type": "InfraredCommandGuidance",
"maxSpeed": 300.0,
"maxFlightTime": 60.0,
"maxFlightDistance": 5000.0,

View File

@ -5,6 +5,7 @@
},
"type": "InfraredImagingTerminalGuidance",
"properties": {
"type": "InfraredImagingTerminalGuidance",
"maxSpeed": 250.0,
"maxFlightTime": 60.0,
"maxFlightDistance": 5000.0,

View File

@ -5,6 +5,7 @@
},
"type": "LaserBeamRiderGuidance",
"properties": {
"type": "LaserBeamRiderGuidance",
"maxSpeed": 300.0,
"maxFlightTime": 60.0,
"maxFlightDistance": 5000.0,

View File

@ -5,6 +5,7 @@
},
"type": "LaserBeamRiderGuidance",
"properties": {
"type": "LaserBeamRiderGuidance",
"maxSpeed": 300.0,
"maxFlightTime": 70.0,
"maxFlightDistance": 5500.0,

View File

@ -5,6 +5,7 @@
},
"type": "LaserBeamRiderGuidance",
"properties": {
"type": "LaserBeamRiderGuidance",
"maxSpeed": 278.0,
"maxFlightTime": 65.0,
"maxFlightDistance": 4500.0,

View File

@ -5,6 +5,7 @@
},
"type": "LaserSemiActiveGuidance",
"properties": {
"type": "LaserSemiActiveGuidance",
"maxSpeed": 800.0,
"maxFlightTime": 60.0,
"maxFlightDistance": 4000.0,

View File

@ -5,6 +5,7 @@
},
"type": "MillimeterWaveTerminalGuidance",
"properties": {
"type": "MillimeterWaveTerminalGuidance",
"maxSpeed": 250.0,
"maxFlightTime": 60.0,
"maxFlightDistance": 8000.0,

View File

@ -5,6 +5,7 @@
},
"type": "TerminalSensitiveMissile",
"properties": {
"type": "TerminalSensitiveMissile",
"maxSpeed": 1000.0,
"maxFlightTime": 100.0,
"maxFlightDistance": 5000.0,

View File

@ -4,31 +4,32 @@
"en": "Armored Personnel Carrier-001"
},
"type": "APC",
"mass": 25000.0,
"length": 7.0,
"width": 3.2,
"height": 2.8,
"maxSpeed": 80.0,
"armorThickness": 400.0,
"radarCrossSection": 12.0,
"infraredRadiationIntensity": 2000.0,
"ultravioletRadiationIntensity": 12.0,
"millimeterWaveRadiationIntensity": 8.0,
"millimeterWaveRadiationTemperature": 350.0,
"laserReflectivity": 0.25,
"thermalPattern": {
"description": "3x3 matrix representing side view temperature distribution (°C)",
"static": [
[35, 40, 65],
[30, 35, 70],
[40, 40, 45]
],
"moving": [
[40, 45, 70],
[35, 40, 75],
[50, 50, 55]
]
"properties": {
"type": "APC",
"mass": 25000.0,
"length": 7.0,
"width": 3.2,
"height": 2.8,
"maxSpeed": 80.0,
"armorThickness": 400.0,
"radarCrossSection": 12.0,
"infraredRadiationIntensity": 2000.0,
"ultravioletRadiationIntensity": 12.0,
"millimeterWaveRadiationIntensity": 8.0,
"millimeterWaveRadiationTemperature": 350.0,
"laserReflectivity": 0.25,
"thermalPattern": {
"description": "3x3 matrix representing side view temperature distribution (°C)",
"static": [
[35, 40, 65],
[30, 35, 70],
[40, 40, 45]
],
"moving": [
[40, 45, 70],
[35, 40, 75],
[50, 50, 55]
]
}
}
}

View File

@ -4,31 +4,32 @@
"en": "Attack Helicopter-001"
},
"type": "Helicopter",
"mass": 10000.0,
"length": 17.0,
"width": 3.0,
"height": 4.5,
"maxSpeed": 280.0,
"armorThickness": 150.0,
"radarCrossSection": 8.0,
"infraredRadiationIntensity": 3000.0,
"ultravioletRadiationIntensity": 20.0,
"millimeterWaveRadiationIntensity": 6.0,
"millimeterWaveRadiationTemperature": 450.0,
"laserReflectivity": 0.2,
"thermalPattern": {
"description": "3x3 matrix representing side view temperature distribution (°C)",
"static": [
[85, 110, 80],
[35, 45, 40],
[30, 35, 30]
],
"moving": [
[90, 115, 85],
[40, 50, 45],
[35, 40, 35]
]
"properties": {
"type": "Helicopter",
"mass": 10000.0,
"length": 17.0,
"width": 3.0,
"height": 4.5,
"maxSpeed": 280.0,
"armorThickness": 150.0,
"radarCrossSection": 8.0,
"infraredRadiationIntensity": 3000.0,
"ultravioletRadiationIntensity": 20.0,
"millimeterWaveRadiationIntensity": 6.0,
"millimeterWaveRadiationTemperature": 450.0,
"laserReflectivity": 0.2,
"thermalPattern": {
"description": "3x3 matrix representing side view temperature distribution (°C)",
"static": [
[85, 110, 80],
[35, 45, 40],
[30, 35, 30]
],
"moving": [
[90, 115, 85],
[40, 50, 45],
[35, 40, 35]
]
}
}
}

View File

@ -5,6 +5,7 @@
},
"type": "Tank",
"properties": {
"type": "Tank",
"mass": 50000.0,
"length": 10.0,
"width": 3.5,

View File

@ -221,6 +221,7 @@ namespace ThreatSource.Guidance
// 添加制导系统特有属性
statusInfo.ExtendedProperties["HasGuidance"] = HasGuidance;
statusInfo.ExtendedProperties["IsJammed"] = IsJammed;
statusInfo.ExtendedProperties["IsBlockingJammed"] = IsBlockingJammed;
statusInfo.ExtendedProperties["GuidanceAcceleration"] = GuidanceAcceleration;
statusInfo.ExtendedProperties["MaxAcceleration"] = MaxAcceleration;
statusInfo.ExtendedProperties["ProportionalNavigationCoefficient"] = ProportionalNavigationCoefficient;
@ -366,7 +367,7 @@ namespace ThreatSource.Guidance
/// </remarks>
protected void InitializeJamming(double jammingResistanceThreshold, IEnumerable<JammingType> supportedTypes, IEnumerable<JammingType> supportedBlockingTypes)
{
_jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedTypes);
_jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedBlockingTypes);
foreach (var type in supportedTypes)
{
_jammingComponent.AddSupportedJammingType(type);

View File

@ -102,12 +102,6 @@ namespace ThreatSource.Guidance
/// </summary>
private double lockConfirmationTimer = 0;
/// <summary>
/// 烟幕衰减
/// </summary>
private double SmokeAttenuation { get; set; } = 1.0;
/// <summary>
/// 初始化红外成像制导系统的新实例
/// </summary>
@ -164,36 +158,14 @@ namespace ThreatSource.Guidance
{
Debug.WriteLine($"红外引导系统受到红外干扰,功率:{parameters.Power}瓦特");
// 在强干扰下切换到搜索模式
if (currentMode != WorkMode.Search)
{
SwitchToSearchMode();
}
// 调用基类处理硬干扰状态
base.HandleJammingApplied(parameters);
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
if (SimulationManager.GetEntityById(parameters.JammerId) is SmokeGrenade smokeGrenade)
{
if (lastTargetPosition != null)
{
// 计算烟幕衰减 (这里存储衰减系数1.0表示无衰减)
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, lastTargetPosition, config.Wavelength);
Console.WriteLine($"[烟幕干扰IR] 视线透过率: {SmokeAttenuation:F3}");
}
else
{
// 目标尚未有效跟踪,计算视线方向衰减
var intersection = MotionAlgorithm.CalculateIntersectionWithGround(Position, Orientation.ToVector(), 0);
Console.WriteLine($"[###烟幕干扰IR] 计算视线方向衰减,交点: {intersection}");
if (intersection != null)
{
SmokeAttenuation = smokeGrenade.GetSmokeTransmittanceOnLine(Position, intersection, config.Wavelength);
Console.WriteLine($"[烟幕干扰IR] 没有发现目标位置,只计算视线透过率: {SmokeAttenuation:F3}");
}
}
}
Debug.WriteLine($"[IR IMAGING] 烟幕干扰事件 {parameters.JammerId} 应用。", "Jamming");
}
}
@ -212,8 +184,7 @@ namespace ThreatSource.Guidance
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
SmokeAttenuation = 1.0;
Debug.WriteLine("[烟幕干扰清除 IR]");
Debug.WriteLine($"[IR IMAGING] 烟幕干扰事件 {parameters.JammerId} 清除。", "Jamming");
}
}
@ -235,9 +206,6 @@ namespace ThreatSource.Guidance
HasTarget = false;
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
// Optionally reset timers?
// targetLostTimer = 0;
// lockConfirmationTimer = 0;
}
/// <summary>
@ -260,17 +228,13 @@ namespace ThreatSource.Guidance
{
targetLostTimer = 0; // 重置丢失计时器
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;
// Use proportional navigation, provide default if velocity is null
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
Position,
@ -418,20 +382,21 @@ namespace ThreatSource.Guidance
{
// 检查视线角条件
double angle = Math.Acos(Vector3D.DotProduct(toTarget.Normalize(), missileVelocity.Normalize()));
Console.WriteLine($"[红外成像制导系统] 目标 {target.Id} 视线角: {angle * 180 / Math.PI} 度, 视场角范围: {currentFov * 180 / Math.PI} 度");
if (angle <= currentFov / 2)
{
// 在生成红外图像前,检查目标是否被烟幕遮挡
bool isTargetObscuredBySmoke = CheckIfTargetObscuredBySmoke(missilePosition, target);
if (isTargetObscuredBySmoke)
// 在生成红外图像前,检查目标是否被烟幕几何遮挡
bool isTargetGeometricallyObscured = CheckIfTargetObscuredBySmoke(missilePosition, target);
if (isTargetGeometricallyObscured)
{
Console.WriteLine($"[红外成像制导系统] 目标 {target.Id} 被烟幕完全遮挡,跳过图像生成");
Console.WriteLine($"[红外成像制导系统] 目标 {target.Id} 被烟幕几何遮挡,跳过图像生成");
continue; // 如果目标被完全遮挡,跳过此目标
}
// 实时计算当前目标到导弹之间的烟幕透过率
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.Position);
// 生成红外图像
Console.WriteLine($"[红外成像制导系统] 生成红外图像,目标类型: {target.GetType().Name}, 烟幕衰减: {SmokeAttenuation}");
var image = imageGenerator.GenerateImage(target, missilePosition, SmokeAttenuation, SimulationManager);
var image = imageGenerator.GenerateImage(target, missilePosition, liveSmokeTransmittance, SimulationManager);
switch (currentMode)
{
@ -571,6 +536,39 @@ namespace ThreatSource.Guidance
return false; // 没有烟幕能有效遮挡目标
}
/// <summary>
/// 计算给定观察点和目标点之间的总烟幕透过率
/// </summary>
/// <param name="observerPosition">观察者位置</param>
/// <param name="targetEndPosition">目标位置</param>
/// <returns>总透过率 (0.0 到 1.0)</returns>
private double CalculateLiveSmokeTransmittance(Vector3D observerPosition, Vector3D targetEndPosition)
{
double totalTransmittance = 1.0;
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
.Where(sg => sg.IsActive && sg.config != null && sg.IsJamming) // 确保烟幕弹已激活并正在干扰
.ToList();
if (!activeSmokeGrenades.Any())
{
return 1.0; // 没有活动的、正在干扰的烟幕,无衰减
}
foreach (var smokeGrenade in activeSmokeGrenades)
{
// 调用 SmokeGrenade 实例的方法来计算其对视线的透过率
double transmittanceForThisSmoke = smokeGrenade.GetSmokeTransmittanceOnLine(observerPosition, targetEndPosition, config.Wavelength);
totalTransmittance *= transmittanceForThisSmoke; // 叠加衰减效应(透过率相乘)
// 如果透过率已经很低,可以提前退出以优化
if (totalTransmittance < 0.001)
{
return 0.0;
}
}
return Math.Max(0.0, totalTransmittance); //确保不为负
}
/// <summary>
/// 获取红外图像制导系统的详细状态信息
/// </summary>

View File

@ -349,7 +349,7 @@ namespace ThreatSource.Guidance
public override void Update(double deltaTime)
{
base.Update(deltaTime);
if (!IsJammed)
if (!IsBlockingJammed)
{
if (LaserIlluminationOn)
{

View File

@ -6,6 +6,7 @@ using ThreatSource.Jammer;
using System.Diagnostics;
using AirTransmission;
using System.Transactions;
using System.Linq;
namespace ThreatSource.Guidance
{
@ -123,11 +124,6 @@ namespace ThreatSource.Guidance
/// </remarks>
private const double AccelerationSmoothingFactor = 0.5;
/// <summary>
/// 烟幕衰减
/// </summary>
private double SmokeAttenuation { get; set; } = 1.0;
/// <summary>
/// 激光目标列表,包括真实目标和诱偏目标
/// </summary>
@ -299,19 +295,16 @@ namespace ThreatSource.Guidance
if (parameters.Type == JammingType.Laser)
{
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 受到激光干扰。", "Jamming");
// 基类已将 HasGuidance 设为 false
TargetPosition = null; // 丢失目标位置
LaserIlluminationOn = false; // 无法确认照射状态
// 不需要再次设置 HasGuidance = false
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
// 计算并应用烟幕衰减
SmokeAttenuation = CalculateSmokeAttenuation(parameters);
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 烟幕干扰应用,衰减因子: {SmokeAttenuation:P2}", "Jamming");
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 烟幕干扰应用JammerId: {parameters.JammerId}", "Jamming");
}
else if (parameters.Type == JammingType.LaserDecoy)
{
Console.WriteLine($"[LASER_SEMI_ACTIVE] 受到激光诱偏干扰。JammerId: {parameters.JammerId}, SourceId: {parameters.SourceId}");
if (parameters.JammerId != null && parameters.SourceId != null)
{
LaserDecoy decoyTarget = SimulationManager.GetEntityById(parameters.JammerId) as LaserDecoy ?? throw new Exception("诱偏目标不存在");
@ -320,6 +313,7 @@ namespace ThreatSource.Guidance
if (!laserTargets.Any(t => t.Target.Id == decoyTarget.Id))
{
laserTargets.Add((decoyTarget, decoyTarget.Position, decoySource));
Console.WriteLine($"[LASER_SEMI_ACTIVE] 诱偏目标添加到激光目标列表。当前激光目标数量: {laserTargets.Count}");
}
}
}
@ -336,13 +330,10 @@ namespace ThreatSource.Guidance
if (parameters.Type == JammingType.Laser)
{
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 激光干扰已清除。", "Jamming");
// 不需要设置 HasGuidance = true, 基类可能已处理
// 激光照射状态 LaserIlluminationOn 会在收到下一个有效 Update 事件时恢复
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
SmokeAttenuation = 1.0; // 重置烟幕衰减
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 烟幕干扰已清除。", "Jamming");
Debug.WriteLine($"[LASER_SEMI_ACTIVE] 烟幕干扰已清除JammerId: {parameters.JammerId}", "Jamming");
}
else if (parameters.Type == JammingType.LaserDecoy)
{
@ -367,7 +358,7 @@ namespace ThreatSource.Guidance
}
// 对激光干扰添加波长检查
if (parameters.Type == JammingType.Laser)
if (parameters.Type == JammingType.Laser || parameters.Type == JammingType.LaserDecoy)
{
// 1. 检查波长
if (Math.Abs((parameters.Wavelength ?? 0) - config.Wavelength) > 1e-6)
@ -375,9 +366,6 @@ namespace ThreatSource.Guidance
Debug.WriteLine($"[LASER_SEMI_ACTIVE] {Id} 忽略激光干扰:波长 {parameters.Wavelength}um 与期望波长 {config.Wavelength}um 不匹配。", "Jamming");
return false;
}
// 2. 移除干扰事件中的编码检查: JammingParameters 不包含 LaserCode.
// 编码检查已移至 OnLaserIlluminationUpdate
}
// 如果是支持的非激光干扰 (例如烟幕) 或通过了激光检查,则返回 true
@ -511,60 +499,49 @@ namespace ThreatSource.Guidance
/// <returns>接收到的激光功率,单位:瓦特</returns>
private double CalculateReceivedPower(Vector3D sourcePos, Vector3D targetPos, double laserPower, double laserDivergenceAngle)
{
double distanceDesignatorToTarget = (sourcePos - targetPos).Magnitude();
double distanceMissileToTarget = (Position - targetPos).Magnitude();
// 计算大气透过率 (1.使用从激光源到目标的单程透过率2.使用从目标到导弹的单程透过率)
// 如果当前天气为null则认为大气透过率为1.0
double atmosphericTransmittanceToTarget = 1.0;
double atmosphericTransmittanceToMissile = 1.0;
// 考虑烟幕衰减,计算大气透过率
if(SimulationManager.CurrentWeather != null)
// --- 路径1: 指示器 (source) 到目标 (target) ---
double distanceSourceToTarget = (sourcePos - targetPos).Magnitude();
double smokeTransmittance_S2T = CalculateLiveSmokeTransmittanceForPath(sourcePos, targetPos);
double atmTransmittance_S2T = 1.0;
if (SimulationManager.CurrentWeather != null)
{
atmosphericTransmittanceToTarget = SmokeAttenuation * AtmosphereDllWrapper.CalculateTransmittance(
distanceDesignatorToTarget,
RadiationType.Laser,
config.Wavelength,
SimulationManager.CurrentWeather);
atmosphericTransmittanceToMissile = SmokeAttenuation * AtmosphereDllWrapper.CalculateTransmittance(
distanceMissileToTarget,
RadiationType.Laser,
config.Wavelength,
SimulationManager.CurrentWeather);
atmTransmittance_S2T = AtmosphereDllWrapper.CalculateTransmittance(
distanceSourceToTarget, RadiationType.Laser, config.Wavelength, SimulationManager.CurrentWeather);
}
double totalTransmittance_S2T = smokeTransmittance_S2T * atmTransmittance_S2T;
// 计算目标处的光斑面积
double spotAreaAtTarget = Math.PI * Math.Pow(distanceDesignatorToTarget * Math.Tan(laserDivergenceAngle), 2);
// 计算目标处的激光功率密度,考虑大气衰减和发射系统透过率
double powerDensityAtTarget = laserPower * atmosphericTransmittanceToTarget * config.TransmitterEfficiency / spotAreaAtTarget;
// 计算从目标反射的总功率
double reflectedPower = powerDensityAtTarget * config.TargetReflectiveArea * config.ReflectionCoefficient;
// 计算反射光在导弹处的扩散面积(假设漫反射)
double reflectedSpotArea = 2 * Math.PI * Math.Pow(distanceMissileToTarget, 2);
// 计算导弹接收到的功率,考虑大气衰减和接收系统透过率
double receivedPower = reflectedPower * atmosphericTransmittanceToMissile * config.ReceiverEfficiency / reflectedSpotArea;
// 功率密度在目标表面
double spotAreaAtTarget = Math.PI * Math.Pow(distanceSourceToTarget * Math.Tan(laserDivergenceAngle), 2);
if (spotAreaAtTarget < 1e-9) spotAreaAtTarget = 1e-9; // 防止除零
double powerDensityOnTargetSurface = laserPower * config.TransmitterEfficiency * totalTransmittance_S2T / spotAreaAtTarget;
// 计算镜头接收到的功率比例
// 目标反射的总功率
double totalPowerReflectedByTarget = powerDensityOnTargetSurface * config.TargetReflectiveArea * config.ReflectionCoefficient;
// --- 路径2: 目标 (target) 到导弹 (missile) ---
double distanceTargetToMissile = (targetPos - Position).Magnitude();
if (distanceTargetToMissile < 1e-9) distanceTargetToMissile = 1e-9; // 防止除零
double smokeTransmittance_T2M = CalculateLiveSmokeTransmittanceForPath(targetPos, Position);
double atmTransmittance_T2M = 1.0;
if (SimulationManager.CurrentWeather != null)
{
atmTransmittance_T2M = AtmosphereDllWrapper.CalculateTransmittance(
distanceTargetToMissile, RadiationType.Laser, config.Wavelength, SimulationManager.CurrentWeather);
}
double totalTransmittance_T2M = smokeTransmittance_T2M * atmTransmittance_T2M;
// 导弹接收孔径处的功率密度 (来自目标反射,假设朗伯体)
// 反射功率在2*PI立体角内分布朗伯反射体
// 功率密度 = 总反射功率 / (2 * PI * R^2)
// 在此步骤应用路径2的透过率因为它影响从目标到导弹的信号强度
double powerDensityAtMissileAperture = totalPowerReflectedByTarget * totalTransmittance_T2M / (2 * Math.PI * Math.Pow(distanceTargetToMissile, 2));
// 导弹最终接收到的功率
double lensArea = Math.PI * Math.Pow(config.LensDiameter / 2, 2);
double illuminatedArea = Math.PI * Math.Pow(distanceMissileToTarget * Math.Tan(config.FieldOfViewAngleInRadians / 2), 2);
double powerRatio = Math.Min(1, lensArea / illuminatedArea);
// 计算聚焦后的功率密度增加
double sensorArea = Math.PI * Math.Pow(config.SensorDiameter / 2, 2);
double focusedArea = Math.PI * Math.Pow(config.FocusedSpotDiameter / 2, 2);
double focusingFactor = sensorArea / focusedArea;
// 计算最终接收到的功率
double finalReceivedPower = receivedPower * powerRatio * focusingFactor;
double finalReceivedPower = powerDensityAtMissileAperture * lensArea * config.ReceiverEfficiency;
Debug.WriteLine($"激光功率计算: 源->目标距离={distanceDesignatorToTarget:F1}m (透过率={atmosphericTransmittanceToTarget:F3}), " +
$"目标->导弹距离={distanceMissileToTarget:F1}m (透过率={atmosphericTransmittanceToMissile:F3}), " +
Console.WriteLine($"激光功率计算: S2T D={distanceSourceToTarget:F1}m (Path1 T={totalTransmittance_S2T:F3}), " +
$"T2M D={distanceTargetToMissile:F1}m (Path2 T={totalTransmittance_T2M:F3}), " +
$"最终功率={finalReceivedPower:E}W");
return finalReceivedPower;
@ -852,46 +829,36 @@ namespace ThreatSource.Guidance
}
/// <summary>
/// 计算烟幕对激光的衰减因子
/// 计算给定路径上的总烟幕透过率
/// </summary>
/// <param name="parameters">烟幕干扰参数</param>
/// <returns>衰减因子范围0-10表示完全衰减1表示无衰减</returns>
private double CalculateSmokeAttenuation(JammingParameters parameters)
/// <param name="pathStart">路径起点</param>
/// <param name="pathEnd">路径终点</param>
/// <returns>总透过率 (0.0 到 1.0)</returns>
private double CalculateLiveSmokeTransmittanceForPath(Vector3D pathStart, Vector3D pathEnd)
{
if (!parameters.SmokeConcentration.HasValue)
return 1.0; // 无衰减
double totalTransmittance = 1.0;
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
.Where(sg => sg.IsActive && sg.config != null && sg.IsJamming) // 确保烟幕弹已激活并正在干扰
.ToList();
if (!activeSmokeGrenades.Any())
{
return 1.0; // 没有活动的、正在干扰的烟幕,无衰减
}
foreach (var smokeGrenade in activeSmokeGrenades)
{
// 调用 SmokeGrenade 实例的方法来计算其对视线的透过率
double transmittanceForThisSmoke = smokeGrenade.GetSmokeTransmittanceOnLine(pathStart, pathEnd, config.Wavelength);
totalTransmittance *= transmittanceForThisSmoke; // 叠加衰减效应(透过率相乘)
// 获取烟幕浓度
double concentration = parameters.SmokeConcentration.Value;
// 计算烟幕厚度(设备到烟幕边缘的距离)
double effectiveThickness = 0;
if (parameters.SmokeType == SmokeScreenType.Cloud)
{
// 对于云状烟幕,使用设备到烟幕中心的距离
double distanceToCenter = (Position - parameters.SourcePosition).Magnitude();
double radius = parameters.SmokeThickness.HasValue ? parameters.SmokeThickness.Value / 2 : 10.0;
effectiveThickness = Math.Max(0, radius - distanceToCenter);
if (distanceToCenter < radius) // 如果在烟幕内部
effectiveThickness = 2 * (radius - distanceToCenter); // 双倍路径
// 如果透过率已经很低,可以提前退出以优化
if (totalTransmittance < 0.001)
{
return 0.0;
}
}
else // SmokeScreenType.Wall
{
// 对于墙状烟幕,使用烟幕厚度
effectiveThickness = parameters.SmokeThickness.HasValue ? parameters.SmokeThickness.Value : 5.0;
// 可以进一步计算有效厚度,但这里简化处理
}
// 使用 AtmosphereDllWrapper 计算烟幕透过率
double transmittance = AtmosphereDllWrapper.CalculateSmokeScreenTransmittance(
config.Wavelength, // 激光波长(微米)
concentration, // 烟幕浓度g/m³
effectiveThickness // 烟幕厚度(米)
);
Debug.WriteLine($"烟幕衰减计算 - 波长: {config.Wavelength:F2}um, 浓度: {concentration}g/m³, 厚度: {effectiveThickness}m, 透过率: {transmittance:P2}");
return transmittance;
return Math.Max(0.0, totalTransmittance); //确保不为负
}
}
}

View File

@ -66,7 +66,7 @@ namespace ThreatSource.Guidance
/// 记录目标的历史位置
/// 用于计算目标速度
/// </remarks>
private Vector3D? lastTargetPosition { get; set; }
private Vector3D? LastTargetPosition { get; set; }
/// <summary>
/// 上一次探测到的目标速度 (使用可空类型)
@ -75,7 +75,7 @@ namespace ThreatSource.Guidance
/// 记录目标的历史速度
/// 用于计算目标速度
/// </remarks>
private Vector3D? lastTargetVelocity { get; set; }
private Vector3D? LastTargetVelocity { get; set; }
/// <summary>
/// 目标丢失计时器
@ -125,11 +125,7 @@ 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;
private const double SpeedOfLight = 299792458.0; // m/s
/// <summary>
/// 初始化毫米波制导系统的新实例
@ -174,8 +170,8 @@ namespace ThreatSource.Guidance
lockConfirmationTimer = 0;
currentScanAngle = 0;
currentScanRadius = config.SearchBeamWidth * Math.PI / 720.0; // 从半个波束宽度的一半开始
lastTargetPosition = null;
lastTargetVelocity = null;
LastTargetPosition = null;
LastTargetVelocity = null;
Trace.WriteLine($"切换到搜索模式,波束宽度: {config.SearchBeamWidth}度");
}
@ -229,8 +225,8 @@ namespace ThreatSource.Guidance
HasTarget = false;
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
lastTargetPosition = null;
lastTargetVelocity = null;
LastTargetPosition = null;
LastTargetVelocity = null;
}
/// <summary>
@ -252,10 +248,8 @@ namespace ThreatSource.Guidance
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
// 计算烟幕透过率
// TODO: 实现基于 JammingParameters 中烟幕属性 (浓度、厚度等) 的真实透过率计算。
_currentSmokeTransmittance = 1.0; // 暂时设置为无遮挡
Debug.WriteLine($"[MMW_GUIDANCE] 烟幕干扰应用,(临时) 透过率: {_currentSmokeTransmittance:P2}", "Jamming");
Debug.WriteLine($"[MMW_GUIDANCE] 受到烟幕干扰影响。Jammer ID: {parameters.JammerId}", "Jamming");
// 烟幕主要通过影响SNR体现不直接触发模式切换
}
}
@ -274,8 +268,8 @@ namespace ThreatSource.Guidance
}
else if (parameters.Type == JammingType.SmokeGrenade)
{
_currentSmokeTransmittance = 1.0; // 重置烟幕透过率
Debug.WriteLine($"[MMW_GUIDANCE] 烟幕干扰已清除。", "Jamming");
// _currentSmokeTransmittance = 1.0; // Field removed, no longer needed here
Debug.WriteLine($"[MMW_GUIDANCE] 烟幕干扰解除。Jammer ID: {parameters.JammerId}", "Jamming");
}
}
@ -298,11 +292,11 @@ namespace ThreatSource.Guidance
// 检查波长是否与工作波长匹配 (单位: 毫米 mm)
// 计算配置波长 (单位: mm), 直接使用光速值 3e8 m/s
const double speedOfLight = 3e8;
double configWavelength = speedOfLight / config.WaveFrequency * 1000.0; // m/s / Hz * 1000 = mm
double configWavelength_mm = speedOfLight / config.WaveFrequency * 1000.0; // m/s / Hz * 1000 = mm
if (Math.Abs((parameters.Wavelength ?? 0) - configWavelength) > 1e-1) // 允许 0.1mm 偏差
if (Math.Abs((parameters.Wavelength ?? 0) - configWavelength_mm) > 1e-1) // 允许 0.1mm 偏差
{
Debug.WriteLine($"[MMW_GUIDANCE] {Id} 忽略毫米波干扰:干扰波长 {parameters.Wavelength}mm 与系统工作波长 {configWavelength:F2}mm (频率 {config.WaveFrequency/1e9:F1}GHz) 不匹配。", "Jamming");
Debug.WriteLine($"[MMW_GUIDANCE] {Id} 忽略毫米波干扰:干扰波长 {parameters.Wavelength}mm 与系统工作波长 {configWavelength_mm:F2}mm (频率 {config.WaveFrequency/1e9:F1}GHz) 不匹配。", "Jamming");
return false;
}
}
@ -461,18 +455,17 @@ namespace ThreatSource.Guidance
{
base.Update(deltaTime); // 更新基类状态和干扰状态
// 在执行核心逻辑前检查干扰状态
if (IsJammed) // 使用基类的 IsJammed 状态
// 在执行核心逻辑前检查阻塞性干扰状态
if (IsBlockingJammed)
{
// 如果被干扰,确保处于搜索模式并且没有制导
// 如果被阻塞性干扰,确保处于搜索模式并且没有制导
if (currentMode != WorkMode.Search)
{
SwitchToSearchMode();
}
HasGuidance = false;
GuidanceAcceleration = Vector3D.Zero;
// Debug.WriteLine($"[MMW_GUIDANCE] {Id} 处于干扰状态,跳过制导计算。", "Jamming");
return; // 不执行后续逻辑
return;
}
// 更新扫描参数
@ -482,13 +475,13 @@ namespace ThreatSource.Guidance
{
targetLostTimer = 0;
Vector3D? currentTargetVelocity = null;
if (lastTargetPosition != null && deltaTime > 0)
if (LastTargetPosition != null && deltaTime > 0)
{
currentTargetVelocity = (currentTargetPosition - lastTargetPosition) / deltaTime;
currentTargetVelocity = (currentTargetPosition - LastTargetPosition) / deltaTime;
}
lastTargetPosition = currentTargetPosition;
lastTargetVelocity = currentTargetVelocity;
LastTargetPosition = currentTargetPosition;
LastTargetVelocity = currentTargetVelocity;
GuidanceAcceleration = MotionAlgorithm.CalculateProportionalNavigation(
ProportionalNavigationCoefficient,
@ -546,56 +539,53 @@ namespace ThreatSource.Guidance
targetPosition = Vector3D.Zero;
double minDistance = double.MaxValue;
bool foundTarget = false;
double currentSNR = double.MinValue;
if (IsJammed)
{
HasGuidance = false;
return false;
}
double currentSNR_dB = double.MinValue;
foreach (var element in SimulationManager.GetEntitiesByType<SimulationElement>())
{
if (element is BaseEquipment target)
if (element is BaseEquipment target && target.Id != MissileId && target.IsActive)
{
Vector3D toTarget = target.Position - missilePosition;
double distance = toTarget.Magnitude();
if (distance <= config.MaxDetectionRange)
if (distance <= config.MaxDetectionRange && distance > 0)
{
double snr = CalculateSNR(distance, target.Properties.RadarCrossSection);
Console.WriteLine($"信噪比: {snr:F2}dB");
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.Position);
double snr_dB = CalculateSNR(distance, target.Properties.RadarCrossSection, liveSmokeTransmittance);
if (currentMode == WorkMode.Search)
{
if (IsTargetInBeam(missileVelocity, toTarget) && snr >= config.RecognitionSNRThreshold)
if (IsTargetInBeam(missileVelocity, toTarget) && snr_dB >= config.RecognitionSNRThreshold)
{
targetPosition = target.Position;
minDistance = distance;
foundTarget = true;
currentSNR = snr;
if (distance < minDistance)
{
targetPosition = target.Position;
minDistance = distance;
foundTarget = true;
currentSNR_dB = snr_dB;
}
}
}
else if (currentMode == WorkMode.Track || currentMode == WorkMode.Lock)
{
if (IsTargetInBeam(missileVelocity, toTarget) &&
lastTargetPosition != null &&
Vector3D.Distance(target.Position, lastTargetPosition) < 100.0)
bool isPotentiallyTrackedTarget = true;
if (LastTargetPosition != null)
{
double requiredSNR = (currentMode == WorkMode.Track) ?
double proximityThreshold = (target.Properties.Length > 0) ? target.Properties.Length * 5.0 : 100.0;
isPotentiallyTrackedTarget = Vector3D.Distance(target.Position, LastTargetPosition) < proximityThreshold;
}
if (IsTargetInBeam(missileVelocity, toTarget) && isPotentiallyTrackedTarget)
{
double requiredSNR_dB = (currentMode == WorkMode.Track) ?
config.RecognitionSNRThreshold : config.LockSNRThreshold;
if (snr >= requiredSNR)
if (snr_dB >= requiredSNR_dB)
{
targetPosition = target.Position;
foundTarget = true;
currentSNR = snr;
Console.WriteLine($"[目标跟踪] 距离: {distance:F1}米, SNR: {snr:F2}dB, 要求SNR: {requiredSNR:F2}dB");
break;
}
else
{
Console.WriteLine($"[目标丢失] 距离: {distance:F1}米, SNR: {snr:F2}dB, 要求SNR: {requiredSNR:F2}dB");
currentSNR_dB = snr_dB;
break;
}
}
}
@ -603,8 +593,8 @@ namespace ThreatSource.Guidance
}
}
UpdateSystemState(foundTarget, currentSNR, deltaTime);
HasTarget = foundTarget;
UpdateSystemState(foundTarget, currentSNR_dB, deltaTime);
HasTarget = foundTarget;
return foundTarget;
}
@ -674,8 +664,8 @@ namespace ThreatSource.Guidance
{
var statusInfo = base.GetStatusInfo();
string lastPosStr = lastTargetPosition != null ? lastTargetPosition.ToString() : "null";
string lastVelStr = lastTargetVelocity != null ? lastTargetVelocity.ToString() : "null";
string lastPosStr = LastTargetPosition != null ? LastTargetPosition.ToString() : "null";
string lastVelStr = LastTargetVelocity != null ? LastTargetVelocity.ToString() : "null";
statusInfo.ExtendedProperties["Mode"] = currentMode.ToString();
statusInfo.ExtendedProperties["LastTargetPosition"] = lastPosStr;
@ -690,6 +680,7 @@ namespace ThreatSource.Guidance
/// </summary>
/// <param name="distance">到目标的距离,单位:米</param>
/// <param name="radarCrossSection">目标雷达散射截面积,单位:平方米</param>
/// <param name="smokeTransmittanceLinear">当前视线的烟幕总线性透过率 (0.0 到 1.0)</param>
/// <returns>信噪比,单位:分贝</returns>
/// <remarks>
/// 计算过程:
@ -698,53 +689,116 @@ namespace ThreatSource.Guidance
/// - 计算噪声功率
/// - 计算信噪比
/// - 转换为分贝值
/// - 应用烟幕衰减
/// </remarks>
private double CalculateSNR(double distance, double radarCrossSection)
private double CalculateSNR(double distance, double radarCrossSection, double smokeTransmittanceLinear)
{
// 雷达参数
double transmitPower = config.TransmitPower; // 发射功率(W)典型值0.3W
double antennaGain = Math.Pow(10, config.AntennaGainDB/10); // 天线增益(线性值)
double wavelength = 3e8 / config.WaveFrequency; // 波长(m)94GHz -> 3.19mm
double bandwidth = 1.0 / config.PulseDuration; // 带宽(Hz),由脉冲持续时间决定
double noiseFigure = Math.Pow(10, config.NoiseFigureDB/10); // 噪声系数(线性值)
double transmitPower = config.TransmitPower;
double antennaGain = Math.Pow(10, config.AntennaGainDB/10);
// 系统损耗单位dB
double atmosphericLoss = 0.4 * distance / 1000.0; // 大气衰减0.4dB/km
double totalLoss = Math.Pow(10, (atmosphericLoss + config.SystemLossDB) / 10); // 转换为线性值
// 波长,单位:米 (m),用于雷达方程核心计算
double wavelength_m = SpeedOfLight / config.WaveFrequency;
// 波长,单位:微米 (µm),用于需要微米单位的函数调用
double wavelength_um = SpeedOfLight / config.WaveFrequency * 1e6;
// 常量
double k = 1.38e-23; // 玻尔兹曼常数
double T0 = 290; // 标准噪声温度(K)
double bandwidth = 1.0 / config.PulseDuration;
double noiseFigure = Math.Pow(10, config.NoiseFigureDB/10);
double atmosphericLoss_dB = 0.4 * distance / 1000.0;
double totalSystemLoss_linear = Math.Pow(10, (atmosphericLoss_dB + config.SystemLossDB) / 10.0);
// 计算接收信号功率
double signalPower = (transmitPower * Math.Pow(antennaGain, 2) * Math.Pow(wavelength, 2) * radarCrossSection)
/ (Math.Pow(4 * Math.PI, 3) * Math.Pow(distance, 4) * totalLoss);
double k = 1.38e-23;
double T0 = 290;
// 考虑大气透过率如果当前天气为null则认为大气透过率为1.0
double atmosphericTransmittance = 1.0;
double signalPowerNumerator = transmitPower * Math.Pow(antennaGain, 2) * Math.Pow(wavelength_m, 2) * radarCrossSection; // Use wavelength_m
double signalPowerDenominator = Math.Pow(4 * Math.PI, 3) * Math.Pow(distance, 4) * totalSystemLoss_linear;
if(SimulationManager.CurrentWeather != null)
{
atmosphericTransmittance = AtmosphereDllWrapper.CalculateTransmittance(
if (signalPowerDenominator == 0) return -100.0;
double signalPower = signalPowerNumerator / signalPowerDenominator;
double weatherSpecificAtmosphericTransmittance = 1.0;
if(SimulationManager?.CurrentWeather != null)
{
weatherSpecificAtmosphericTransmittance = AtmosphereDllWrapper.CalculateTransmittance(
distance,
RadiationType.MillimeterWave,
wavelength,
wavelength_um, // Use wavelength_um
SimulationManager.CurrentWeather);
}
signalPower *= weatherSpecificAtmosphericTransmittance;
signalPower *= atmosphericTransmittance;
// 应用当前生效的烟幕衰减 (通过存储的透过率)
signalPower *= _currentSmokeTransmittance;
// 计算噪声功率
double noisePower = k * T0 * bandwidth * noiseFigure;
if (noisePower == 0) return 100.0;
// 计算信噪比
double snr = signalPower / noisePower;
double snr_linear = signalPower / noisePower;
// 转换为dB
return 10 * Math.Log10(snr);
if (snr_linear <= 0) return -100.0;
double snr_dB_no_smoke = 10 * Math.Log10(snr_linear);
Console.WriteLine($"[SNR_CALC] SNR_dB (before smoke): {snr_dB_no_smoke:F2} dB. Incoming SmokeTransmittance_Linear: {smokeTransmittanceLinear:F6}");
// 应用当前生效的烟幕衰减 (通过存储的透过率) - REMOVED _currentSmokeTransmittance FIELD USAGE
// signalPower *= _currentSmokeTransmittance; // This line is now handled by the parameter
// 应用烟幕透过率 (smokeTransmittanceLinear is already linear 0-1)
if (smokeTransmittanceLinear <= 0.000001) // Threshold to prevent log(0) or very large negative numbers
{
Console.WriteLine($"[SNR_CALC] Smoke transmittance too low. Final_SNR_dB: -100.0 dB");
return -100.0; // Effectively zero signal due to smoke
}
// Loss_smoke_dB = -10 * log10(T_smoke_linear)
// SNR_final_dB = SNR_original_dB - Loss_smoke_dB = SNR_original_dB + 10 * log10(T_smoke_linear)
double smokeEffect_dB = 10 * Math.Log10(smokeTransmittanceLinear);
double final_snr_dB = snr_dB_no_smoke + smokeEffect_dB;
Console.WriteLine($"[SNR_CALC] SmokeEffect: {smokeEffect_dB:F2} dB. Final_SNR_dB (after smoke): {final_snr_dB:F2} dB");
return final_snr_dB;
}
/// <summary>
/// 计算给定观察点和目标点之间的总烟幕透过率。
/// </summary>
/// <param name="observerPosition">观察者位置</param>
/// <param name="targetEndPosition">目标位置</param>
/// <returns>总透过率 (0.0 到 1.0)。</returns>
private double CalculateLiveSmokeTransmittance(Vector3D observerPosition, Vector3D targetEndPosition)
{
double totalTransmittance = 1.0;
if (SimulationManager == null)
{
Trace.WriteLineIf(SimulationManager == null, "[MMW_GUIDANCE] SimulationManager is null in CalculateLiveSmokeTransmittance. Assuming no smoke.", "Warning");
return 1.0;
}
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
.Where(sg => sg.IsActive && sg.IsJamming && sg.config != null)
.ToList();
if (activeSmokeGrenades.Count == 0)
{
return 1.0;
}
double wavelength_um = SpeedOfLight / config.WaveFrequency * 1e6;
if (wavelength_um <= 0) {
Trace.WriteLine("[MMW_GUIDANCE] Calculated wavelength in meters is invalid in CalculateLiveSmokeTransmittance. Assuming no smoke effect.", "Error");
return 1.0;
}
foreach (var smokeGrenade in activeSmokeGrenades)
{
double transmittanceForThisSmoke = smokeGrenade.GetSmokeTransmittanceOnLine(observerPosition, targetEndPosition, wavelength_um);
totalTransmittance *= transmittanceForThisSmoke;
if (totalTransmittance < 0.000001) // Use a smaller threshold for early exit if transmittance is effectively zero
{
return 0.0;
}
}
return Math.Max(0.0, totalTransmittance);
}
}
}

View File

@ -158,18 +158,7 @@ namespace ThreatSource.Indicator
/// <param name="parameters">干扰参数</param>
protected virtual void HandleJammingApplied(JammingParameters parameters)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (parameters.Type == JammingType.SmokeGrenade)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现特定的干扰响应
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} 受到 {parameters.Type} 类型干扰,功率:{parameters.Power}W", "Jamming");
}
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} 接收到应用干扰事件: {parameters.Type}, 功率: {parameters.Power}W", "Jamming");
}
/// <summary>
@ -178,44 +167,31 @@ namespace ThreatSource.Indicator
/// <param name="parameters">干扰参数</param>
protected virtual void HandleJammingCleared(JammingParameters parameters)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (parameters.Type == JammingType.SmokeGrenade)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现干扰清除后的特定行为
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} {parameters.Type} 类型干扰已清除", "Jamming");
}
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} 接收到清除干扰事件: {parameters.Type}", "Jamming");
}
/// <summary>
/// 激活指示器 (基类统一处理干扰事件订阅)
/// 激活指示器
/// </summary>
public override void Activate()
{
if (!IsActive)
{
IsActive = true;
// 统一订阅 JammingEvent
SimulationManager.SubscribeToEvent<JammingEvent>(HandleJammingEvent);
SimulationManager.SubscribeToEvent<JammingStoppedEvent>(HandleJammingStoppedEvent);
}
base.Activate();
RecalculateObscurationStatus(); // 激活时检查一次初始遮挡状态
}
/// <summary>
/// 停用指示器 (基类统一处理干扰事件取消订阅)
/// 停用指示器
/// </summary>
public override void Deactivate()
{
if (IsActive)
{
IsActive = false;
// 统一取消订阅 JammingEvent
SimulationManager.UnsubscribeFromEvent<JammingEvent>(HandleJammingEvent);
SimulationManager.UnsubscribeFromEvent<JammingStoppedEvent>(HandleJammingStoppedEvent);
}
@ -231,15 +207,18 @@ namespace ThreatSource.Indicator
/// </summary>
public override void Update(double deltaTime)
{
_jammingComponent.UpdateJammingStatus(deltaTime); // 更新干扰状态(包括持续时间等)
_jammingComponent.UpdateJammingStatus(deltaTime);
if (IsActive)
if (IsActive)
{
// 只有未被电子干扰时才更新指示器特定功能
// 烟幕遮挡 (IsTargetObscured) 在 UpdateIndicator 内部处理
if (!IsJammed)
if(IsJammed && !IsBlockingJammed)
{
UpdateIndicator(deltaTime);
RecalculateObscurationStatus();
}
if (!IsBlockingJammed)
{
UpdateIndicator(deltaTime);
}
}
}
@ -273,6 +252,7 @@ namespace ThreatSource.Indicator
statusInfo.ExtendedProperties["lastKnownTargetOrientation"] = _lastKnownTargetOrientation ?? new Orientation(0, 0, 0);
statusInfo.ExtendedProperties["IsTargetObscured"] = IsTargetObscured;
statusInfo.ExtendedProperties["IsJammed"] = IsJammed;
statusInfo.ExtendedProperties["IsBlockingJammed"] = IsBlockingJammed;
return statusInfo;
}
@ -321,11 +301,8 @@ namespace ThreatSource.Indicator
}
// 3. 获取活动的烟幕
var activeSmokeGrenades = SimulationManager.GetEntitiesByType<SmokeGrenade>()
.Where(sg => sg != null && sg.IsActive)
.ToList(); // 获取列表以便检查是否为空
if (activeSmokeGrenades.Count == 0)
var activeJammerIds = _jammingComponent.GetActiveJammerIdsOfType(JammingType.SmokeGrenade);
if (!activeJammerIds.Any())
{
return false; // 没有活动的烟幕,视为未遮挡
}
@ -337,8 +314,14 @@ namespace ThreatSource.Indicator
Vector3D targetDims = new(targetFound.Properties.Width, targetFound.Properties.Height, targetFound.Properties.Length);
// 5. 遍历烟幕检查遮挡
foreach (var smokeGrenade in activeSmokeGrenades)
foreach (var jammerId in activeJammerIds)
{
var smokeGrenade = SimulationManager.GetEntityById(jammerId) as SmokeGrenade;
if (smokeGrenade == null)
{
continue;
}
try
{
Vector3D smokeCenter = smokeGrenade.Position;
@ -376,15 +359,12 @@ namespace ThreatSource.Indicator
return false; // 未被遮挡
}
// --- 新增干扰事件处理逻辑 ---
/// <summary>
/// 统一处理干扰事件
/// 处理干扰事件
/// </summary>
/// <param name="evt">干扰事件</param>
protected virtual void HandleJammingEvent(JammingEvent evt)
{
Console.WriteLine($"[BaseIndicator] 处理干扰事件: {evt.Parameters.Type}");
if (evt == null) return;
// 在应用干扰前检查是否应该处理此干扰
@ -416,8 +396,12 @@ namespace ThreatSource.Indicator
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected virtual bool ShouldHandleJamming(JammingParameters parameters)
{
// 子类可以重写此方法添加额外检查,如波段匹配
// 注意:烟幕干扰虽然通过这里,但主要效果通过 IsTargetObscured 和 RecalculateObscurationStatus 体现
// 首先检查当前指示器实例(通过其具体的 SupportedJammingTypes 实现)是否支持此干扰类型
if (!this.SupportedJammingTypes.Contains(parameters.Type))
{
return false;
}
// 如果类型受支持,则允许子类进行更具体的检查
return true;
}
}

View File

@ -90,22 +90,11 @@ namespace ThreatSource.Indicator
/// <param name="deltaTime">时间步长,单位:秒</param>
/// <remarks>
/// 实现红外测角仪特定的更新逻辑:
/// - 基类 Update 会检查 IsJammed (电子干扰)
/// - UpdateTracking 方法内部会处理 IsTargetObscured (烟幕遮挡)
/// - 更新跟踪状态
/// </remarks>
protected override void UpdateIndicator(double deltaTime)
{
// 基类 Update 已经检查了 IsJammed
// if (IsJammed)
// {
// StopTracking(); // Logic moved to HandleJammingApplied
// Console.WriteLine($"InfraredTracker {Id} 受到红外干扰,停止跟踪。");
// return;
// }
// 无论是否被遮挡,都尝试更新跟踪状态
// IsTargetObscured is handled within UpdateTracking
UpdateTracking();
}
@ -317,7 +306,7 @@ namespace ThreatSource.Indicator
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected override bool ShouldHandleJamming(JammingParameters parameters)
{
// 首先调用基类检查是否支持该干扰类型 (包括 SmokeScreen)
// 首先调用基类的检查,看是否支持此类型
if (!base.ShouldHandleJamming(parameters))
{
return false;
@ -337,7 +326,7 @@ namespace ThreatSource.Indicator
}
}
// 如果是支持的非红外干扰 (例如烟幕) 或通过了红外波长检查,则返回 true
// 如果是支持的类型 (SmokeGrenade) 或通过了特定检查的类型 (Infrared)返回 true
return true;
}

View File

@ -199,7 +199,7 @@ namespace ThreatSource.Indicator
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected override bool ShouldHandleJamming(JammingParameters parameters)
{
// 首先调用基类检查是否支持该干扰类型 (包括 SmokeScreen)
// 首先调用基类的检查,看是否支持此类型
if (!base.ShouldHandleJamming(parameters))
{
return false;
@ -216,7 +216,7 @@ namespace ThreatSource.Indicator
}
}
// 如果是支持的非激光干扰 (例如烟幕) 或通过了激光波长检查,则返回 true
// 如果是支持的类型 (SmokeGrenade) 或通过了特定检查的类型 (Laser)返回 true
return true;
}
@ -335,9 +335,10 @@ namespace ThreatSource.Indicator
// 添加子类特定响应:如果是激光干扰清除,并且设备仍激活,尝试启动光束
if (parameters.Type == JammingType.Laser)
{
{
Debug.WriteLine($"[LaserBeamRider] {Id} 激光干扰已清除。", "Jamming");
if (IsActive && !IsJammed) // Check IsJammed in case other jams exist
// 如果设备仍处于激活状态,并且没有其他阻塞性干扰,恢复激光照射
if (IsActive && !IsBlockingJammed)
{
StartBeamIllumination();
}

View File

@ -209,8 +209,8 @@ namespace ThreatSource.Indicator
if (parameters.Type == JammingType.Laser)
{
Console.WriteLine($"[LaserDesignator] {Id} 激光干扰已清除。", "Jamming");
// 如果设备仍处于激活状态,并且没有其他干扰,恢复激光照射
if (IsActive && !IsJammed)
// 如果设备仍处于激活状态,并且没有其他阻塞性干扰,恢复激光照射
if (IsActive && !IsBlockingJammed)
{
StartLaserIllumination();
}
@ -324,7 +324,7 @@ namespace ThreatSource.Indicator
/// <returns>如果应该处理则返回 true否则返回 false</returns>
protected override bool ShouldHandleJamming(JammingParameters parameters)
{
// 首先调用基类检查是否支持该干扰类型 (包括 SmokeScreen)
// 首先调用基类的检查,看是否支持此类型
if (!base.ShouldHandleJamming(parameters))
{
return false;
@ -340,8 +340,7 @@ namespace ThreatSource.Indicator
return false;
}
}
// 如果是支持的非激光干扰 (例如烟幕) 或通过了激光波长检查,则返回 true
// 对于其他支持的类型 (如 SmokeGrenade它没有额外的检查) 或通过了特定检查的类型,返回 true
return true;
}

View File

@ -171,75 +171,104 @@ namespace ThreatSource.Jammable
/// <returns>如果干扰有效返回true否则返回false</returns>
public bool IsJammingEffective(JammingParameters parameters)
{
// 1. 检查是否支持该干扰类型
// 1. 检查是否支持该干扰类型 (这个检查保持在最前面)
if (!_supportedJammingTypes.Contains(parameters.Type))
{
Console.WriteLine($"[干扰有效性检查] 不支持的干扰类型: {parameters.Type}");
return false;
}
// 2. 提取原有的角度和距离相关计算逻辑
// 计算距离和相对位置
Vector3D devicePosition = _positionProvider();
Vector3D relativePosition = devicePosition - parameters.SourcePosition;
double distance = relativePosition.Magnitude();
Console.WriteLine($"干扰计算 - 设备位置: {devicePosition}, 干扰源位置: {parameters.SourcePosition}");
Console.WriteLine($"干扰计算 - 相对位置: {relativePosition}, 距离: {distance:F2}m");
if (distance <= 0)
bool isDistanceNearZero = false;
// 使用一个小的 epsilon 来判断距离是否接近零
if (distance <= 1e-6)
{
distance = 0.1; // 设置最小距离
isDistanceNearZero = true;
Console.WriteLine($"干扰计算 - 调整近零距离为 {distance}m");
}
Console.WriteLine($"干扰计算 - 设备位置: {devicePosition}, 干扰源位置: {parameters.SourcePosition}");
Console.WriteLine($"干扰计算 - 相对位置: {relativePosition}, 距离: {distance:F2}m");
// 角度检查逻辑 (普遍适用)
// 仅当 AngleRange 有意义时 (大于0且小于360表示有方向性) 才进行角度检查
if (parameters.AngleRange > 0 && parameters.AngleRange < 360)
// 2. 选择性角度约束检查
bool performAngleCheck = false;
// 仅当 AngleRange 被指定且表示方向性时才检查
if (parameters.AngleRange > 0 && parameters.AngleRange < 360)
{
Vector3D jammerDirection = parameters.Direction.Normalize();
Vector3D jammerToDeviceDirection = relativePosition.Normalize();
double dotProduct = Vector3D.DotProduct(jammerDirection, jammerToDeviceDirection);
double angle = Math.Acos(Math.Clamp(dotProduct, -1.0, 1.0)); // 弧度
Console.WriteLine($"干扰计算 - 干扰器方向: {jammerDirection}, 到设备方向: {jammerToDeviceDirection}");
Console.WriteLine($"干扰计算 - 点积: {dotProduct:F2}, 夹角: {angle * 180 / Math.PI:F2} 度, 波束范围: {parameters.AngleRange:F2} 度");
// 如果到设备的角度超出了干扰器波束半角 (AngleRange 是总锥角)
if (angle > (parameters.AngleRange * Math.PI / 360.0) )
// 定义哪些类型/模式需要进行角度检查
if (parameters.Mode == JammingMode.Blocking ||
parameters.Type == JammingType.MillimeterWaveCompensation) // 在此添加其他需要角度检查的类型
{
Console.WriteLine($"[干扰有效性检查] 类型 {parameters.Type}, 模式 {parameters.Mode}:超出波束角度。判定无效。");
return false; // 超出角度,对于所有模式都判定无效
performAngleCheck = true;
}
}
// 3. 特定于模式的检查
if (performAngleCheck)
{
Console.WriteLine($"[干扰有效性检查] 类型 {parameters.Type},模式 {parameters.Mode}:需要进行角度检查。");
Vector3D jammerDirection = parameters.Direction.Normalize();
// 如果距离非常近,避免对零向量进行归一化
Vector3D jammerToDeviceDirection = !isDistanceNearZero ? relativePosition.Normalize() : Vector3D.Zero;
// 如果距离非常近,我们可能认为它总是在波束内,跳过角度比较。
if (isDistanceNearZero) {
Console.WriteLine($"干扰计算 - 距离过近,跳过角度约束比较。");
}
else // 距离不近,执行角度比较
{
double dotProduct = Vector3D.DotProduct(jammerDirection, jammerToDeviceDirection);
// Clamp dot product 以避免 Math.Acos 的定义域错误
dotProduct = Math.Max(-1.0, Math.Min(1.0, dotProduct));
double angle = Math.Acos(dotProduct); // 角度(弧度)
Console.WriteLine($"干扰计算 - 干扰器方向: {jammerDirection}, 到设备方向: {jammerToDeviceDirection}");
Console.WriteLine($"干扰计算 - 点积: {dotProduct:F2}, 夹角: {angle * 180 / Math.PI:F2} 度, 波束范围: {parameters.AngleRange:F2} 度");
// AngleRange 是总锥角,与半角进行比较
if (angle > (parameters.AngleRange * Math.PI / 360.0))
{
Console.WriteLine($"[干扰有效性检查] 类型 {parameters.Type},模式 {parameters.Mode}:超出波束角度。判定无效。");
return false; // 未通过角度约束
}
Console.WriteLine($"[干扰有效性检查] 类型 {parameters.Type},模式 {parameters.Mode}:通过角度检查。");
}
}
else
{
Console.WriteLine($"[干扰有效性检查] 类型 {parameters.Type},模式 {parameters.Mode}:无需角度检查 (AngleRange={parameters.AngleRange})。");
}
// 3. 针对阻塞模式的检查
if (parameters.Mode == JammingMode.Blocking)
{
// 检查是否支持该干扰类型且定义了阻塞干扰的阈值
// 对阻塞干扰进行功率阈值检查
if (!_jammingThresholds.TryGetValue(parameters.Type, out double threshold))
{
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type} 未找到阈值。假设无效。");
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type} 未找到阈值。判定无效。");
return false;
}
// 球面扩散损耗模型
// 使用已计算的距离
double receivedPower = parameters.Power / (4 * Math.PI * distance * distance);
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type} - 发射功率: {parameters.Power}W, 接收功率: {receivedPower:E6}W, 阈值: {threshold:E6}W, 距离: {distance:F2}m");
if (receivedPower < threshold)
{
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type}:接收功率低于阈值。判定无效。");
return false; // 功率不足,判定无效
return false; // 未通过功率阈值检查
}
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type}:通过角度和功率检查。判定有效。");
return true; // 通过所有阻塞特定检查
Console.WriteLine($"[干扰有效性检查] 阻塞干扰 {parameters.Type}:通过所有检查。判定有效。");
return true; // 通过了阻塞模式的所有检查
}
else // 非阻塞干扰 (e.g., Deception, Compensation)
else // 非阻塞模式 (Deception, Compensation, 等)
{
// 对于非阻塞干扰,如果通过了上面的类型支持和角度检查,则认为它们是有效的。
Console.WriteLine($"[干扰有效性检查] 非阻塞干扰 {parameters.Type}:通过类型支持和角度检查。判定有效。");
return true;
Console.WriteLine($"[干扰有效性检查] 非阻塞干扰 {parameters.Type}:通过检查。判定有效。");
return true; // 如果是非阻塞且通过了初步检查,则认为有效
}
}
@ -270,13 +299,10 @@ namespace ThreatSource.Jammable
/// <summary>
/// 处理内部 JammingHandler 的 JammingCleared 事件
/// </summary>
private void HandleInternalJammingCleared(JammingParameters? parameters)
private void HandleInternalJammingCleared(JammingParameters parameters)
{
// 触发公共事件,如果可用,则提供类型
if (parameters != null)
{
JammingCleared?.Invoke(parameters);
}
// 触发公共事件
JammingCleared?.Invoke(parameters);
}
/// <summary>

View File

@ -1,6 +1,7 @@
using ThreatSource.Jammer;
using System; // Added for Action delegate
using System.Collections.Generic;
using System.Linq; // Added for Linq methods like Any() and FirstOrDefault()
namespace ThreatSource.Jammable
{
@ -9,34 +10,41 @@ namespace ThreatSource.Jammable
/// </summary>
public class JammingHandler
{
/// <summary>
/// 内部类,用于管理单个活动干扰的状态
/// </summary>
private class ActiveJammingState
{
public JammingParameters Parameters { get; }
public double ElapsedTime { get; set; }
public ActiveJammingState(JammingParameters parameters)
{
Parameters = parameters ?? throw new ArgumentNullException(nameof(parameters));
ElapsedTime = 0;
}
}
/// <summary>
/// 当前所有激活的干扰状态列表
/// </summary>
private readonly List<ActiveJammingState> _activeJammingStates = new List<ActiveJammingState>();
/// <summary>
/// 是否处于被干扰状态
/// </summary>
public bool IsJammed { get; private set; }
public bool IsJammed => _activeJammingStates.Any();
/// <summary>
/// 获取当前是否正受到有效的阻塞式干扰
/// </summary>
public bool IsBlockingJammed
{
get
{
// 如果没有被干扰,或者当前没有干扰参数,则肯定不是阻塞干扰
// 否则,检查当前干扰的模式是否为阻塞
return IsJammed && CurrentJamming != null && CurrentJamming.Mode == JammingMode.Blocking;
}
}
public bool IsBlockingJammed => _activeJammingStates.Any(state => state.Parameters.Mode == JammingMode.Blocking);
/// <summary>
/// 当前干扰参数
/// </summary>
protected JammingParameters? CurrentJamming { get; private set; }
/// <summary>
/// 累积的干扰时间
/// </summary>
private double _elapsedTime;
/// <summary>
/// 当干扰被应用时触发
/// </summary>
@ -53,13 +61,20 @@ namespace ThreatSource.Jammable
/// <param name="deltaTime">时间步长,单位:秒</param>
public virtual void Update(double deltaTime)
{
if (IsJammed && CurrentJamming?.Duration != null)
// 从后向前遍历,以便在迭代过程中安全地移除元素
for (int i = _activeJammingStates.Count - 1; i >= 0; i--)
{
_elapsedTime += deltaTime;
// 检查是否超时
if (_elapsedTime >= CurrentJamming.Duration.Value)
var state = _activeJammingStates[i];
if (state.Parameters.Duration != null)
{
ClearJamming(CurrentJamming);
state.ElapsedTime += deltaTime;
// 检查是否超时
if (state.ElapsedTime >= state.Parameters.Duration.Value)
{
var clearedJamming = state.Parameters;
_activeJammingStates.RemoveAt(i);
OnJammingCleared(clearedJamming);
}
}
}
}
@ -70,24 +85,43 @@ namespace ThreatSource.Jammable
/// <param name="parameters">干扰参数</param>
public virtual void HandleJamming(JammingParameters parameters)
{
_elapsedTime = 0;
IsJammed = true;
CurrentJamming = parameters;
OnJammingApplied(parameters);
if (parameters == null) throw new ArgumentNullException(nameof(parameters));
// 可选:替换来自同一干扰源且类型相同的现有干扰
var existingState = _activeJammingStates.FirstOrDefault(s =>
s.Parameters.JammerId == parameters.JammerId &&
s.Parameters.Type == parameters.Type);
if (existingState != null)
{
var replacedJamming = existingState.Parameters;
_activeJammingStates.Remove(existingState);
// OnJammingCleared(replacedJamming); // 可选:为被替换的干扰触发清除事件,如果需要
}
var newState = new ActiveJammingState(parameters);
_activeJammingStates.Add(newState);
OnJammingApplied(parameters);
}
/// <summary>
/// 清除干扰
/// 清除与指定参数匹配的干扰
/// </summary>
public virtual void ClearJamming(JammingParameters parameters)
/// <param name="parametersToClearCriteria">用于匹配要清除的干扰的参数。
/// 通常会基于 JammerId 和 Type 进行匹配。</param>
public virtual void ClearJamming(JammingParameters parametersToClearCriteria)
{
if (IsJammed)
if (parametersToClearCriteria == null) return; // 或者抛出异常
var statesToClear = _activeJammingStates
.Where(s => s.Parameters.JammerId == parametersToClearCriteria.JammerId &&
s.Parameters.Type == parametersToClearCriteria.Type)
.ToList(); // ToList() to avoid modification issues during enumeration
foreach (var state in statesToClear)
{
IsJammed = false;
_elapsedTime = 0;
var oldJamming = CurrentJamming;
CurrentJamming = null;
OnJammingCleared(oldJamming);
_activeJammingStates.Remove(state);
OnJammingCleared(state.Parameters);
}
}
@ -119,17 +153,20 @@ namespace ThreatSource.Jammable
/// <returns>匹配的活动干扰源ID枚举若无则为空枚举</returns>
public IEnumerable<string> GetActiveJammerIds(JammingType? typeFilter = null)
{
if (IsJammed && CurrentJamming != null)
{
if (typeFilter == null || CurrentJamming.Type == typeFilter)
{
if (!string.IsNullOrEmpty(CurrentJamming.JammerId))
{
yield return CurrentJamming.JammerId;
}
}
}
yield break; //确保在没有匹配项时返回一个空的IEnumerable
return _activeJammingStates
.Where(s => (typeFilter == null || s.Parameters.Type == typeFilter) &&
!string.IsNullOrEmpty(s.Parameters.JammerId))
.Select(s => s.Parameters.JammerId!)
.Distinct();
}
/// <summary>
/// 获取当前所有活动的干扰参数
/// </summary>
/// <returns>当前所有活动干扰参数的枚举</returns>
public IEnumerable<JammingParameters> GetAllActiveJammings()
{
return _activeJammingStates.Select(s => s.Parameters);
}
}
}

View File

@ -146,7 +146,7 @@ namespace ThreatSource.Jammer
if (thickness > 0.1 && config.IsObscuring)
{
effectiveTransmittance = physicalTransmittance * 0.01;
effectiveTransmittance = physicalTransmittance * 1e-3;
Console.WriteLine($"[烟幕透过率计算] 屏蔽型烟幕生效,透射率调整为: {effectiveTransmittance} (物理透射率: {physicalTransmittance})");
}

View File

@ -500,6 +500,7 @@ namespace ThreatSource.Missile
statusInfo.ExtendedProperties["IsGuidance"] = IsGuidance;
statusInfo.ExtendedProperties["LostGuidanceTime"] = LostGuidanceTime;
statusInfo.ExtendedProperties["Velocity"] = Velocity;
statusInfo.ExtendedProperties["GuidanceAcceleration"] = GuidanceAcceleration;
return statusInfo;
}

View File

@ -185,7 +185,7 @@ namespace ThreatSource.Sensor
}
/// <summary>
/// 统一处理干扰事件
/// 处理干扰事件
/// </summary>
/// <param name="evt">干扰事件</param>
protected virtual void HandleJammingEvent(JammingEvent evt)
@ -201,7 +201,7 @@ namespace ThreatSource.Sensor
}
/// <summary>
/// 统一处理干扰停止事件
/// 处理干扰停止事件
/// </summary>
/// <param name="evt">干扰停止事件</param>
protected virtual void HandleJammingStoppedEvent(JammingStoppedEvent evt)
@ -271,7 +271,7 @@ namespace ThreatSource.Sensor
if (!IsActive)
{
IsActive = true;
// 统一订阅 HandleJammingEvent
// 订阅 HandleJammingEvent
SimulationManager.SubscribeToEvent<JammingEvent>(HandleJammingEvent);
SimulationManager.SubscribeToEvent<JammingStoppedEvent>(HandleJammingStoppedEvent);
}
@ -286,7 +286,7 @@ namespace ThreatSource.Sensor
if (IsActive)
{
IsActive = false;
// 统一取消订阅 HandleJammingEvent
// 取消订阅 HandleJammingEvent
SimulationManager.UnsubscribeFromEvent<JammingEvent>(HandleJammingEvent);
SimulationManager.UnsubscribeFromEvent<JammingStoppedEvent>(HandleJammingStoppedEvent);
}

View File

@ -178,30 +178,28 @@ namespace ThreatSource.Sensor
}
}
}
if ((!IsJammed || IsJammed && !IsBlockingJammed) && smokeAttenuation > 0.1)
// 只有在没有阻塞性干扰虽然对该传感器为false且烟雾不影响时才进行核心更新
if (!IsBlockingJammed && smokeAttenuation > 0.1)
{
// 获取当前方向上的辐射温度
// 获取当前方向上的辐射温度 (此方法内部会考虑 isCompensationJammed)
double currentDirectionRadiationTemperature = GetCurrentDirectionRadiationTemperature();
// 计算辐射温度差
double temperatureDifference = Math.Abs(currentDirectionRadiationTemperature - lastDetectionTemperature);
// 如果温度差大于检测阈值,则认为检测到目标
if (temperatureDifference >= DetectionTemperatureDifferenceThreshold)
{
sensorData.IsTargetDetected = true;
}
else
{
sensorData.IsTargetDetected = false;
}
sensorData.IsTargetDetected = temperatureDifference >= DetectionTemperatureDifferenceThreshold;
sensorData.IsValid = true; // 数据流是有效的,即使可能被欺骗
// 更新上次检测温度
lastDetectionTemperature = currentDirectionRadiationTemperature;
}
else
else // 由于阻塞或烟雾导致无法工作
{
sensorData.IsValid = false;
sensorData.IsTargetDetected = false;
lastDetectionTemperature = BackgroundTemperature; // 重置,避免下次计算异常
}
}
@ -312,16 +310,14 @@ namespace ThreatSource.Sensor
base.HandleJammingCleared(parameters);
if (parameters.Type == JammingType.MillimeterWaveCompensation)
{
Debug.WriteLine("[MillimeterWaveRadiometer] 毫米波辐射计干扰已清除", "Jamming");
// 只有在整体未被干扰时才恢复有效状态
if (!IsJammed)
Debug.WriteLine("[MillimeterWaveRadiometer] 毫米波辐射计补偿干扰已清除", "Jamming");
isCompensationJammed = false; // 确保重置补偿干扰标志
// 只有在整体未被其他类型干扰(例如持续的烟幕)时才恢复有效状态
if (!IsJammed)
{
sensorData.IsValid = true;
}
else
{
sensorData.IsValid = false; // 如果仍被干扰,保持无效
}
}
}
}

View File

@ -1 +1 @@
0.2.15
0.2.16

View File

@ -7,6 +7,13 @@
- 事件描述
- 分析处理
## 2025-05-09 完善了各组件的干扰功能
- 把各组件的烟幕透过率计算移到 update 方法中
- 完善了指示器的烟幕遮挡计算逻辑
- 完善了激光半主动导弹的落点计算逻辑
- 完善了配置文件格式
- 集成测试的正常运行,坦克、指示器、导弹的初始化运动参数关联很大,修改了可能会影响很多测试场景。
## 2025-05-07 完善了末敏弹各传感器的干扰功能
- 增加了烟幕弹对末敏弹的干扰功能

View File

@ -263,4 +263,45 @@
[LaserDesignator] LD_1 受到激光干扰,停止照射。
```
实验结论:当激光干扰器的发射功率是 2000W 时,距离 2000米时如果激光指示器或导弹的阻塞干扰阈值是 1e-5W干扰效果是有效的。
实验结论:当激光干扰器的发射功率是 2000W 时,距离 2000米时如果激光指示器或导弹的阻塞干扰阈值是 1e-5W干扰效果是有效的。
## 激光驾束导弹实验记录v0.2.16
时间2025-05-08 10:00:00
版本v0.2.14
### 初始位置
1. 导弹和驾束仪初始位置
因为导弹必须沿着激光束飞行,所以需要调整激光束的初始位置,使得导弹在激光束的初始位置附近。
假设光束直径是6米导弹发射出去之后必须要进入这个光束才能沿着光束飞行。所以两者的初始位置的距离必须小于3米。
2. 坦克初始位置
如果是烟幕干扰,因为烟幕的底边在地面上,所以坦克全部都需要在地面以上,才能被烟幕遮住。所以坦克的中心位置,高度应该是坦克高度的 1/2。
## 激光半主动导弹实验记录v0.2.16
时间2025-05-09 10:00:00
版本v0.2.15
### 激光诱偏参数和结果
1. 坦克初始位置
0.0,1.2,0.0
2. 诱偏激光目标位置
0.0,0.0,50.0
3. 导弹视场角
30 度
4. 运行结果
诱偏功率 25W落点位置 (-97.01, 0.02, 29.87)
诱偏功率 50W落点位置 (-57.31, 0.01, 37.83)
诱偏功率 100W落点位置 (-20.88, 0.00, 43.26)
诱偏功率 200W落点位置 (5.64, 0.01, 46.44)
结论:
1. 诱偏功率越大,落点越接近假目标。
2. 接近计算出的落点时,两个目标都离开了导引头视野,不再计算落点。导弹会惯性飞行,会偏离之前计算出的落点。

View File

@ -93,8 +93,8 @@ namespace ThreatSource.Tools.MissileSimulation
// 激光驾束导弹
missileJammingMap["LBRM_1"] = new List<(JammingType, string, string, string, string)>
{
(JammingType.Laser, "激光干扰", "LaserJammer_Designator", "阻塞", "指示器"),
(JammingType.SmokeGrenade, "烟幕弹", "SG_1", "遮蔽", "两者")
(JammingType.Laser, "激光干扰", "LaserJammer_Designator", "阻塞", "驾束仪"),
(JammingType.SmokeGrenade, "烟幕弹", "SG_1", "遮蔽", "驾束仪")
};
// 激光半主动制导导弹
@ -124,7 +124,7 @@ namespace ThreatSource.Tools.MissileSimulation
missileJammingMap["MMWG_1"] = new List<(JammingType, string, string, string, string)>
{
(JammingType.MillimeterWave, "毫米波干扰", "MillimeterWaveJammer_Missile", "阻塞", "导弹"),
(JammingType.SmokeGrenade, "烟幕弹", "SG_1", "遮蔽", "导弹")
(JammingType.SmokeGrenade, "烟幕弹", "SG_3", "遮蔽", "导弹")
};
// 末敏子弹药
@ -134,7 +134,7 @@ namespace ThreatSource.Tools.MissileSimulation
(JammingType.Infrared, "红外干扰", "InfraredJammer_Submunition", "阻塞", "子弹药"),
(JammingType.MillimeterWave, "毫米波干扰", "MillimeterWaveJammer_Submunition", "阻塞", "子弹药"),
(JammingType.MillimeterWave, "毫米波假信号", "MillimeterWaveCompensationJammer_Submunition", "欺骗", "子弹药"),
(JammingType.SmokeGrenade, "烟幕弹", "SG_3", "遮蔽", "子弹药")
(JammingType.SmokeGrenade, "烟幕弹", "SG_4", "遮蔽", "子弹药")
};
}
@ -158,7 +158,7 @@ namespace ThreatSource.Tools.MissileSimulation
AddMillimeterWaveMissile();
// 添加各种传感器和指示器
AddDesignators();
AddIndicators();
// 添加烟幕弹
AddSmokeGrenade();
@ -193,7 +193,7 @@ namespace ThreatSource.Tools.MissileSimulation
{
Position = new Vector3D(0, 1.2, 0),
Orientation = new Orientation(Math.PI/2, 0.0, 0.0),
InitialSpeed = 0.0
InitialSpeed = 2.0
};
string targetId = "Tank_1";
var target = _threatSourceFactory.CreateTarget(targetId, "mbt_001", motionParameters);
@ -252,18 +252,27 @@ namespace ThreatSource.Tools.MissileSimulation
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
}
smokeGrenadeId = "SG_3";
smokeGrenade = _threatSourceFactory.CreateJammer(smokeGrenadeId, "mmw", motionParameters, "Tank_1");
if (smokeGrenade is BaseJammer jammer3)
{
simulationManager.RegisterEntity(smokeGrenadeId, jammer3);
jammers[smokeGrenadeId] = jammer3;
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
}
var motionParametersTop = new MotionParameters
{
Position = new Vector3D(0, 10, 0),
Orientation = Orientation.FromVector(Vector3D.UnitY),
InitialSpeed = 0.0
};
smokeGrenadeId = "SG_3";
smokeGrenadeId = "SG_4";
smokeGrenade = _threatSourceFactory.CreateJammer(smokeGrenadeId, "top", motionParametersTop, "Tank_1");
if (smokeGrenade is BaseJammer jammer3)
if (smokeGrenade is BaseJammer jammer4)
{
simulationManager.RegisterEntity(smokeGrenadeId, jammer3);
jammers[smokeGrenadeId] = jammer3;
simulationManager.RegisterEntity(smokeGrenadeId, jammer4);
jammers[smokeGrenadeId] = jammer4;
Console.WriteLine($"注册烟幕弹 {smokeGrenadeId}");
}
}
@ -275,8 +284,8 @@ namespace ThreatSource.Tools.MissileSimulation
{
var motionParameters = new MotionParameters
{
Position = new Vector3D(2000, 1, 20),
Orientation = new Orientation(Math.PI, 0.01, 0),
Position = new Vector3D(2000, 10, 20),
Orientation = new Orientation(Math.PI, -0.01, 0),
InitialSpeed = 700
};
string missileId = "LSGM_1";
@ -293,8 +302,8 @@ namespace ThreatSource.Tools.MissileSimulation
{
var motionParameters = new MotionParameters
{
Position = new Vector3D(2000, 1, 20),
Orientation = new Orientation(Math.PI, 0.01, 0.0),
Position = new Vector3D(2000, 10, 10),
Orientation = new Orientation(Math.PI, -0.01, 0.0),
InitialSpeed = 300
};
string missileId = "LBRM_1";
@ -382,7 +391,7 @@ namespace ThreatSource.Tools.MissileSimulation
/// <summary>
/// 添加传感器和指示器
/// </summary>
private void AddDesignators()
private void AddIndicators()
{
// 添加激光目标指示器
string laserDesignatorId = "LD_1";
@ -401,7 +410,7 @@ namespace ThreatSource.Tools.MissileSimulation
string laserBeamRiderId = "LBR_1";
var laserBeamRiderLaunchParams = new MotionParameters
{
Position = new Vector3D(2100, 1, 20),
Position = new Vector3D(2100, 1, 10),
Orientation = new Orientation(Math.PI, 0, 0),
InitialSpeed = 0
};