ThreatSourceLibaray/ThreatSource.Tests/src/Equipment/RcsPatternTests.cs

503 lines
33 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using Microsoft.VisualStudio.TestTools.UnitTesting;
using ThreatSource.Equipment;
using ThreatSource.Utils;
namespace ThreatSource.Tests.Equipment
{
[TestClass]
public class RcsPatternTests
{
// 此处理想情况下应与 RcsPattern.cs 中的常量匹配,或从该常量获取。
// 为测试目的,我们在此定义。如果 RcsPattern.DEFAULT_RCS_DBSM 发生更改,请确保此处保持一致。
private const double DEFAULT_RCS_DBSM_IN_PATTERN = 10.0;
private const double TEST_DELTA = 1e-6; // 浮点数比较的容差
/// <summary>辅助方法用于创建具有可预测值的测试RCS矩阵。</summary>
/// <param name="rows">矩阵的行数。</param>
/// <param name="cols">矩阵的列数。</param>
/// <param name="valueFunc">用于生成值的函数。如果为null则使用 (i * 10 + j + 1.0)。</param>
/// <returns>表示RCS矩阵的二维双精度浮点数组。</returns>
private double[,] CreateTestMatrix(int rows = 6, int cols = 4, Func<int, int, double>? valueFunc = null)
{
var matrix = new double[rows, cols];
if (valueFunc == null)
{
valueFunc = (r, c) => (r * 10 + c + 1.0);
}
for (int i = 0; i < rows; i++)
{
for (int j = 0; j < cols; j++)
{
matrix[i, j] = valueFunc(i, j);
}
}
return matrix;
}
[TestMethod]
public void DefaultConstructor_InitializesCorrectly()
{
var rcsPattern = new RcsPattern();
// Check RcsMatrixDataSource (List<List<double>>)
Assert.IsNotNull(rcsPattern.RcsMatrixDataSource, "RcsMatrixDataSource 不应该为 null。");
Assert.AreEqual(6, rcsPattern.RcsMatrixDataSource.Count, "RcsMatrixDataSource 应该有 6 行。");
for (int i = 0; i < 6; i++)
{
Assert.IsNotNull(rcsPattern.RcsMatrixDataSource[i], $"RcsMatrixDataSource 行 {i} 不应该为 null。");
Assert.AreEqual(4, rcsPattern.RcsMatrixDataSource[i].Count, $"RcsMatrixDataSource 行 {i} 应该有 4 列。");
for (int j = 0; j < 4; j++)
{
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, rcsPattern.RcsMatrixDataSource[i][j], TEST_DELTA, $"RcsMatrixDataSource[{i}][{j}] 应该为默认值。");
}
}
// Check RcsMatrixData (double[,])
var rcsData = rcsPattern.RcsMatrixData;
Assert.IsNotNull(rcsData, "RcsMatrixData should not be null.");
Assert.AreEqual(6, rcsData.GetLength(0), "RcsMatrixData should have 6 rows.");
Assert.AreEqual(4, rcsData.GetLength(1), "RcsMatrixData should have 4 columns.");
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 4; j++)
{
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, rcsData[i, j], TEST_DELTA, $"RcsMatrixData[{i},{j}] 应该为默认值。");
}
}
}
[TestMethod]
public void ParameterizedConstructor_WithValidMatrix_InitializesCorrectly()
{
double[,] expectedMatrix = CreateTestMatrix(); // 使用默认的6x4矩阵其值为 (i*10+j+1)
var rcsPattern = new RcsPattern(expectedMatrix);
var actualMatrixData = rcsPattern.RcsMatrixData;
var actualMatrixDataSource = rcsPattern.RcsMatrixDataSource;
// 检查 RcsMatrixData (double[,])
Assert.AreEqual(6, actualMatrixData.GetLength(0));
Assert.AreEqual(4, actualMatrixData.GetLength(1));
for (int i = 0; i < 6; i++)
{
for (int j = 0; j < 4; j++)
{
Assert.AreEqual(expectedMatrix[i, j], actualMatrixData[i, j], TEST_DELTA, $"RcsMatrixData[{i},{j}] 没有匹配预期。");
}
}
// Check RcsMatrixDataSource (List<List<double>>)
Assert.IsNotNull(actualMatrixDataSource);
Assert.AreEqual(6, actualMatrixDataSource.Count);
for (int i = 0; i < 6; i++)
{
Assert.IsNotNull(actualMatrixDataSource[i]);
Assert.AreEqual(4, actualMatrixDataSource[i].Count);
for (int j = 0; j < 4; j++)
{
Assert.AreEqual(expectedMatrix[i, j], actualMatrixDataSource[i][j], TEST_DELTA, $"RcsMatrixDataSource[{i}][{j}] 没有匹配预期。");
}
}
}
[TestMethod]
public void ParameterizedConstructor_WithNullMatrix_ThrowsArgumentNullException()
{
double[,]? nullMatrix = null;
Assert.ThrowsException<ArgumentNullException>(() => new RcsPattern(nullMatrix!));
}
[TestMethod]
public void ParameterizedConstructor_WithInvalidDimensions_ThrowsArgumentException()
{
double[,] wrongRowsMatrix = CreateTestMatrix(5, 4);
double[,] wrongColsMatrix = CreateTestMatrix(6, 3);
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongRowsMatrix), "构造函数应该在行数错误时抛出异常。");
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongColsMatrix), "构造函数应该在列数错误时抛出异常。");
}
[TestMethod]
public void RcsMatrixData_HandlesInvalidDataSource_ReturnsDefault()
{
var rcsPattern = new RcsPattern(); // 以有效的默认值开始
var defaultMatrix = CreateTestMatrix(6, 4, (r,c) => DEFAULT_RCS_DBSM_IN_PATTERN);
// 执行与断言 案例1DataSource 为 null
rcsPattern.RcsMatrixDataSource = null!;
var rcsDataAfterNullSource = rcsPattern.RcsMatrixData;
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterNullSource, "RcsMatrixData 应该在 DataSource 为 null 时返回默认值。");
// 执行与断言 案例2DataSource 的行数错误
rcsPattern.RcsMatrixDataSource = [[1, 2, 3, 4]]; // 只有 1 行
var rcsDataAfterWrongRowCount = rcsPattern.RcsMatrixData;
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterWrongRowCount, "RcsMatrixData 应该在 DataSource 中处理行数错误,返回默认值。");
// 执行与断言 案例3DataSource 中的某行为 null
rcsPattern = new RcsPattern(); // 重置为有效状态
rcsPattern.RcsMatrixDataSource[2] = null!; // 将其中一行设置为 null
var rcsDataAfterNullRow = rcsPattern.RcsMatrixData;
// RcsPattern.ConvertSourceToGrid 会用默认值填充特定错误行,其他行保持不变。
// 因此,我们需要为此情况构造特定的预期矩阵。
var expectedMatrixWithOneDefaultRow = new RcsPattern().RcsMatrixData; // 以全默认值开始
// RcsPattern() 的原始测试矩阵就是默认矩阵。因此,此特定设置无需更改。
// 如果 RcsPattern() 的初始化方式不同,我们需要相应地调整 'expectedMatrixWithOneDefaultRow'。
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRow, rcsDataAfterNullRow, "RcsMatrixData 应该在 DataSource 中处理 null 行,通过默认该行。");
// 执行与断言 案例4DataSource 中某行的列数错误
rcsPattern = new RcsPattern(); // 重置为有效状态
rcsPattern.RcsMatrixDataSource[3] = [1, 2, 3]; // 第3行只有3列
var rcsDataAfterWrongColCountInRow = rcsPattern.RcsMatrixData;
// 与案例3类似RcsPattern.ConvertSourceToGrid 应将问题行置为默认值。
var expectedMatrixWithOneDefaultRowAgain = new RcsPattern().RcsMatrixData;
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRowAgain, rcsDataAfterWrongColCountInRow, "RcsMatrixData 应该在 DataSource 中处理某行的列数错误,通过默认该行。");
}
// GetRcsValueDbSm(double azimuthDeg, double elevationDeg) 的测试
// 基于角度的测试的辅助方法,使用可预测的矩阵
private RcsPattern CreateAngleTestPattern()
{
// 矩阵中 RcsMatrix[row, col] = (row + 1) * 100 + (col + 1) * 10 以便于识别
return new RcsPattern(CreateTestMatrix(6, 4, (r, c) => (r + 1) * 100 + (c + 1) * 10));
}
[DataTestMethod]
[DataRow(0.0, 0, DisplayName = "Top_Az0_Q0")]
[DataRow(45.0, 0, DisplayName = "Top_Az45_Q0")]
[DataRow(89.9, 0, DisplayName = "Top_Az89.9_Q0")]
[DataRow(90.0, 1, DisplayName = "Top_Az90_Q1")]
[DataRow(135.0, 1, DisplayName = "Top_Az135_Q1")]
[DataRow(179.9, 1, DisplayName = "Top_Az179.9_Q1")]
[DataRow(-0.1, 3, DisplayName = "Top_Az-0.1_Q3")]
[DataRow(-45.0, 3, DisplayName = "Top_Az-45_Q3")]
[DataRow(-89.9, 3, DisplayName = "Top_Az-89.9_Q3")]
[DataRow(-90.0, 2, DisplayName = "Top_Az-90_Q2")]
[DataRow(-135.0, 2, DisplayName = "Top_Az-135_Q2")]
[DataRow(-179.9, 2, DisplayName = "Top_Az-179.9_Q2")]
[DataRow(180.0, 2, DisplayName = "Top_Az180_Q2")]
[DataRow(-180.0, 2, DisplayName = "Top_Az-180_Q2")]
public void GetRcsAngleBased_TopAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
double elevationDeg = 75.0;
int expectedMajorAspectRow = 4;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"失败 for Az: {azimuthDeg}, El: {elevationDeg}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
[DataTestMethod]
// 与顶部视角类似的数据行,仅高程为-75
[DataRow(0.0, 0, DisplayName = "Bottom_Az0_Q0")]
[DataRow(89.9, 0, DisplayName = "Bottom_Az89.9_Q0")]
[DataRow(90.0, 1, DisplayName = "Bottom_Az90_Q1")]
[DataRow(179.9, 1, DisplayName = "Bottom_Az179.9_Q1")]
[DataRow(-0.1, 3, DisplayName = "Bottom_Az-0.1_Q3")]
[DataRow(-89.9, 3, DisplayName = "Bottom_Az-89.9_Q3")]
[DataRow(-90.0, 2, DisplayName = "Bottom_Az-90_Q2")]
[DataRow(-179.9, 2, DisplayName = "Bottom_Az-179.9_Q2")]
[DataRow(180.0, 2, DisplayName = "Bottom_Az180_Q2")]
[DataRow(-180.0, 2, DisplayName = "Bottom_Az-180_Q2")]
public void GetRcsAngleBased_BottomAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
double elevationDeg = -75.0; // 底部视角
int expectedMajorAspectRow = 5; // 底部
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"失败 for Az: {azimuthDeg}, El: {elevationDeg}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// 正面视角 (行 0)
[DataTestMethod]
[DataRow(0.0, 30.0, 0, DisplayName = "Front_Az0_El30_Q0_TopRight")]
[DataRow(44.9, 30.0, 0, DisplayName = "Front_Az44.9_El30_Q0_TopRight")]
[DataRow(0.0, -30.0, 3, DisplayName = "Front_Az0_El-30_Q3_BottomRight")]
[DataRow(44.9, -30.0, 3, DisplayName = "Front_Az44.9_El-30_Q3_BottomRight")]
[DataRow(-0.1, 30.0, 1, DisplayName = "Front_Az-0.1_El30_Q1_TopLeft")]
[DataRow(-44.9, 30.0, 1, DisplayName = "Front_Az-44.9_El30_Q1_TopLeft")]
[DataRow(-0.1, -30.0, 2, DisplayName = "Front_Az-0.1_El-30_Q2_BottomLeft")]
[DataRow(-44.9, -30.0, 2, DisplayName = "Front_Az-44.9_El-30_Q2_BottomLeft")]
public void GetRcsAngleBased_FrontAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, double elevationDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
int expectedMajorAspectRow = 0;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"正面视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// 背面视角 (行 1) - 背面方位角为 135 到 -135 (或 135 到 225)
[DataTestMethod]
[DataRow(179.9, 30.0, 0, DisplayName = "Back_Az179.9_El30_Q0_TopRight")] // 方位角 179.9 (>0), 高程 > 0 -> 背面, Q0 (简化逻辑)
[DataRow(135.1, 30.0, 0, DisplayName = "Back_Az135.1_El30_Q0_TopRight")] // 方位角 135.1 (>0), 高程 > 0 -> 背面, Q0
[DataRow(179.9, -30.0, 3, DisplayName = "Back_Az179.9_El-30_Q3_BottomRight")]// 方位角 179.9 (>0), 高程 < 0 -> 背面, Q3
[DataRow(135.1, -30.0, 3, DisplayName = "Back_Az135.1_El-30_Q3_BottomRight")]// 方位角 135.1 (>0), 高程 < 0 -> 背面, Q3
[DataRow(-179.9, 30.0, 1, DisplayName = "Back_Az-179.9_El30_Q1_TopLeft")] // 方位角 -179.9 (<0), 高程 > 0 -> 背面, Q1
[DataRow(-135.1, 30.0, 1, DisplayName = "Back_Az-135.1_El30_Q1_TopLeft")] // 方位角 -135.1 (<0), 高程 > 0 -> 背面, Q1
[DataRow(-179.9, -30.0, 2, DisplayName = "Back_Az-179.9_El-30_Q2_BottomLeft")]// 方位角 -179.9 (<0), 高程 < 0 -> 背面, Q2
[DataRow(-135.1, -30.0, 2, DisplayName = "Back_Az-135.1_El-30_Q2_BottomLeft")]// 方位角 -135.1 (<0), 高程 < 0 -> 背面, Q2
[DataRow(180.0, 30.0, 1, DisplayName = "Back_Az180_El30_Q0_TopRight")] // 方位角 180.0, 高程 > 0 -> 背面, Q0 (简化逻辑)
[DataRow(-180.0, 30.0, 1, DisplayName = "Back_Az-180_El30_Q1_TopLeft")]// 方位角 -180.0, 高程 > 0 -> 背面, Q1 (简化逻辑)
public void GetRcsAngleBased_BackAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, double elevationDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
int expectedMajorAspectRow = 1; // 背面
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"背面视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// 右侧视角 (行 3) - 右侧方位角为 45 到 135
[DataTestMethod]
[DataRow(45.1, 30.0, 0, DisplayName = "Right_Az45.1_El30_Q0_TopRight")] // 方位角 45.1 (<90), 高程 > 0 -> 右侧, Q0
[DataRow(89.9, 30.0, 0, DisplayName = "Right_Az89.9_El30_Q0_TopRight")] // 方位角 89.9 (<90), 高程 > 0 -> 右侧, Q0
[DataRow(45.1, -30.0, 3, DisplayName = "Right_Az45.1_El-30_Q3_BottomRight")]// 方位角 45.1 (<90), 高程 < 0 -> 右侧, Q3
[DataRow(89.9, -30.0, 3, DisplayName = "Right_Az89.9_El-30_Q3_BottomRight")]// 方位角 89.9 (<90), 高程 < 0 -> 右侧, Q3
[DataRow(90.1, 30.0, 1, DisplayName = "Right_Az90.1_El30_Q1_TopLeft")] // 方位角 90.1 (>=90), 高程 > 0 -> 右侧, Q1
[DataRow(134.9, 30.0, 1, DisplayName = "Right_Az134.9_El30_Q1_TopLeft")] // 方位角 134.9 (>=90), 高程 > 0 -> 右侧, Q1
[DataRow(90.1, -30.0, 2, DisplayName = "Right_Az90.1_El-30_Q2_BottomLeft")] // 方位角 90.1 (>=90), 高程 < 0 -> 右侧, Q2
[DataRow(134.9, -30.0, 2, DisplayName = "Right_Az134.9_El-30_Q2_BottomLeft")]// 方位角 134.9 (>=90), 高程 < 0 -> 右侧, Q2
public void GetRcsAngleBased_RightAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, double elevationDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
int expectedMajorAspectRow = 3; // 右侧
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"右侧视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// 左侧视角 (行 2) - 左侧方位角为 -45 到 -135
[DataTestMethod]
[DataRow(-45.1, 30.0, 1, DisplayName = "Left_Az-45.1_El30_Q1_TopLeft")] // 方位角 -45.1 (不 < -90), 高程 > 0 -> 左侧, Q1
[DataRow(-89.9, 30.0, 1, DisplayName = "Left_Az-89.9_El30_Q1_TopLeft")] // 方位角 -89.9 (不 < -90), 高程 > 0 -> 左侧, Q1
[DataRow(-45.1, -30.0, 2, DisplayName = "Left_Az-45.1_El-30_Q2_BottomLeft")]// 方位角 -45.1 (不 < -90), 高程 < 0 -> 左侧, Q2
[DataRow(-89.9, -30.0, 2, DisplayName = "Left_Az-89.9_El-30_Q2_BottomLeft")]// 方位角 -89.9 (不 < -90), 高程 < 0 -> 左侧, Q2
[DataRow(-90.1, 30.0, 0, DisplayName = "Left_Az-90.1_El30_Q0_TopRight")] // 方位角 -90.1 (< -90), 高程 > 0 -> 左侧, Q0
[DataRow(-134.9, 30.0, 0, DisplayName = "Left_Az-134.9_El30_Q0_TopRight")] // 方位角 -134.9 (< -90), 高程 > 0 -> 左侧, Q0
[DataRow(-90.1, -30.0, 3, DisplayName = "Left_Az-90.1_El-30_Q3_BottomRight")]// 方位角 -90.1 (< -90), 高程 < 0 -> 左侧, Q3
[DataRow(-134.9, -30.0, 3, DisplayName = "Left_Az-134.9_El-30_Q3_BottomRight")]// 方位角 -134.9 (< -90), 高程 < 0 -> 左侧, Q3
public void GetRcsAngleBased_LeftAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, double elevationDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
int expectedMajorAspectRow = 2; // 左侧
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"左侧视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
[TestMethod]
public void GetRcsAngleBased_InvalidAngles_ReturnsDefaultRcs()
{
var rcsPattern = CreateAngleTestPattern();
// 高程过高/过低 (应由方法限制,但最好测试逻辑的边界行为)
// 该方法基于高程 > 70 或 < -70 选择行的内部逻辑是稳健的。
// 此测试更关注具有有效高程的极端方位角。
Assert.AreEqual(rcsPattern.GetRcsValueDbSm(720, 30), rcsPattern.GetRcsValueDbSm(0, 30), TEST_DELTA, "方位角 720 应与 0 相同");
Assert.AreEqual(rcsPattern.GetRcsValueDbSm(-720, 30), rcsPattern.GetRcsValueDbSm(0, 30), TEST_DELTA, "方位角 -720 应与 0 相同");
// 测试高程限制
// 如果高程为 95则限制为 90。行 4 (顶部)。方位角 0 -> 列 0。
double expectedTopQ0 = (4 + 1) * 100 + (0 + 1) * 10;
Assert.AreEqual(expectedTopQ0, rcsPattern.GetRcsValueDbSm(0, 95), TEST_DELTA, "Elevation 95 应被限制为 90, 顶部 Q0.");
// 如果高程为 -95则限制为 -90。行 5 (底部)。方位角 0 -> 列 0。
double expectedBottomQ0 = (5 + 1) * 100 + (0 + 1) * 10;
Assert.AreEqual(expectedBottomQ0, rcsPattern.GetRcsValueDbSm(0, -95), TEST_DELTA, "Elevation -95 应被限制为 -90, 底部 Q0.");
// 测试是否有任何意外组合导致调试路径返回 DEFAULT_RCS_DBSM_IN_PATTERN
// 这需要在逻辑处理后找到 majorAspectRow 或 quadrantColumn 超出边界的情况。
// 鉴于当前逻辑,很难触发返回 DEFAULT_RCS_DBSM_IN_PATTERN 的最终 'else' 分支
// 因为方位角和高程归一化 + 选择逻辑似乎覆盖了所有输入。
// 但是,我们可以测试在没有 CreateAngleTestPattern 的情况下,底层矩阵可能为默认值的特定情况
var defaultPattern = new RcsPattern(); // 所有值均为 DEFAULT_RCS_DBSM_IN_PATTERN
// 如果 GetRcsValueDbSm 由于某种原因未能找到单元格并从其最终的 else 返回了 DEFAULT_RCS_DBSM_IN_PATTERN
// 这将会通过。在没有更具体条件下,这不是对该路径的强力测试。
// 目前,我们假设现有的 DataRow 测试覆盖了有效的单元格选择。
// 对备用逻辑的更稳健测试是矩阵查找本身失败,这不是此方法测试的内容。
}
// GetRcsValueDbSm(Vector3D observerDirection, ...) 的测试
private readonly Vector3D TARGET_FORWARD = new(1, 0, 0);
private readonly Vector3D TARGET_UP = new(0, 1, 0);
private readonly Vector3D TARGET_RIGHT = new(0, 0, 1);
// 顶部视角 (行 4) - 观察者在远高于目标的位置
// 顶部局部坐标轴localRight = targetRight (Z), localUp = -targetForward (-X)。
// Q0(Z>0, X<0), Q1(Z<0, X<0), Q2(Z<0, X>0), Q3(Z>0, X>0)。
[DataTestMethod]
[DataRow(-0.1, 10.0, 0.1, 0, DisplayName = "VectorTop_NearFwdSlightRight_Q0")] // X<0 (localUp), Z>0 (localRight) -> Q0 (观察者: -0.1, 10, 0.1)
[DataRow(-1.0, 10.0, 1.0, 0, DisplayName = "VectorTop_FwdLeft_Q0")] // X<0, Z>0 -> Q0
[DataRow(-1.0, 10.0, -1.0, 1, DisplayName = "VectorTop_FwdRight_Q1")]// X<0, Z<0 -> Q1
[DataRow(1.0, 10.0, -1.0, 2, DisplayName = "VectorTop_BackRight_Q2")] // X>0, Z<0 -> Q2
[DataRow(1.0, 10.0, 1.0, 3, DisplayName = "VectorTop_BackLeft_Q3")] // X>0, Z>0 -> Q3
public void GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 4;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Top Aspect - 失败 for Obs: {observerDirection}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// 底部视角 (行 5) - 观察者在远低于目标的位置
// 底部局部坐标轴localRight = targetRight (Z), localUp = targetForward (X)。
// Q0(Z>0, X>0), Q1(Z<0, X>0), Q2(Z<0, X<0), Q3(Z>0, X<0)。
[DataTestMethod]
[DataRow(1.0, -10.0, 1.0, 0, DisplayName = "VectorBottom_FwdLeft_Q0")] // X>0 (localUp), Z>0 (localRight) -> Q0
[DataRow(1.0, -10.0, -1.0, 1, DisplayName = "VectorBottom_FwdRight_Q1")] // X>0, Z<0 -> Q1
[DataRow(-1.0, -10.0, -1.0, 2, DisplayName = "VectorBottom_BackRight_Q2")]// X<0, Z<0 -> Q2
[DataRow(-1.0, -10.0, 1.0, 3, DisplayName = "VectorBottom_BackLeft_Q3")] // X<0, Z>0 -> Q3
public void GetRcsVectorBased_BottomAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 5;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Bottom Aspect - 失败 for Obs: {observerDirection}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// 正面视角 (行 0) - 观察者在前方 (如果 TARGET_FORWARD 是 (1,0,0),则观察者在目标前方的正 X 方向)
// 全局方位角接近 0。observerDirection 应在 TARGET_FORWARD 方向上具有较大的正分量。
// 正面局部坐标轴localRight = targetRight (Z), localUp = targetUp (Y)。
// Q0(Z>=0, Y>=0), Q1(Z<0, Y>=0), Q2(Z<0, Y<0), Q3(Z>=0, Y<0)。
[DataTestMethod]
[DataRow(10.0, 1.0, 1.0, 0, DisplayName = "VectorFront_UpRightQuadrant_Q0")]
[DataRow(10.0, 1.0, -1.0, 1, DisplayName = "VectorFront_UpLeftQuadrant_Q1")]
[DataRow(10.0, -1.0, -1.0, 2, DisplayName = "VectorFront_DownLeftQuadrant_Q2")]
[DataRow(10.0, -1.0, 1.0, 3, DisplayName = "VectorFront_DownRightQuadrant_Q3")]
// 测试点积为零的边界情况
[DataRow(10.0, 0.0, 0.001, 0, DisplayName = "VectorFront_Y0_Zpos_Q0")] // Y=0, Z>0. dot(obs,Y)=0. isLocallyTopQuadrant=true. isLocallyRightQuadrant=true. -> Q0
[DataRow(10.0, 0.0, -0.001, 1, DisplayName = "VectorFront_Y0_Zneg_Q1")]// Y=0, Z<0. isLocallyTopQuadrant=true. isLocallyRightQuadrant=false. -> Q1
[DataRow(10.0, 0.001, 0.0, 0, DisplayName = "VectorFront_Ypos_Z0_Q0")] // Y>0, Z=0. isLocallyTopQuadrant=true. isLocallyRightQuadrant=true. -> Q0
[DataRow(10.0, -0.001, 0.0, 3, DisplayName = "VectorFront_Yneg_Z0_Q3")]// Y<0, Z=0. isLocallyTopQuadrant=false. isLocallyRightQuadrant=true. -> Q3
public void GetRcsVectorBased_FrontAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 0;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Front Aspect - 失败 for Obs: {observerDirection}.");
}
// 背面视角 (行 1) - 观察者在后方 (相对于目标前方的负X方向)
// 背面局部坐标轴localRight = -targetRight (-Z), localUp = targetUp (Y)。
// Q0(Z<=0, Y>=0), Q1(Z>0, Y>=0), Q2(Z>0, Y<0), Q3(Z<=0, Y<0)。
[DataTestMethod]
[DataRow(-10.0, 1.0, -1.0, 0, DisplayName = "VectorBack_UpLeftFacingTarget_Q0")]
[DataRow(-10.0, 1.0, 1.0, 1, DisplayName = "VectorBack_UpRightFacingTarget_Q1")]
[DataRow(-10.0, -1.0, 1.0, 2, DisplayName = "VectorBack_DownRightFacingTarget_Q2")]
[DataRow(-10.0, -1.0, -1.0, 3, DisplayName = "VectorBack_DownLeftFacingTarget_Q3")]
public void GetRcsVectorBased_BackAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 1;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Back Aspect - 失败 for Obs: {observerDirection}.");
}
// 右侧视角 (行 3) - 观察者在右侧 (如果 TARGET_RIGHT 是 (0,0,1)则观察者在目标右侧的正Z方向)
// 右侧局部坐标轴localRight = -targetForward (-X), localUp = targetUp (Y)。
// Q0(X<=0, Y>=0), Q1(X>0, Y>=0), Q2(X>0, Y<0), Q3(X<=0, Y<0)。
[DataTestMethod]
[DataRow(-1.0, 1.0, 10.0, 0, DisplayName = "VectorRight_UpFrontOfTarget_Q0")]
[DataRow(1.0, 1.0, 10.0, 1, DisplayName = "VectorRight_UpBackOfTarget_Q1")]
[DataRow(1.0, -1.0, 10.0, 2, DisplayName = "VectorRight_DownBackOfTarget_Q2")]
[DataRow(-1.0, -1.0, 10.0, 3, DisplayName = "VectorRight_DownFrontOfTarget_Q3")]
public void GetRcsVectorBased_RightAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 3;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Right Aspect - 失败 for Obs: {observerDirection}.");
}
// 左侧视角 (行 2) - 观察者在左侧 (相对于目标右侧的负Z方向)
// 左侧局部坐标轴localRight = targetForward (X), localUp = targetUp (Y)。
// Q0(X>=0, Y>=0), Q1(X<0, Y>=0), Q2(X<0, Y<0), Q3(X>=0, Y<0)。
[DataTestMethod]
[DataRow(1.0, 1.0, -10.0, 0, DisplayName = "VectorLeft_UpBackOfTarget_Q0")]
[DataRow(-1.0, 1.0, -10.0, 1, DisplayName = "VectorLeft_UpFrontOfTarget_Q1")]
[DataRow(-1.0, -1.0, -10.0, 2, DisplayName = "VectorLeft_DownFrontOfTarget_Q2")]
[DataRow(1.0, -1.0, -10.0, 3, DisplayName = "VectorLeft_DownBackOfTarget_Q3")]
public void GetRcsVectorBased_LeftAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 2;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(observerDirection, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Left Aspect - 失败 for Obs: {observerDirection}.");
}
[TestMethod]
public void GetRcsVectorBased_SpecialVectors_ReturnsExpected()
{
var rcsPattern = CreateAngleTestPattern();
// 观察者直接沿坐标轴 (应选择一个一致的象限)
// 正前方 (X轴): 应为正面 (行 0), Q0。
double expectedFrontQ0 = (0 + 1) * 100 + (0 + 1) * 10;
Assert.AreEqual(expectedFrontQ0, rcsPattern.GetRcsValueDbSm(new Vector3D(1,0,0), TARGET_FORWARD, TARGET_UP, TARGET_RIGHT), TEST_DELTA, "Directly Forward vector.");
// 正上方 (Y轴): 应为顶部 (行 4), Q0。
// 注意: 对于顶部视图, localUp = -forwardNorm, localRight = rightNorm。观察者 (0,1,0) 得到 Q0。
double expectedTopQ0 = (4 + 1) * 100 + (0 + 1) * 10;
Assert.AreEqual(expectedTopQ0, rcsPattern.GetRcsValueDbSm(new Vector3D(0,1,0), TARGET_FORWARD, TARGET_UP, TARGET_RIGHT), TEST_DELTA, "Directly Up vector.");
// 零观察者向量: 归一化为 (0,0,0), 结果为正面 (行 0), Q0。
Assert.AreEqual(expectedFrontQ0, rcsPattern.GetRcsValueDbSm(Vector3D.Zero, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT), TEST_DELTA, "Zero observer vector.");
// 观察者直接向下 (0,-1,0): 应为底部 (行 5), Q0。
// 对于底部视图, localUp = forwardNorm, localRight = rightNorm。
double expectedBottomQ0 = (5+1)*100 + (0+1)*10;
Assert.AreEqual(expectedBottomQ0, rcsPattern.GetRcsValueDbSm(new Vector3D(0,-1,0), TARGET_FORWARD, TARGET_UP, TARGET_RIGHT), TEST_DELTA, "Directly Down vector.");
}
[TestMethod]
public void GetAverageRcsDbSm_ReturnsCorrectAverage()
{
var testMatrix = CreateTestMatrix(6, 4, (r, c) => 10.0);
var rcsPattern = new RcsPattern(testMatrix);
double expectedAverage = 10.0;
double actualAverage = rcsPattern.GetAverageRcsDbSm();
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "平均值计算不正确.");
var variedMatrix = CreateTestMatrix();
rcsPattern = new RcsPattern(variedMatrix);
expectedAverage = 660.0 / 24.0;
actualAverage = rcsPattern.GetAverageRcsDbSm();
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "平均值计算不正确.");
}
[TestMethod]
public void GetAverageRcsDbSm_WhenDataSourceIsNull_ReturnsDefault()
{
var rcsPattern = new RcsPattern
{
RcsMatrixDataSource = null!
};
double actualAverage = rcsPattern.GetAverageRcsDbSm();
// 如果 RcsMatrixDataSource 为 nullRcsMatrixData的getter将返回一个用默认值填充的网格因此平均值为默认值。
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, actualAverage, TEST_DELTA, "当DataSource为null, 平均值应为默认值.");
}
}
}