diff --git a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs index 965fd8b..af6f275 100644 --- a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs @@ -402,6 +402,12 @@ namespace CounterDrone.Core.Algorithms if (deliveryTime > interceptTime) return null; if (!HasInterceptWindow(threat, midArc, expansionTime, deliveryTime)) return null; + var (icArc, _, _) = InterceptCalculator.ComputeHorizontal( + threat.Waypoints, threat.DetectArc, GetDroneSpeedKph(threat), + _config.ReactionTime + expansionTime, unit.Position, unit.CruiseSpeed, + unit.ReleaseAltitude, (float)threat.Target.TypicalAltitude); + if (icArc <= 0) return null; + float avgSpeed = GetDroneSpeedMs(threat); float neededExposure = _damageModel.RequiredExposureSeconds((TargetType)threat.Target.TargetType, (PowerType)threat.Target.PowerType, ammoType); float actualExposure = effectiveR * 2f / avgSpeed; @@ -495,12 +501,17 @@ namespace CounterDrone.Core.Algorithms var (ia, _, la) = InterceptCalculator.Compute( wps, threat.DetectArc, typicalSpeed, _config.ReactionTime + expansionTime, unit.Position, unit.MuzzleVelocity); - crossArc = ia > 0 ? ia : midArc + targetOffset; + crossArc = (ia > 0 ? ia : midArc) + targetOffset; launchAngle = la; } else { - crossArc = midArc + targetOffset; + var (ia, _, la) = InterceptCalculator.ComputeHorizontal( + wps, threat.DetectArc, typicalSpeed, + _config.ReactionTime + expansionTime, unit.Position, unit.CruiseSpeed, + unit.ReleaseAltitude, (float)threat.Target.TypicalAltitude); + crossArc = (ia > 0 ? ia : midArc) + targetOffset; + launchAngle = la; } // 编队横向偏移:按车道分布在航路法向上,与 DroneEntity 编队偏移一致(起点展开)。 diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index 31cba87..45be707 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -152,8 +152,9 @@ namespace CounterDrone.Core.Tests Assert.All(eng.Events.Where(e => e.Type == SimEventType.MunitionLaunched), e => Assert.Contains("空基", e.Description)); var drone = eng.Drones[0]; - Assert.True(eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched) > 0); - Assert.True(drone.Hp < 0.1f); + var detail = _scenario.GetTaskDetail(_taskId)!; + var report = new ReportGenerator().Generate(detail, eng.Events.ToList(), drone.Status, eng.SimulationTime); + Assert.True(drone.Hp < 0.1f, $"Hp={drone.Hp:F3} pos=({drone.PosX:F0},{drone.PosY:F0}) launched={eng.Events.Count(e => e.Type == SimEventType.MunitionLaunched)}\n{report}"); VerifyAndExportReport(eng, drone); }