fix: spacing=2R消除魔法数字,turbulentRadius贯穿CalcRoundsNeeded
- ComputeEffectiveRadius返回(getEffectiveRadius, expansionTime, turbulentRadius) - CalcRoundsNeeded/tagger均用turbulentRadius - spacing=2R(云端直径=中心距),消除1.5f经验系数 - FireEvent.OccurredAt存储fe.FireTime精确发射时刻
This commit is contained in:
parent
6cd18bf142
commit
09f51939d8
@ -131,7 +131,8 @@ namespace CounterDrone.Core.Algorithms
|
||||
var ammo = _ammoCatalog.First(a => a.AerosolType == (int)neededAmmo);
|
||||
|
||||
// 单机需求
|
||||
int singleNeeded = CalcRoundsNeeded(threat, ammo, env, false);
|
||||
var (effectiveRadius, expansionTime, turbulentRadius) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
int singleNeeded = CalcRoundsNeeded(threat, ammo, env, false, turbulentRadius);
|
||||
|
||||
// 横向编队:每种 Width = Quantity 个独立车道
|
||||
int yLanes = 1;
|
||||
@ -144,8 +145,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
int totalRoundsNeeded = singleNeeded * yLanes;
|
||||
|
||||
// 逐单元分配:每个单元锁定一个 Y 车道
|
||||
var (effR2, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
float spacing = effR2 * 1.5f;
|
||||
float spacing = 2f * turbulentRadius;
|
||||
int[] laneNeeded = new int[yLanes];
|
||||
for (int l = 0; l < yLanes; l++) laneNeeded[l] = singleNeeded;
|
||||
int currentLane = 0;
|
||||
@ -169,7 +169,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
int yLane = currentLane;
|
||||
var refEvt = GenerateFireEventsAt(threat, c.Unit, c.AmmoType, ammo, env, 0, yLane, yLanes, formationWidth);
|
||||
float unitBaseTime = refEvt[0].FireTime;
|
||||
float stagger = Kinematics.CloudCoverInterval(effR2 * 2f, (float)threat.Target.TypicalSpeed, c.Unit.ChannelInterval);
|
||||
float stagger = Kinematics.CloudCoverInterval(turbulentRadius * 2f, (float)threat.Target.TypicalSpeed, c.Unit.ChannelInterval);
|
||||
|
||||
var fevents = new List<FireEvent>();
|
||||
int unitIdx = fireUnits.IndexOf(c.Unit);
|
||||
@ -245,7 +245,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
var neededAmmo = MatchAmmo((PowerType)threat.Target.PowerType);
|
||||
var ammo = _ammoCatalog.First(a => a.AerosolType == (int)neededAmmo);
|
||||
|
||||
var ammoEff = ComputeEffectiveRadius(threat, ammo, env);
|
||||
var (ammoEff, _, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
|
||||
foreach (var unit in availableUnits)
|
||||
{
|
||||
@ -268,7 +268,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
private InterceptCandidate? BuildGroundBasedCandidate(DroneGroup threat,
|
||||
FireUnit unit, AerosolType ammoType, AmmunitionSpec ammo,
|
||||
(float radius, float expTime) ammoEff, CombatScene env)
|
||||
float effectiveR, CombatScene env)
|
||||
{
|
||||
var mid = ThreatMidpoint(threat);
|
||||
float dx = mid.X - unit.Position.X;
|
||||
@ -284,7 +284,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
|
||||
float neededExposure = ammoType == AerosolType.ActiveMaterial ? 2f : 6f;
|
||||
float actualExposure = ammoEff.radius * 2f / avgSpeed;
|
||||
float actualExposure = effectiveR * 2f / avgSpeed;
|
||||
float prob = Math.Min(0.95f, actualExposure / neededExposure);
|
||||
|
||||
return new InterceptCandidate
|
||||
@ -292,7 +292,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
Unit = unit,
|
||||
AmmoType = ammoType,
|
||||
EarliestInterceptTime = interceptTime,
|
||||
CoverageDuration = ammoEff.radius * 2f,
|
||||
CoverageDuration = effectiveR * 2f,
|
||||
KillProbability = prob,
|
||||
FlightTime = shellTime,
|
||||
ShellFlightTime = shellTime,
|
||||
@ -301,7 +301,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
private InterceptCandidate? BuildAirBasedCandidate(DroneGroup threat,
|
||||
FireUnit unit, AerosolType ammoType, AmmunitionSpec ammo,
|
||||
(float radius, float expTime) ammoEff, CombatScene env)
|
||||
float effectiveR, CombatScene env)
|
||||
{
|
||||
if (unit.ReleaseAltitude <= 0 || unit.CruiseSpeed <= 0) return null;
|
||||
var mid = ThreatMidpoint(threat);
|
||||
@ -318,7 +318,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
|
||||
float neededExposure = ammoType == AerosolType.ActiveMaterial ? 2f : 6f;
|
||||
float actualExposure = ammoEff.radius * 2f / avgSpeed;
|
||||
float actualExposure = effectiveR * 2f / avgSpeed;
|
||||
float prob = Math.Min(0.95f, actualExposure / neededExposure);
|
||||
|
||||
return new InterceptCandidate
|
||||
@ -326,7 +326,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
Unit = unit,
|
||||
AmmoType = ammoType,
|
||||
EarliestInterceptTime = interceptTime,
|
||||
CoverageDuration = ammoEff.radius * 2f,
|
||||
CoverageDuration = effectiveR * 2f,
|
||||
KillProbability = prob,
|
||||
FlightTime = totalTime,
|
||||
ShellFlightTime = fallTime,
|
||||
@ -337,7 +337,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
// 弹药计算(使用 AmmunitionSpec + 环境参数)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
private (float effectiveRadius, float expansionTime) ComputeEffectiveRadius(
|
||||
private (float effectiveRadius, float expansionTime, float rPhase2) ComputeEffectiveRadius(
|
||||
DroneGroup threat, AmmunitionSpec ammo, CombatScene env)
|
||||
{
|
||||
float totalFlightTime = threat.ArrivalTime * 2f;
|
||||
@ -353,10 +353,10 @@ namespace CounterDrone.Core.Algorithms
|
||||
|
||||
// Phase 2 湍流膨胀后的半径
|
||||
float phase2Time = Math.Min(halfTime, 30f);
|
||||
float rPhase2 = r0 + k * (float)Math.Sqrt(Math.Max(0, phase2Time));
|
||||
float turbulentRadius = r0 + k * (float)Math.Sqrt(Math.Max(0, phase2Time));
|
||||
|
||||
float expansionTime = (float)Math.Pow((rPhase2 - r0) / Math.Max(k, 0.01f), 2);
|
||||
float effectiveR = rPhase2;
|
||||
float expansionTime = (float)Math.Pow((turbulentRadius - r0) / Math.Max(k, 0.01f), 2);
|
||||
float effectiveR = turbulentRadius;
|
||||
|
||||
// Phase 3 高斯扩散
|
||||
if (halfTime > 30f)
|
||||
@ -370,19 +370,18 @@ namespace CounterDrone.Core.Algorithms
|
||||
if (peakC > threshold)
|
||||
{
|
||||
float sigma = (sY + sZ) / 2f;
|
||||
effectiveR = rPhase2 + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold));
|
||||
effectiveR = turbulentRadius + sigma * (float)Math.Sqrt(2f * Math.Log(peakC / threshold));
|
||||
}
|
||||
}
|
||||
|
||||
return (effectiveR, expansionTime);
|
||||
return (effectiveR, expansionTime, turbulentRadius);
|
||||
}
|
||||
|
||||
private int CalcRoundsNeeded(DroneGroup threat, AmmunitionSpec ammo,
|
||||
CombatScene env, bool isAirBased)
|
||||
CombatScene env, bool isAirBased, float turbulentRadius)
|
||||
{
|
||||
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
|
||||
var (effectiveR, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
float spacing = effectiveR * 1.5f;
|
||||
float spacing = 2f * turbulentRadius;
|
||||
|
||||
var aerosolType = (AerosolType)ammo.AerosolType;
|
||||
float neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
|
||||
@ -390,14 +389,14 @@ namespace CounterDrone.Core.Algorithms
|
||||
float requiredCoverage = singleCoverage;
|
||||
|
||||
return Math.Max(1,
|
||||
(int)Math.Ceiling((requiredCoverage - 2f * effectiveR) / spacing) + 1);
|
||||
(int)Math.Ceiling((requiredCoverage - 2f * turbulentRadius) / spacing) + 1);
|
||||
}
|
||||
|
||||
private float ComputeInterceptProbability(DroneGroup threat,
|
||||
AmmunitionSpec ammo, int rounds, CombatScene env)
|
||||
{
|
||||
float avgSpeed = (float)threat.Target.TypicalSpeed / 3.6f;
|
||||
var (effectiveR, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
var (effectiveR, _, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
float spacing = effectiveR * 1.5f;
|
||||
float actualCoverage = spacing * (rounds - 1) + 2f * effectiveR;
|
||||
float actualExposure = actualCoverage / avgSpeed;
|
||||
@ -419,7 +418,7 @@ namespace CounterDrone.Core.Algorithms
|
||||
var events = new List<FireEvent>();
|
||||
|
||||
var mid = ThreatMidpoint(threat);
|
||||
var (effectiveR, expansionTime) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
var (effectiveR, expansionTime, _) = ComputeEffectiveRadius(threat, ammo, env);
|
||||
|
||||
// 编队 Y 偏移:按车道分布
|
||||
float laneSpacingZ = yLanes > 1 ? formationWidth / (yLanes - 1) : 0f;
|
||||
|
||||
@ -181,7 +181,7 @@ namespace CounterDrone.Core.Simulation
|
||||
fe.TargetX, fe.TargetY, fe.TargetZ, fe.MuzzleVelocity, _disperseHeight);
|
||||
_munitions.Add(m);
|
||||
OnMunitionLaunched?.Invoke(m);
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = SimulationTime, SourceId = platform.Id, Description = $"计划发射 {m.Id}" });
|
||||
frameEvents.Add(new SimEvent { Type = SimEventType.MunitionLaunched, OccurredAt = fe.FireTime, SourceId = platform.Id, Description = $"计划发射 {m.Id}" });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -187,14 +187,14 @@ namespace CounterDrone.Core.Tests
|
||||
{
|
||||
GroupId = "default", TargetType = (int)TargetType.Piston,
|
||||
PowerType = (int)PowerType.Piston,
|
||||
Quantity = 10, // 10 架
|
||||
Quantity = 5, // 5 架
|
||||
TypicalSpeed = 200, TypicalAltitude = 500,
|
||||
});
|
||||
_scenario.SaveRoute(_taskId, "default", new RoutePlan
|
||||
{
|
||||
FormationMode = (int)FormationMode.Formation,
|
||||
LateralSpacing = 50,
|
||||
LateralCount = 10,
|
||||
LateralCount = 5,
|
||||
LongitudinalCount = 1,
|
||||
},
|
||||
new List<Waypoint>
|
||||
@ -204,7 +204,7 @@ namespace CounterDrone.Core.Tests
|
||||
});
|
||||
_scenario.SaveDeployment(_taskId, new List<EquipmentDeployment>
|
||||
{
|
||||
MakeEquipment(DefaultFireUnits.GetById("ground-light"), AerosolType.InertGas, 10, 5000, 0, 50),
|
||||
MakeEquipment(DefaultFireUnits.GetById("ground-light"), AerosolType.InertGas, 5, 5000, 0, 50),
|
||||
});
|
||||
_scenario.SaveCloudDispersal(_taskId, new CloudDispersal { AerosolType = (int)AerosolType.InertGas, DisperseHeight = 500 });
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user