189 lines
7.5 KiB
C#
189 lines
7.5 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using CounterDrone.Core.Models;
|
||
|
||
namespace CounterDrone.Core.Algorithms
|
||
{
|
||
public class DefaultDefenseAdvisor : IDefenseAdvisor
|
||
{
|
||
private readonly List<AmmunitionSpec> _ammoCatalog;
|
||
|
||
public DefaultDefenseAdvisor(List<AmmunitionSpec> ammoCatalog)
|
||
{
|
||
_ammoCatalog = ammoCatalog ?? new List<AmmunitionSpec>();
|
||
}
|
||
|
||
private static readonly Dictionary<PowerType, AerosolType> MatchTable = new()
|
||
{
|
||
{ PowerType.Electric, AerosolType.InertGas },
|
||
{ PowerType.Piston, AerosolType.InertGas },
|
||
{ PowerType.Jet, AerosolType.ActiveMaterial },
|
||
};
|
||
|
||
public DefenseRecommendation Recommend(ThreatProfile threat)
|
||
{
|
||
var result = new DefenseRecommendation
|
||
{
|
||
Best = new DefenseSolution(),
|
||
Critical = new DefenseSolution(),
|
||
};
|
||
|
||
// ===== 输入校验 =====
|
||
if (threat.Targets.Count == 0)
|
||
{
|
||
result.Best.SummaryRationale = "失败:未配置威胁目标";
|
||
return result;
|
||
}
|
||
if (threat.Waypoints.Count < 2)
|
||
{
|
||
result.Best.SummaryRationale = "失败:需要至少两个航路点";
|
||
return result;
|
||
}
|
||
|
||
var target = threat.Targets[0];
|
||
if (target.TypicalSpeed <= 0)
|
||
{
|
||
result.Best.SummaryRationale = "失败:目标速度为 0";
|
||
return result;
|
||
}
|
||
|
||
// ===== Step A:气溶胶选型 =====
|
||
var powerType = (PowerType)target.PowerType;
|
||
var aerosolType = MatchTable.TryGetValue(powerType, out var match)
|
||
? match : AerosolType.InertGas;
|
||
|
||
var ammo = _ammoCatalog.FirstOrDefault(a => a.AerosolType == (int)aerosolType);
|
||
if (ammo == null)
|
||
{
|
||
result.Best.SummaryRationale = $"失败:弹药库中未找到 {aerosolType} 类型的弹药规格";
|
||
return result;
|
||
}
|
||
|
||
var rationale = powerType switch
|
||
{
|
||
PowerType.Electric or PowerType.Piston =>
|
||
$"{powerType}发动机依赖氧气,推荐惰性气体窒息方案",
|
||
PowerType.Jet =>
|
||
$"{powerType}发动机高温表面可触发活性材料爆燃反应",
|
||
_ => "默认推荐惰性气体方案",
|
||
};
|
||
|
||
// ===== Step B:时空交汇优化 =====
|
||
var start = ToV3(threat.Waypoints[0]);
|
||
var end = ToV3(threat.Waypoints[^1]);
|
||
var mid = new Vector3((start.X + end.X) / 2f, (start.Y + end.Y) / 2f, (start.Z + end.Z) / 2f);
|
||
var routeLength = start.DistanceTo(end);
|
||
var avgSpeed = (float)target.TypicalSpeed / 3.6f;
|
||
var totalFlightTime = routeLength / avgSpeed;
|
||
|
||
if (routeLength < 100)
|
||
{
|
||
result.Best.SummaryRationale = "失败:航路太短(<100m)";
|
||
return result;
|
||
}
|
||
|
||
// 弹药参数
|
||
var initialR = (float)ammo.InitialRadius;
|
||
var maxDur = (float)ammo.MaxDuration;
|
||
|
||
// 无人机穿过单个云团的时间
|
||
var crossTime = (2f * initialR) / avgSpeed;
|
||
var neededExposure = aerosolType == AerosolType.ActiveMaterial ? 2f : 6f;
|
||
var spacing = initialR * 1.5f; // 云团间隔
|
||
|
||
// 有效覆盖距离 = 间距×(N-1) + 直径,需 ≥ 所需暴露时间 × 速度
|
||
var requiredCoverage = neededExposure * avgSpeed;
|
||
var roundsNeeded = Math.Max(1,
|
||
(int)Math.Ceiling((requiredCoverage - 2f * initialR) / spacing) + 1);
|
||
|
||
// 实际有效暴露时间
|
||
var actualCoverage = spacing * (roundsNeeded - 1) + 2f * initialR;
|
||
var actualExposure = actualCoverage / avgSpeed;
|
||
var prob = Math.Min(0.95f, actualExposure / neededExposure);
|
||
|
||
// 多轮次云团:沿航路等距分布
|
||
var cloudSalvo = new List<CloudDispersal>();
|
||
for (int i = 0; i < roundsNeeded; i++)
|
||
{
|
||
var offset = (i - (roundsNeeded - 1) / 2f) * spacing;
|
||
cloudSalvo.Add(new CloudDispersal
|
||
{
|
||
AerosolType = (int)aerosolType,
|
||
PositionX = mid.X + offset,
|
||
PositionY = mid.Y,
|
||
PositionZ = mid.Z,
|
||
DisperseHeight = (float)target.TypicalAltitude,
|
||
TriggerMode = (int)TriggerMode.Time,
|
||
Duration = (int)maxDur,
|
||
InitialScale = ammo.InitialVolume,
|
||
ReleaseMode = (int)ReleaseMode.Single,
|
||
Source = "Algorithm",
|
||
PositionMode = (int)PositionMode.AlgorithmRecommended,
|
||
RecommendedTiming = totalFlightTime / 2f,
|
||
});
|
||
}
|
||
|
||
// 平台部署:假设齐射(同时发射),每门炮打一发后冷却 5s
|
||
// 需要 roundsNeeded 门炮同时发射
|
||
var gunCount = roundsNeeded;
|
||
var platforms = new List<RecommendedPlatform>();
|
||
for (int i = 0; i < gunCount; i++)
|
||
platforms.Add(new RecommendedPlatform
|
||
{
|
||
Type = PlatformType.GroundBased,
|
||
Position = new Vector3(mid.X + i * 50, 0, 50),
|
||
Quantity = 1,
|
||
MunitionCount = 1,
|
||
CoverageVolume = (float)ammo.InitialVolume,
|
||
Cooldown = 5f,
|
||
MuzzleVelocity = 800f,
|
||
});
|
||
|
||
result.Best = new DefenseSolution
|
||
{
|
||
RecommendedAerosolType = aerosolType,
|
||
AerosolRationale = rationale,
|
||
RecommendedCloud = cloudSalvo[0],
|
||
CloudSalvo = cloudSalvo,
|
||
Platforms = platforms,
|
||
Detections = new List<RecommendedDetection>
|
||
{
|
||
new RecommendedDetection
|
||
{
|
||
Position = new Vector3(mid.X, mid.Y, 0),
|
||
DetectionRadius = Math.Max(3000f, routeLength * 0.4f),
|
||
Quantity = 1,
|
||
}
|
||
},
|
||
InterceptProbability = prob,
|
||
SummaryRationale = $"{aerosolType}方案,{roundsNeeded}发,预计拦截概率 {prob:P0}",
|
||
};
|
||
|
||
result.Critical = new DefenseSolution
|
||
{
|
||
RecommendedAerosolType = aerosolType,
|
||
RecommendedCloud = new CloudDispersal
|
||
{
|
||
AerosolType = (int)aerosolType,
|
||
PositionX = start.X + (end.X - start.X) * 0.25f,
|
||
PositionY = start.Y,
|
||
PositionZ = start.Z,
|
||
DisperseHeight = (float)target.TypicalAltitude,
|
||
Duration = (int)maxDur,
|
||
Source = "Algorithm",
|
||
PositionMode = (int)PositionMode.AlgorithmRecommended,
|
||
RecommendedTiming = totalFlightTime * 0.25f,
|
||
},
|
||
Platforms = platforms,
|
||
InterceptProbability = prob * 0.3f,
|
||
SummaryRationale = $"临界方案:拦截概率仅 {prob * 0.3f:P0}",
|
||
};
|
||
|
||
return result;
|
||
}
|
||
|
||
private static Vector3 ToV3(Waypoint wp) => new((float)wp.PosX, (float)wp.PosY, (float)wp.PosZ);
|
||
}
|
||
}
|