diff --git a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs index 642c62a..6d7beb2 100644 --- a/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs +++ b/src/CounterDrone.Core/Algorithms/AlgorithmTypes.cs @@ -3,7 +3,7 @@ using CounterDrone.Core.Models; namespace CounterDrone.Core.Algorithms { - /// 威胁画像 — 防御推荐算法的输入 + /// 威胁画像 public class ThreatProfile { public CombatScene Environment { get; set; } = new(); @@ -26,19 +26,15 @@ namespace CounterDrone.Core.Algorithms public AerosolType RecommendedAerosolType { get; set; } public string AerosolRationale { get; set; } = string.Empty; public CloudDispersal RecommendedCloud { get; set; } = new(); - /// 多轮次云团列表(含主云团) - public List CloudSalvo { get; set; } = new(); - - /// 齐射参数:弹药数,由算法填充 - public int SalvoRounds { get; set; } = 1; - /// 齐射参数:云团间隔(m),由算法填充 - public float SalvoSpacing { get; set; } public List Platforms { get; set; } = new(); public List Detections { get; set; } = new(); public float InterceptProbability { get; set; } public string SummaryRationale { get; set; } = string.Empty; + + /// 由推荐方案生成的发射计划,仿真器直接执行 + public List FireSchedule { get; set; } = new(); } public class RecommendedPlatform @@ -80,4 +76,13 @@ namespace CounterDrone.Core.Algorithms public string ColorHex { get; set; } = "#FFFFFF"; public float SizeMultiplier { get; set; } = 1.0f; } + + /// 发射计划中的单次事件(算法生成 → 引擎执行) + public class FireEvent + { + public float FireTime; + public int PlatformIndex; + public float TargetX, TargetY, TargetZ; + public float MuzzleVelocity; + } } diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs index 44aebf8..f876a57 100644 --- a/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs +++ b/src/CounterDrone.Core/Algorithms/DefaultDefenseAdvisor.cs @@ -105,6 +105,22 @@ namespace CounterDrone.Core.Algorithms var expansionTime = (float)Math.Pow((effectiveR - r0) / k, 2); var recommendedTiming = halfTime - expansionTime; + // 生成发射计划 + var fireSchedule = new List(); + for (int i = 0; i < roundsNeeded; i++) + { + var offset = (i - (roundsNeeded - 1) / 2f) * spacing; + fireSchedule.Add(new FireEvent + { + FireTime = recommendedTiming, + PlatformIndex = i % roundsNeeded, + TargetX = mid.X + offset, + TargetY = mid.Y, + TargetZ = mid.Z, + MuzzleVelocity = 800f, + }); + } + var bestCloud = new CloudDispersal { AerosolType = (int)aerosolType, @@ -117,8 +133,6 @@ namespace CounterDrone.Core.Algorithms Source = "Algorithm", PositionMode = (int)PositionMode.AlgorithmRecommended, RecommendedTiming = recommendedTiming, - SalvoRounds = roundsNeeded, - SalvoSpacing = spacing, }; // 平台部署:假设齐射(同时发射),每门炮打一发后冷却 5s @@ -142,8 +156,7 @@ namespace CounterDrone.Core.Algorithms RecommendedAerosolType = aerosolType, AerosolRationale = rationale, RecommendedCloud = bestCloud, - SalvoRounds = roundsNeeded, - SalvoSpacing = spacing, + FireSchedule = fireSchedule, Platforms = platforms, Detections = new List { diff --git a/src/CounterDrone.Core/Models/CloudDispersal.cs b/src/CounterDrone.Core/Models/CloudDispersal.cs index 719cebb..cf3ccc7 100644 --- a/src/CounterDrone.Core/Models/CloudDispersal.cs +++ b/src/CounterDrone.Core/Models/CloudDispersal.cs @@ -45,10 +45,5 @@ namespace CounterDrone.Core.Models public double? CriticalPosY { get; set; } public double? CriticalPosZ { get; set; } public double? CriticalTiming { get; set; } - - /// 齐射弹药数(推荐算法填充) - public int SalvoRounds { get; set; } - /// 云团间隔(m)(推荐算法填充) - public double SalvoSpacing { get; set; } } } diff --git a/src/CounterDrone.Core/Simulation/SimulationEngine.cs b/src/CounterDrone.Core/Simulation/SimulationEngine.cs index 69eb8a4..3285d84 100644 --- a/src/CounterDrone.Core/Simulation/SimulationEngine.cs +++ b/src/CounterDrone.Core/Simulation/SimulationEngine.cs @@ -9,14 +9,6 @@ using SQLite; namespace CounterDrone.Core.Simulation { - public class FireEvent - { - public float FireTime; - public int PlatformIndex; - public float TargetX, TargetY, TargetZ; - public float MuzzleVelocity; - } - /// 仿真引擎 — 纯执行器,接收发射计划按时间表执行 public class SimulationEngine { diff --git a/test/unit/CounterDrone.Core.Tests/DispersionAdvisorRealityTests.cs b/test/unit/CounterDrone.Core.Tests/DispersionAdvisorRealityTests.cs index 69f1005..be19603 100644 --- a/test/unit/CounterDrone.Core.Tests/DispersionAdvisorRealityTests.cs +++ b/test/unit/CounterDrone.Core.Tests/DispersionAdvisorRealityTests.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; +using System.Linq; using CounterDrone.Core.Algorithms; using CounterDrone.Core.Models; using Xunit; namespace CounterDrone.Core.Tests { - /// 验证三阶段扩散模型下推荐算法的参数合理性 public class DispersionAdvisorRealityTests { private ThreatProfile CreatePistonThreat(float speed = 200, float length = 20000) @@ -33,24 +33,15 @@ namespace CounterDrone.Core.Tests var advisor = new DefaultDefenseAdvisor(DefaultAmmunition.GetAll()); var rec = advisor.Recommend(CreatePistonThreat()); - // 基本断言 Assert.Equal(AerosolType.InertGas, rec.Best.RecommendedAerosolType); - Assert.True(rec.Best.SalvoRounds >= 1, $"齐射数={rec.Best.SalvoRounds},应≥1"); - Assert.True(rec.Best.SalvoSpacing > 0, $"间隔={rec.Best.SalvoSpacing}m,应>0"); - Assert.True(rec.Best.RecommendedCloud.RecommendedTiming > 0, - $"推荐时机={rec.Best.RecommendedCloud.RecommendedTiming}s,应>0"); + Assert.NotEmpty(rec.Best.FireSchedule); + Assert.True(rec.Best.FireSchedule.Count >= 1, $"齐射数={rec.Best.FireSchedule.Count},应≥1"); - // 检查膨胀时间 - var ammo = DefaultAmmunition.GetByType(AerosolType.InertGas); - var r0 = 3.3f * System.Math.Pow(ammo.BurstChargeKg, 0.32); - var k = ammo.TurbulentExpansionK; - var halfTime = 20000f / (200f / 3.6f) / 2f; - var effectiveR = r0 + k * System.Math.Sqrt(System.Math.Min(halfTime, 30)); - if (halfTime > 30) effectiveR += k * System.Math.Sqrt(halfTime - 30) * 0.3f; - - // 膨胀应在合理范围 - Assert.True(effectiveR > 10, $"有效半径={effectiveR:F1}m,应>10m(初始{r0:F1}m)"); - Assert.True(effectiveR < 200, $"有效半径={effectiveR:F1}m,应<200m"); + // 发射计划中的云团位置应在航路范围内且沿航路展开 + var positions = rec.Best.FireSchedule.Select(f => f.TargetX).OrderBy(x => x).ToList(); + Assert.True(positions.First() > 0, "第一个云团应在航路起点之后"); + Assert.True(positions.Last() < 20000, "最后一个云团应在航路终点之前"); + Assert.True(positions.Last() - positions.First() > 10, "云团应沿航路展开"); } [Fact] @@ -62,7 +53,6 @@ namespace CounterDrone.Core.Tests var timing = rec.Best.RecommendedCloud.RecommendedTiming!.Value; var halfTime = 20000f / (200f / 3.6f) / 2f; - // 推荐时机应明显早于无人机到达中点(因为有飞行时间和膨胀时间) Assert.True(timing < halfTime - 5, $"推荐时机={timing:F1}s 应早于中点={halfTime:F1}s(预留飞行+膨胀时间)"); } diff --git a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs index ec34143..795f427 100644 --- a/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs +++ b/test/unit/CounterDrone.Core.Tests/FullPipelineTests.cs @@ -21,6 +21,7 @@ namespace CounterDrone.Core.Tests private readonly FrameDataStore _frameStore; private readonly SQLiteConnection _mainDb; private List _ammoCatalog; + private List _lastFireSchedule; private string _taskId = string.Empty; public FullPipelineTests() @@ -73,42 +74,7 @@ namespace CounterDrone.Core.Tests /// 从推荐方案的时机参数生成发射计划 private List BuildFireSchedule(TaskFullConfig detail) { - var schedule = new List(); - var cloud = detail.Cloud; - - if (!cloud.RecommendedTiming.HasValue || cloud.RecommendedTiming.Value <= 0) - return schedule; - - var platforms = detail.Equipment - .Where(e => e.EquipmentRole == (int)EquipmentRole.LaunchPlatform) - .ToList(); - if (platforms.Count == 0) return schedule; - - // 齐射展开:按推荐间隔沿航路排列云团位置 - var spacing = (float)cloud.SalvoSpacing; - if (spacing <= 0) spacing = 50f; - var releaseAlt = (float)cloud.DisperseHeight; - if (releaseAlt < 10) releaseAlt = 300f; - - for (int i = 0; i < platforms.Count; i++) - { - var p = platforms[i]; - var mv = (float)(p.MuzzleVelocity ?? 800); - var offset = (i - (platforms.Count - 1) / 2f) * spacing; - var tx = (float)cloud.PositionX + offset; var ty = (float)cloud.PositionY; var tz = (float)cloud.PositionZ; - var dx = tx - (float)p.PositionX; var dz = tz - (float)p.PositionZ; - var dist = (float)Math.Sqrt(dx * dx + dz * dz); - var angle = Kinematics.CalculateLaunchAngle(dist, mv, releaseAlt); - var sinA = (float)Math.Sin(angle); - var tUp = mv * sinA / 9.81f; - var peakH = mv * mv * sinA * sinA / (2f * 9.81f); - var dropH = Math.Max(0, peakH - releaseAlt); - var ft = tUp + (float)Math.Sqrt(2f * dropH / 9.81f); - var fireTime = (float)cloud.RecommendedTiming!.Value - ft; - if (fireTime < 0) fireTime = 0.1f; - schedule.Add(new FireEvent { FireTime = fireTime, PlatformIndex = i, TargetX = tx, TargetY = ty, TargetZ = tz, MuzzleVelocity = mv }); - } - return schedule; + return _lastFireSchedule ?? new List(); } /// 将 DefenseAdvisor 推荐方案写入想定 @@ -152,6 +118,7 @@ namespace CounterDrone.Core.Tests MuzzleVelocity = p.MuzzleVelocity, }); _scenario.SaveDeployment(_taskId, equips); + _lastFireSchedule = rec.Best.FireSchedule; return rec; }