131 lines
5.4 KiB
C#
131 lines
5.4 KiB
C#
using CounterDrone.Core.Algorithms;
|
|
using CounterDrone.Core.Models;
|
|
using CounterDrone.Core.Simulation;
|
|
using Xunit;
|
|
|
|
namespace CounterDrone.Core.Tests
|
|
{
|
|
public class MunitionEntityTests
|
|
{
|
|
[Fact]
|
|
public void GroundBased_DoesNotArriveImmediately()
|
|
{
|
|
var m = new MunitionEntity("m1", PlatformType.GroundBased,
|
|
AerosolType.InertGas,
|
|
startX: 0, startY: 0, startZ: 0,
|
|
targetX: 5000, targetY: 300, targetZ: 0,
|
|
muzzleVelocity: 500f, releaseAltitude: 300f, launchTime: 0f);
|
|
|
|
m.Update(0.05f);
|
|
Assert.False(m.HasArrived, "炮弹不能第一帧就到达");
|
|
}
|
|
|
|
[Fact]
|
|
public void GroundBased_ArrivesNearTarget_WithInterpolation()
|
|
{
|
|
var m = new MunitionEntity("m2", PlatformType.GroundBased,
|
|
AerosolType.InertGas,
|
|
startX: 5000, startY: 0, startZ: 50,
|
|
targetX: 10000, targetY: 500, targetZ: 0,
|
|
muzzleVelocity: 800f, releaseAltitude: 500f, launchTime: 143.69f);
|
|
|
|
float time = Kinematics.ParabolicShellTime(5000.25f, 500f, 800f);
|
|
for (float t = 0; t < time + 1f; t += 0.4f)
|
|
{
|
|
m.Update(0.4f);
|
|
if (m.HasArrived) break;
|
|
}
|
|
Assert.True(m.HasArrived, "应到达");
|
|
Assert.Equal(500f, m.PosY, 5f);
|
|
Assert.True(System.Math.Abs(m.PosX - 10000) < 200f, $"X={m.PosX:F1} 应≈10000");
|
|
}
|
|
|
|
// ═══════════════════════════════════════
|
|
// 抛物线运动学:固定角度验证
|
|
// ═══════════════════════════════════════
|
|
|
|
[Fact]
|
|
public void Parabolic_45Deg_Azimuth0_MovesInZ()
|
|
{
|
|
// ParabolicPosition: azimuth=0 → +Z方向, sin(0)=0→X, cos(0)=1→Z
|
|
float angle45 = 45f * (float)System.Math.PI / 180f;
|
|
var (x, y, z) = Kinematics.ParabolicPosition(0, 0, 0, angle45, 0, 100, 1f);
|
|
Assert.True(System.Math.Abs(x) < 0.1f, $"X={x:F1} ≈0");
|
|
Assert.True(System.Math.Abs(y - 65.8f) < 1f, $"Y={y:F1} ≈65.8");
|
|
Assert.True(System.Math.Abs(z - 70.7f) < 1f, $"Z={z:F1} ≈70.7");
|
|
}
|
|
|
|
[Fact]
|
|
public void Parabolic_30Deg_Azimuth90_MovesInX()
|
|
{
|
|
// azimuth=90°(π/2): sin(π/2)=1→X, cos(π/2)=0→Z
|
|
float angle30 = 30f * (float)System.Math.PI / 180f;
|
|
float az90 = 90f * (float)System.Math.PI / 180f;
|
|
var (x, y, z) = Kinematics.ParabolicPosition(0, 0, 0, angle30, az90, 100, 1f);
|
|
Assert.True(System.Math.Abs(x - 86.6f) < 1f, $"X={x:F1} ≈86.6");
|
|
Assert.True(System.Math.Abs(z) < 0.1f, $"Z={z:F1} ≈0");
|
|
}
|
|
|
|
[Fact]
|
|
public void Parabolic_60Deg_Azimuth270_MovesBackward()
|
|
{
|
|
// azimuth=270°(3π/2): sin=-1→X方向, cos=0→Z
|
|
float angle60 = 60f * (float)System.Math.PI / 180f;
|
|
float az270 = 270f * (float)System.Math.PI / 180f;
|
|
var (x, y, z) = Kinematics.ParabolicPosition(0, 0, 0, angle60, az270, 100, 1f);
|
|
Assert.True(System.Math.Abs(x + 50f) < 1f, $"X={x:F1} ≈-50");
|
|
Assert.True(System.Math.Abs(z) < 0.1f, $"Z={z:F1} ≈0");
|
|
}
|
|
|
|
[Fact]
|
|
public void GroundBased_LaunchAngle_ReachesReleaseAltitude()
|
|
{
|
|
// 验证运动学:给定距离和初速,弹道能达到释放高度
|
|
var range = 5000f;
|
|
var v0 = 500f;
|
|
var releaseAlt = 300f;
|
|
var angle = Kinematics.CalculateLaunchAngle(range, v0, releaseAlt);
|
|
|
|
// 弹道顶点高度 >= 释放高度
|
|
var peakH = v0 * v0 * (float)System.Math.Sin(angle) * (float)System.Math.Sin(angle) / (2f * 9.81f);
|
|
Assert.True(peakH >= releaseAlt,
|
|
$"弹道顶点 {peakH:F0}m ≥ 释放高度 {releaseAlt}m, angle={angle * 180 / System.Math.PI:F1}°");
|
|
}
|
|
|
|
[Fact]
|
|
public void GroundBased_MovesForwardOverTime()
|
|
{
|
|
var m = new MunitionEntity("m1", PlatformType.GroundBased,
|
|
AerosolType.InertGas,
|
|
startX: 0, startY: 0, startZ: 0,
|
|
targetX: 1000, targetY: 300, targetZ: 0,
|
|
muzzleVelocity: 500f, releaseAltitude: 300f, launchTime: 0f);
|
|
|
|
m.Update(1f);
|
|
var dist = System.Math.Sqrt(m.PosX * m.PosX + m.PosZ * m.PosZ);
|
|
Assert.True(dist > 100f, "1秒后炮弹应水平移动 > 100m");
|
|
}
|
|
|
|
[Fact]
|
|
public void AirBased_DropsToReleaseAltitude()
|
|
{
|
|
var m = new MunitionEntity("m1", PlatformType.AirBased,
|
|
AerosolType.InertGas,
|
|
startX: 0, startY: 500, startZ: 0,
|
|
targetX: 1000, targetY: 300, targetZ: 0,
|
|
muzzleVelocity: 0, releaseAltitude: 300f, launchTime: 0f);
|
|
|
|
// 空基投放:自由落体 500m → 300m = 200m 下落
|
|
// h = 0.5*g*t² → t = sqrt(2*200/9.81) ≈ 6.4s
|
|
for (int i = 0; i < 500; i++)
|
|
{
|
|
m.Update(0.05f);
|
|
if (m.HasArrived) break;
|
|
}
|
|
|
|
Assert.True(m.HasArrived, "空基弹药应到达释放高度");
|
|
Assert.True(m.PosY <= 300f + 1f);
|
|
}
|
|
}
|
|
}
|