diff --git a/src/CounterDrone.Core/Algorithms/CloudExpansionModel.cs b/src/CounterDrone.Core/Algorithms/CloudExpansionModel.cs
index b35c34e..86aa15a 100644
--- a/src/CounterDrone.Core/Algorithms/CloudExpansionModel.cs
+++ b/src/CounterDrone.Core/Algorithms/CloudExpansionModel.cs
@@ -49,6 +49,26 @@ namespace CounterDrone.Core.Algorithms
return volume > 0.001f ? (float)_ammo.SourceStrength / volume : (float)_ammo.CoreDensity;
}
+ /// 达到指定浓度所需时间(密度随膨胀下降,较晚时间密度更低)
+ /// 浓度首次低于threshold的时间(s),如果不会低于则返回MaxDuration
+ public float TimeToDensity(float threshold)
+ {
+ // 密度 ≈ SourceStrength / (4/3 π R³),随R增大而下降
+ // 求R使得密度=threshold: R³ = SourceStrength / (threshold * 4π/3)
+ float src = (float)_ammo.SourceStrength;
+ float volume = src / Math.Max(threshold, 0.000001f);
+ float r = (float)Math.Pow(volume * 3f / (4f * (float)Math.PI), 1f / 3f);
+ return TimeToReach(r);
+ }
+
+ /// 覆盖指定距离需要的云团数量(间距=2R)
+ public int RoundsNeeded(float requiredCoverage)
+ {
+ float R = RadiusAt(30f);
+ float spacing = 2f * R;
+ return Math.Max(1, (int)Math.Ceiling(requiredCoverage / spacing));
+ }
+
/// 达到指定半径所需时间 (s)
public float TimeToReach(float radius)
{
diff --git a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs
index 41d3861..cfcdcff 100644
--- a/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs
+++ b/src/CounterDrone.Core/Algorithms/DefaultDefensePlanner.cs
@@ -361,16 +361,12 @@ namespace CounterDrone.Core.Algorithms
private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec ammo,
CombatScene env, bool isAirBased, float turbulentRadius)
{
+ var cloudModel = new CloudExpansionModel(ammo, env);
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
- float spacing = 2f * turbulentRadius;
-
var aerosolType = (AerosolType)ammo.AerosolType;
float neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
- float singleCoverage = neededExposure * avgSpeed;
- float requiredCoverage = singleCoverage;
-
- return Math.Max(1,
- (int)Math.Ceiling(requiredCoverage / spacing));
+ float requiredCoverage = neededExposure * avgSpeed;
+ return cloudModel.RoundsNeeded(requiredCoverage);
}
private float ComputeInterceptProbability(DroneGroup threat,
@@ -399,7 +395,8 @@ namespace CounterDrone.Core.Algorithms
var events = new List();
var mid = ThreatMidpoint(threat);
- var (effectiveR, expansionTime, _) = ComputeEffectiveRadius(threat, ammo, env);
+ var cloudModel = new CloudExpansionModel(ammo, env);
+ float expansionTime = cloudModel.TimeToReach(cloudModel.RadiusAt(30f));
// 编队 Y 偏移:按车道分布
float laneSpacingZ = yLanes > 1 ? formationWidth / (yLanes - 1) : 0f;
@@ -407,7 +404,7 @@ namespace CounterDrone.Core.Algorithms
float tx = mid.X + targetOffset;
- // 按目标点位置计算无人机到达时间,非中点
+ // 无人机到达目标点时间
float droneSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
if (droneSpeed <= 0) return events;
float startX = (float)threat.Waypoints[0].PosX;
@@ -420,7 +417,6 @@ namespace CounterDrone.Core.Algorithms
distToTx = Math.Max(0, Math.Min(totalDist, distToTx));
float txArrival = distToTx / droneSpeed;
float recommendedTiming = txArrival - expansionTime;
- // 来不及:无人机到达时云团未形成 → 跳过此目标点
if (recommendedTiming <= 0f) return events;
float fireTime;