diff --git a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs index 928ee6b..965fd8b 100644 --- a/src/CounterDrone.Core/Algorithms/DefensePlanner.cs +++ b/src/CounterDrone.Core/Algorithms/DefensePlanner.cs @@ -366,13 +366,7 @@ namespace CounterDrone.Core.Algorithms if (dist > maxRange) return null; float shellTime = dist / unit.MuzzleVelocity; - - // 用 InterceptCalculator 验证可行性 + 获取发射角 - float speedKph = GetDroneSpeedKph(threat); - var (interceptArc, _, launchAngle) = InterceptCalculator.Compute( - threat.Waypoints, threat.DetectArc, speedKph, - _config.ReactionTime + expansionTime, unit.Position, unit.MuzzleVelocity); - if (interceptArc <= 0) return null; + if (!HasInterceptWindow(threat, midArc, expansionTime, shellTime)) return null; float interceptTime = threat.ArrivalTime; @@ -408,14 +402,6 @@ namespace CounterDrone.Core.Algorithms if (deliveryTime > interceptTime) return null; if (!HasInterceptWindow(threat, midArc, expansionTime, deliveryTime)) return null; - // 空基也用 InterceptCalculator 验证 - float speedKph = GetDroneSpeedKph(threat); - var (icArc, _, _) = InterceptCalculator.ComputeHorizontal( - threat.Waypoints, threat.DetectArc, speedKph, - _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; @@ -499,11 +485,23 @@ namespace CounterDrone.Core.Algorithms float typicalSpeed = GetDroneSpeedKph(threat); if (typicalSpeed <= 0) return (events, "目标速度无效"); - // 航路感知布局:穿越点弧长 = 航路中点弧长 + 沿航路偏移 - // targetOffset 现在是沿航路切向的弧长偏移(不再是 X 分量),支持任意方向航路 + // 用 InterceptCalculator 计算拦截弧长和发射角 var mid = ThreatMidpoint(threat); float midArc = RouteGeometry.ArcLengthNearestTo(wps, mid.X, mid.Z); - float crossArc = midArc + targetOffset; + float crossArc; + float launchAngle = 0; + if (unit.Type == PlatformType.GroundBased) + { + var (ia, _, la) = InterceptCalculator.Compute( + wps, threat.DetectArc, typicalSpeed, + _config.ReactionTime + expansionTime, unit.Position, unit.MuzzleVelocity); + crossArc = ia > 0 ? ia : midArc + targetOffset; + launchAngle = la; + } + else + { + crossArc = midArc + targetOffset; + } // 编队横向偏移:按车道分布在航路法向上,与 DroneEntity 编队偏移一致(起点展开)。 // DroneEntity: latOffset = formationIndex * lateralSpacing(0/50/100...) @@ -578,6 +576,7 @@ namespace CounterDrone.Core.Algorithms TargetY = ty, TargetZ = cloudGenZ, MuzzleVelocity = unit.Type == PlatformType.AirBased ? 0f : unit.MuzzleVelocity, + LaunchAngle = launchAngle, }); return (events, null); diff --git a/src/CounterDrone.Core/Simulation/MunitionEntity.cs b/src/CounterDrone.Core/Simulation/MunitionEntity.cs index 1f6dc25..75303b5 100644 --- a/src/CounterDrone.Core/Simulation/MunitionEntity.cs +++ b/src/CounterDrone.Core/Simulation/MunitionEntity.cs @@ -37,6 +37,7 @@ namespace CounterDrone.Core.Simulation float targetX, float targetY, float targetZ, float muzzleVelocity, float releaseAltitude, float launchTime, + float? launchAngle = null, float carrierVelX = 0f, float carrierVelY = 0f, float carrierVelZ = 0f) { Id = id; @@ -58,7 +59,7 @@ namespace CounterDrone.Core.Simulation var dz = targetZ - startZ; var horizontalDist = (float)System.Math.Sqrt(dx * dx + dz * dz); _azimuth = horizontalDist > 0.001f ? (float)System.Math.Atan2(dx, dz) : 0; - _launchAngle = Kinematics.CalculateLaunchAngle(horizontalDist, muzzleVelocity, releaseAltitude); + _launchAngle = launchAngle ?? Kinematics.CalculateLaunchAngle(horizontalDist, muzzleVelocity, releaseAltitude); _flightDuration = Kinematics.ParabolicTimeOfFlight(horizontalDist, _launchAngle, muzzleVelocity); } else diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index 8fb699d..1ddaa50 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -215,7 +215,7 @@ namespace CounterDrone.Core.Simulation platform.AerosolType ?? Models.AerosolType.InertGas, platform.PosX, platform.PosY, platform.PosZ, fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight, - fe.FireTime); + fe.FireTime, fe.LaunchAngle); _munitions.Add(m); OnMunitionLaunched?.Invoke(m); _frameEventPool.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"计划发射 {m.Id} @({platform.PosX:F0},{platform.PosY:F0},{platform.PosZ:F0})→({fe.TargetX:F0},{fe.TargetY:F0},{fe.TargetZ:F0})" });