diff --git a/CHANGELOG.md b/CHANGELOG.md index d4fbe26..f446d5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,7 +16,13 @@ - 多种发射弹道模式:低平弹道、高抛弹道、俯冲弹道 - 双模、多模制导 - Orientation 坐标系的调整(前向方向从 X 轴改为 Z 轴) -- 末敏弹各探测组件的干扰处理,增加毫米波补偿干扰 + +## [0.2.16] - 2025-05-09 +- 支持每个组件同时处理多种干扰 +- 把各组件的烟幕透过率计算移到 update 方法中 +- 完善了指示器的烟幕遮挡计算逻辑 +- 完善了激光半主动导弹的落点计算逻辑 +- 完善了配置文件格式 ## [0.2.15] - 2025-05-07 - 增加了毫米波补偿干扰器 diff --git a/ThreatSource/data/jammers/laser_jammers/laser_decoy.json b/ThreatSource/data/jammers/laser_jammers/laser_decoy.json index 3a8e3db..db440d4 100644 --- a/ThreatSource/data/jammers/laser_jammers/laser_decoy.json +++ b/ThreatSource/data/jammers/laser_jammers/laser_decoy.json @@ -6,7 +6,7 @@ "type": "LaserDecoy", "LaserDecoyConfig": { "Mode": "Deception", - "Power": 25.0, + "Power": 200.0, "Wavelength": 1.06, "DivergenceAngle": 0.001, "ReflectiveArea": 1.2, diff --git a/ThreatSource/data/jammers/smoke_grenades/mmw.json b/ThreatSource/data/jammers/smoke_grenades/mmw.json new file mode 100644 index 0000000..593cf94 --- /dev/null +++ b/ThreatSource/data/jammers/smoke_grenades/mmw.json @@ -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 + } +} \ No newline at end of file diff --git a/ThreatSource/data/missiles/ir_command/irc_001.json b/ThreatSource/data/missiles/ir_command/irc_001.json index e9858d5..e794396 100644 --- a/ThreatSource/data/missiles/ir_command/irc_001.json +++ b/ThreatSource/data/missiles/ir_command/irc_001.json @@ -5,6 +5,7 @@ }, "type": "InfraredCommandGuidance", "properties": { + "type": "InfraredCommandGuidance", "maxSpeed": 300.0, "maxFlightTime": 60.0, "maxFlightDistance": 5000.0, diff --git a/ThreatSource/data/missiles/ir_imaging/itg_001.json b/ThreatSource/data/missiles/ir_imaging/itg_001.json index 425d811..e700ae8 100644 --- a/ThreatSource/data/missiles/ir_imaging/itg_001.json +++ b/ThreatSource/data/missiles/ir_imaging/itg_001.json @@ -5,6 +5,7 @@ }, "type": "InfraredImagingTerminalGuidance", "properties": { + "type": "InfraredImagingTerminalGuidance", "maxSpeed": 250.0, "maxFlightTime": 60.0, "maxFlightDistance": 5000.0, diff --git a/ThreatSource/data/missiles/laser_beam_rider/hj10.json b/ThreatSource/data/missiles/laser_beam_rider/hj10.json index df2c24c..a8e363c 100644 --- a/ThreatSource/data/missiles/laser_beam_rider/hj10.json +++ b/ThreatSource/data/missiles/laser_beam_rider/hj10.json @@ -5,6 +5,7 @@ }, "type": "LaserBeamRiderGuidance", "properties": { + "type": "LaserBeamRiderGuidance", "maxSpeed": 300.0, "maxFlightTime": 60.0, "maxFlightDistance": 5000.0, diff --git a/ThreatSource/data/missiles/laser_beam_rider/kornet.json b/ThreatSource/data/missiles/laser_beam_rider/kornet.json index 2ff9740..8643d51 100644 --- a/ThreatSource/data/missiles/laser_beam_rider/kornet.json +++ b/ThreatSource/data/missiles/laser_beam_rider/kornet.json @@ -5,6 +5,7 @@ }, "type": "LaserBeamRiderGuidance", "properties": { + "type": "LaserBeamRiderGuidance", "maxSpeed": 300.0, "maxFlightTime": 70.0, "maxFlightDistance": 5500.0, diff --git a/ThreatSource/data/missiles/laser_beam_rider/tow.json b/ThreatSource/data/missiles/laser_beam_rider/tow.json index 3c4f39f..fc33378 100644 --- a/ThreatSource/data/missiles/laser_beam_rider/tow.json +++ b/ThreatSource/data/missiles/laser_beam_rider/tow.json @@ -5,6 +5,7 @@ }, "type": "LaserBeamRiderGuidance", "properties": { + "type": "LaserBeamRiderGuidance", "maxSpeed": 278.0, "maxFlightTime": 65.0, "maxFlightDistance": 4500.0, diff --git a/ThreatSource/data/missiles/laser_semi_active/lsgm_001.json b/ThreatSource/data/missiles/laser_semi_active/lsgm_001.json index 50e7f4a..c825f79 100644 --- a/ThreatSource/data/missiles/laser_semi_active/lsgm_001.json +++ b/ThreatSource/data/missiles/laser_semi_active/lsgm_001.json @@ -5,6 +5,7 @@ }, "type": "LaserSemiActiveGuidance", "properties": { + "type": "LaserSemiActiveGuidance", "maxSpeed": 800.0, "maxFlightTime": 60.0, "maxFlightDistance": 4000.0, diff --git a/ThreatSource/data/missiles/mmw/mmw_001.json b/ThreatSource/data/missiles/mmw/mmw_001.json index b4bf693..74db66d 100644 --- a/ThreatSource/data/missiles/mmw/mmw_001.json +++ b/ThreatSource/data/missiles/mmw/mmw_001.json @@ -5,6 +5,7 @@ }, "type": "MillimeterWaveTerminalGuidance", "properties": { + "type": "MillimeterWaveTerminalGuidance", "maxSpeed": 250.0, "maxFlightTime": 60.0, "maxFlightDistance": 8000.0, diff --git a/ThreatSource/data/missiles/terminal_sensitive/tsm_001.json b/ThreatSource/data/missiles/terminal_sensitive/tsm_001.json index 14639d5..90e1f70 100644 --- a/ThreatSource/data/missiles/terminal_sensitive/tsm_001.json +++ b/ThreatSource/data/missiles/terminal_sensitive/tsm_001.json @@ -5,6 +5,7 @@ }, "type": "TerminalSensitiveMissile", "properties": { + "type": "TerminalSensitiveMissile", "maxSpeed": 1000.0, "maxFlightTime": 100.0, "maxFlightDistance": 5000.0, diff --git a/ThreatSource/data/targets/apcs/apc_001.json b/ThreatSource/data/targets/apcs/apc_001.json index 506011b..1719051 100644 --- a/ThreatSource/data/targets/apcs/apc_001.json +++ b/ThreatSource/data/targets/apcs/apc_001.json @@ -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] + ] + } } } \ No newline at end of file diff --git a/ThreatSource/data/targets/helicopters/heli_001.json b/ThreatSource/data/targets/helicopters/heli_001.json index c3c6b97..a1c6a96 100644 --- a/ThreatSource/data/targets/helicopters/heli_001.json +++ b/ThreatSource/data/targets/helicopters/heli_001.json @@ -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] + ] + } } } \ No newline at end of file diff --git a/ThreatSource/data/targets/tanks/mbt_001.json b/ThreatSource/data/targets/tanks/mbt_001.json index 2037002..0e2981d 100644 --- a/ThreatSource/data/targets/tanks/mbt_001.json +++ b/ThreatSource/data/targets/tanks/mbt_001.json @@ -5,6 +5,7 @@ }, "type": "Tank", "properties": { + "type": "Tank", "mass": 50000.0, "length": 10.0, "width": 3.5, diff --git a/ThreatSource/src/Guidance/BaseGuidanceSystem.cs b/ThreatSource/src/Guidance/BaseGuidanceSystem.cs index 26cbd2e..221fb38 100644 --- a/ThreatSource/src/Guidance/BaseGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/BaseGuidanceSystem.cs @@ -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 /// protected void InitializeJamming(double jammingResistanceThreshold, IEnumerable supportedTypes, IEnumerable supportedBlockingTypes) { - _jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedTypes); + _jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedBlockingTypes); foreach (var type in supportedTypes) { _jammingComponent.AddSupportedJammingType(type); diff --git a/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs b/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs index aafeb8e..0f5f77a 100644 --- a/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/InfraredImagingGuidanceSystem.cs @@ -102,12 +102,6 @@ namespace ThreatSource.Guidance /// private double lockConfirmationTimer = 0; - /// - /// 烟幕衰减 - /// - private double SmokeAttenuation { get; set; } = 1.0; - - /// /// 初始化红外成像制导系统的新实例 /// @@ -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; } /// @@ -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; // 没有烟幕能有效遮挡目标 } + /// + /// 计算给定观察点和目标点之间的总烟幕透过率 + /// + /// 观察者位置 + /// 目标位置 + /// 总透过率 (0.0 到 1.0) + private double CalculateLiveSmokeTransmittance(Vector3D observerPosition, Vector3D targetEndPosition) + { + double totalTransmittance = 1.0; + var activeSmokeGrenades = SimulationManager.GetEntitiesByType() + .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); //确保不为负 + } + /// /// 获取红外图像制导系统的详细状态信息 /// diff --git a/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs b/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs index ec8859f..1fa6fa7 100644 --- a/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/LaserBeamRiderGuidanceSystem.cs @@ -349,7 +349,7 @@ namespace ThreatSource.Guidance public override void Update(double deltaTime) { base.Update(deltaTime); - if (!IsJammed) + if (!IsBlockingJammed) { if (LaserIlluminationOn) { diff --git a/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs b/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs index 372ddc9..47da9f4 100644 --- a/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/LaserSemiActiveGuidanceSystem.cs @@ -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 /// private const double AccelerationSmoothingFactor = 0.5; - /// - /// 烟幕衰减 - /// - private double SmokeAttenuation { get; set; } = 1.0; - /// /// 激光目标列表,包括真实目标和诱偏目标 /// @@ -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 /// 接收到的激光功率,单位:瓦特 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 } /// - /// 计算烟幕对激光的衰减因子 + /// 计算给定路径上的总烟幕透过率 /// - /// 烟幕干扰参数 - /// 衰减因子,范围:0-1,0表示完全衰减,1表示无衰减 - private double CalculateSmokeAttenuation(JammingParameters parameters) + /// 路径起点 + /// 路径终点 + /// 总透过率 (0.0 到 1.0) + private double CalculateLiveSmokeTransmittanceForPath(Vector3D pathStart, Vector3D pathEnd) { - if (!parameters.SmokeConcentration.HasValue) - return 1.0; // 无衰减 + double totalTransmittance = 1.0; + var activeSmokeGrenades = SimulationManager.GetEntitiesByType() + .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); //确保不为负 } } } diff --git a/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs b/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs index 1d39268..c36f18b 100644 --- a/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs +++ b/ThreatSource/src/Guidance/MillimeterWaveGuidanceSystem.cs @@ -66,7 +66,7 @@ namespace ThreatSource.Guidance /// 记录目标的历史位置 /// 用于计算目标速度 /// - private Vector3D? lastTargetPosition { get; set; } + private Vector3D? LastTargetPosition { get; set; } /// /// 上一次探测到的目标速度 (使用可空类型) @@ -75,7 +75,7 @@ namespace ThreatSource.Guidance /// 记录目标的历史速度 /// 用于计算目标速度 /// - private Vector3D? lastTargetVelocity { get; set; } + private Vector3D? LastTargetVelocity { get; set; } /// /// 目标丢失计时器 @@ -125,11 +125,7 @@ namespace ThreatSource.Guidance /// private double maxScanRadius => config.FieldOfViewAngle * Math.PI / 180.0 / 2; - /// - /// 当前视线的烟幕透过率 (0.0 - 1.0) - /// - private double _currentSmokeTransmittance = 1.0; - + private const double SpeedOfLight = 299792458.0; // m/s /// /// 初始化毫米波制导系统的新实例 @@ -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; } /// @@ -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()) { - 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 /// /// 到目标的距离,单位:米 /// 目标雷达散射截面积,单位:平方米 + /// 当前视线的烟幕总线性透过率 (0.0 到 1.0) /// 信噪比,单位:分贝 /// /// 计算过程: @@ -698,53 +689,116 @@ namespace ThreatSource.Guidance /// - 计算噪声功率 /// - 计算信噪比 /// - 转换为分贝值 + /// - 应用烟幕衰减 /// - 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; + } + + /// + /// 计算给定观察点和目标点之间的总烟幕透过率。 + /// + /// 观察者位置 + /// 目标位置 + /// 总透过率 (0.0 到 1.0)。 + 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() + .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); } } } diff --git a/ThreatSource/src/Indicator/BaseIndicator.cs b/ThreatSource/src/Indicator/BaseIndicator.cs index f39a2e5..442e2f4 100644 --- a/ThreatSource/src/Indicator/BaseIndicator.cs +++ b/ThreatSource/src/Indicator/BaseIndicator.cs @@ -158,18 +158,7 @@ namespace ThreatSource.Indicator /// 干扰参数 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"); } /// @@ -178,44 +167,31 @@ namespace ThreatSource.Indicator /// 干扰参数 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"); } /// - /// 激活指示器 (基类统一处理干扰事件订阅) + /// 激活指示器 /// public override void Activate() { if (!IsActive) { IsActive = true; - // 统一订阅 JammingEvent SimulationManager.SubscribeToEvent(HandleJammingEvent); SimulationManager.SubscribeToEvent(HandleJammingStoppedEvent); } base.Activate(); - RecalculateObscurationStatus(); // 激活时检查一次初始遮挡状态 } /// - /// 停用指示器 (基类统一处理干扰事件取消订阅) + /// 停用指示器 /// public override void Deactivate() { if (IsActive) { IsActive = false; - // 统一取消订阅 JammingEvent SimulationManager.UnsubscribeFromEvent(HandleJammingEvent); SimulationManager.UnsubscribeFromEvent(HandleJammingStoppedEvent); } @@ -231,15 +207,18 @@ namespace ThreatSource.Indicator /// 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() - .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; // 未被遮挡 } - // --- 新增干扰事件处理逻辑 --- - /// - /// 统一处理干扰事件 + /// 处理干扰事件 /// /// 干扰事件 protected virtual void HandleJammingEvent(JammingEvent evt) { - Console.WriteLine($"[BaseIndicator] 处理干扰事件: {evt.Parameters.Type}"); if (evt == null) return; // 在应用干扰前检查是否应该处理此干扰 @@ -416,8 +396,12 @@ namespace ThreatSource.Indicator /// 如果应该处理则返回 true,否则返回 false protected virtual bool ShouldHandleJamming(JammingParameters parameters) { - // 子类可以重写此方法添加额外检查,如波段匹配 - // 注意:烟幕干扰虽然通过这里,但主要效果通过 IsTargetObscured 和 RecalculateObscurationStatus 体现 + // 首先检查当前指示器实例(通过其具体的 SupportedJammingTypes 实现)是否支持此干扰类型 + if (!this.SupportedJammingTypes.Contains(parameters.Type)) + { + return false; + } + // 如果类型受支持,则允许子类进行更具体的检查 return true; } } diff --git a/ThreatSource/src/Indicator/InfraredTracker.cs b/ThreatSource/src/Indicator/InfraredTracker.cs index a85aebc..c1630ad 100644 --- a/ThreatSource/src/Indicator/InfraredTracker.cs +++ b/ThreatSource/src/Indicator/InfraredTracker.cs @@ -90,22 +90,11 @@ namespace ThreatSource.Indicator /// 时间步长,单位:秒 /// /// 实现红外测角仪特定的更新逻辑: - /// - 基类 Update 会检查 IsJammed (电子干扰) /// - UpdateTracking 方法内部会处理 IsTargetObscured (烟幕遮挡) /// - 更新跟踪状态 /// 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 /// 如果应该处理则返回 true,否则返回 false 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; } diff --git a/ThreatSource/src/Indicator/LaserBeamRider.cs b/ThreatSource/src/Indicator/LaserBeamRider.cs index 27bfacf..240625a 100644 --- a/ThreatSource/src/Indicator/LaserBeamRider.cs +++ b/ThreatSource/src/Indicator/LaserBeamRider.cs @@ -199,7 +199,7 @@ namespace ThreatSource.Indicator /// 如果应该处理则返回 true,否则返回 false 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(); } diff --git a/ThreatSource/src/Indicator/LaserDesignator.cs b/ThreatSource/src/Indicator/LaserDesignator.cs index 5418cac..5d8bfde 100644 --- a/ThreatSource/src/Indicator/LaserDesignator.cs +++ b/ThreatSource/src/Indicator/LaserDesignator.cs @@ -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 /// 如果应该处理则返回 true,否则返回 false 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; } diff --git a/ThreatSource/src/Jammable/JammableComponent.cs b/ThreatSource/src/Jammable/JammableComponent.cs index bc45d1b..0d41115 100644 --- a/ThreatSource/src/Jammable/JammableComponent.cs +++ b/ThreatSource/src/Jammable/JammableComponent.cs @@ -171,75 +171,104 @@ namespace ThreatSource.Jammable /// 如果干扰有效返回true,否则返回false 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 /// /// 处理内部 JammingHandler 的 JammingCleared 事件 /// - private void HandleInternalJammingCleared(JammingParameters? parameters) + private void HandleInternalJammingCleared(JammingParameters parameters) { - // 触发公共事件,如果可用,则提供类型 - if (parameters != null) - { - JammingCleared?.Invoke(parameters); - } + // 触发公共事件 + JammingCleared?.Invoke(parameters); } /// diff --git a/ThreatSource/src/Jammable/JammingHandler.cs b/ThreatSource/src/Jammable/JammingHandler.cs index 4149515..54fc220 100644 --- a/ThreatSource/src/Jammable/JammingHandler.cs +++ b/ThreatSource/src/Jammable/JammingHandler.cs @@ -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 /// public class JammingHandler { + /// + /// 内部类,用于管理单个活动干扰的状态 + /// + 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; + } + } + + /// + /// 当前所有激活的干扰状态列表 + /// + private readonly List _activeJammingStates = new List(); + /// /// 是否处于被干扰状态 /// - public bool IsJammed { get; private set; } + public bool IsJammed => _activeJammingStates.Any(); /// /// 获取当前是否正受到有效的阻塞式干扰 /// - public bool IsBlockingJammed - { - get - { - // 如果没有被干扰,或者当前没有干扰参数,则肯定不是阻塞干扰 - // 否则,检查当前干扰的模式是否为阻塞 - return IsJammed && CurrentJamming != null && CurrentJamming.Mode == JammingMode.Blocking; - } - } + public bool IsBlockingJammed => _activeJammingStates.Any(state => state.Parameters.Mode == JammingMode.Blocking); /// /// 当前干扰参数 /// protected JammingParameters? CurrentJamming { get; private set; } - /// - /// 累积的干扰时间 - /// - private double _elapsedTime; - /// /// 当干扰被应用时触发 /// @@ -53,13 +61,20 @@ namespace ThreatSource.Jammable /// 时间步长,单位:秒 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 /// 干扰参数 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); } /// - /// 清除干扰 + /// 清除与指定参数匹配的干扰 /// - public virtual void ClearJamming(JammingParameters parameters) + /// 用于匹配要清除的干扰的参数。 + /// 通常会基于 JammerId 和 Type 进行匹配。 + 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 /// 匹配的活动干扰源ID枚举;若无则为空枚举 public IEnumerable 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(); + } + + /// + /// 获取当前所有活动的干扰参数 + /// + /// 当前所有活动干扰参数的枚举 + public IEnumerable GetAllActiveJammings() + { + return _activeJammingStates.Select(s => s.Parameters); } } } \ No newline at end of file diff --git a/ThreatSource/src/Jammer/SmokeGrenade.cs b/ThreatSource/src/Jammer/SmokeGrenade.cs index d090fdf..d12a282 100644 --- a/ThreatSource/src/Jammer/SmokeGrenade.cs +++ b/ThreatSource/src/Jammer/SmokeGrenade.cs @@ -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})"); } diff --git a/ThreatSource/src/MIssile/BaseMissile.cs b/ThreatSource/src/MIssile/BaseMissile.cs index 63ccd2c..2c09a3d 100644 --- a/ThreatSource/src/MIssile/BaseMissile.cs +++ b/ThreatSource/src/MIssile/BaseMissile.cs @@ -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; } diff --git a/ThreatSource/src/Sensor/BaseSensor.cs b/ThreatSource/src/Sensor/BaseSensor.cs index 247ab78..3d2009a 100644 --- a/ThreatSource/src/Sensor/BaseSensor.cs +++ b/ThreatSource/src/Sensor/BaseSensor.cs @@ -185,7 +185,7 @@ namespace ThreatSource.Sensor } /// - /// 统一处理干扰事件 + /// 处理干扰事件 /// /// 干扰事件 protected virtual void HandleJammingEvent(JammingEvent evt) @@ -201,7 +201,7 @@ namespace ThreatSource.Sensor } /// - /// 统一处理干扰停止事件 + /// 处理干扰停止事件 /// /// 干扰停止事件 protected virtual void HandleJammingStoppedEvent(JammingStoppedEvent evt) @@ -271,7 +271,7 @@ namespace ThreatSource.Sensor if (!IsActive) { IsActive = true; - // 统一订阅 HandleJammingEvent + // 订阅 HandleJammingEvent SimulationManager.SubscribeToEvent(HandleJammingEvent); SimulationManager.SubscribeToEvent(HandleJammingStoppedEvent); } @@ -286,7 +286,7 @@ namespace ThreatSource.Sensor if (IsActive) { IsActive = false; - // 统一取消订阅 HandleJammingEvent + // 取消订阅 HandleJammingEvent SimulationManager.UnsubscribeFromEvent(HandleJammingEvent); SimulationManager.UnsubscribeFromEvent(HandleJammingStoppedEvent); } diff --git a/ThreatSource/src/Sensor/MillimeterWaveRadiometer.cs b/ThreatSource/src/Sensor/MillimeterWaveRadiometer.cs index 0897570..d259001 100644 --- a/ThreatSource/src/Sensor/MillimeterWaveRadiometer.cs +++ b/ThreatSource/src/Sensor/MillimeterWaveRadiometer.cs @@ -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; // 如果仍被干扰,保持无效 - } } } } diff --git a/VERSION b/VERSION index 797eef9..d84abc1 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.2.15 \ No newline at end of file +0.2.16 \ No newline at end of file diff --git a/docs/project/develop_log.md b/docs/project/develop_log.md index 6663ce1..a5a01b3 100644 --- a/docs/project/develop_log.md +++ b/docs/project/develop_log.md @@ -7,6 +7,13 @@ - 事件描述 - 分析处理 +## 2025-05-09 完善了各组件的干扰功能 +- 把各组件的烟幕透过率计算移到 update 方法中 +- 完善了指示器的烟幕遮挡计算逻辑 +- 完善了激光半主动导弹的落点计算逻辑 +- 完善了配置文件格式 +- 集成测试的正常运行,坦克、指示器、导弹的初始化运动参数关联很大,修改了可能会影响很多测试场景。 + ## 2025-05-07 完善了末敏弹各传感器的干扰功能 - 增加了烟幕弹对末敏弹的干扰功能 diff --git a/docs/project/tunning.md b/docs/project/tunning.md index b94fd50..afe09cf 100644 --- a/docs/project/tunning.md +++ b/docs/project/tunning.md @@ -263,4 +263,45 @@ [LaserDesignator] LD_1 受到激光干扰,停止照射。 ``` -实验结论:当激光干扰器的发射功率是 2000W 时,距离 2000米时,如果激光指示器或导弹的阻塞干扰阈值是 1e-5W,干扰效果是有效的。 \ No newline at end of file +实验结论:当激光干扰器的发射功率是 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. 接近计算出的落点时,两个目标都离开了导引头视野,不再计算落点。导弹会惯性飞行,会偏离之前计算出的落点。 + + + diff --git a/tools/ComprehensiveMissileSimulator.cs b/tools/ComprehensiveMissileSimulator.cs index 3179f87..1c93af5 100644 --- a/tools/ComprehensiveMissileSimulator.cs +++ b/tools/ComprehensiveMissileSimulator.cs @@ -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 /// /// 添加传感器和指示器 /// - 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 };