完善 RCS 的处理逻辑,增加测试

This commit is contained in:
Tian jianyong 2025-05-18 00:44:09 +08:00
parent 585163db54
commit 5a0f9f1ece
56 changed files with 298 additions and 529 deletions

1
RcsPatternTests.cs Normal file
View File

@ -0,0 +1 @@

View File

@ -1,117 +0,0 @@
# 上下文
文件名:[RcsPattern_TestFixes.md]
创建于:[2024-07-30 10:00:00]
创建者:[AI]
关联协议RIPER-5 + Multidimensional + Agent Protocol
# 任务描述
用户要求运行 RcsPatternTests 中的所有测试,并解决测试暴露出的问题。
# 项目概述
项目涉及雷达散射截面 (RCS) 的计算,特别是 `RcsPattern`该类根据观察者的角度或矢量确定RCS值。测试失败表明在特定视角尤其是顶部和某些边界角度的象限确定逻辑存在问题。
---
*以下部分由 AI 在协议执行过程中维护*
---
# 分析 (由 RESEARCH 模式填充)
测试结果显示 `RcsPatternTests` 中有8个失败
1. `GetRcsAngleBased_TopAspect_ReturnsCorrectQuadrantValue` (Top_Az-90_Q2): 期望Q2, 实际Q3。原因角度法中Top/Bottom的 `-90` 度方位角边界条件判断错误。
2. `GetRcsAngleBased_BottomAspect_ReturnsCorrectQuadrantValue` (Bottom_Az-90_Q2): 期望Q2, 实际Q3。原因同上。
3. `GetRcsAngleBased_BackAspect_ReturnsCorrectQuadrantValue` (Back_Az180_El30_Q0_TopRight): 期望Q0, 实际Q1。原因角度法中后部视角对 `180` 度处理与测试期望不一致,归一化后 `-180` 的区分处理存在问题。
4. `GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue` (VectorTop_NearFwdSlightRight_Q0): 期望Q0, 实际Q3。原因向量法中Top视角的 `isLocallyTopQuadrant` 定义与 `localUp` 轴 (-forwardNorm) 不一致,而是错误地使用了 `forwardNorm`
5. `GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue` (VectorTop_FwdLeft_Q0): 期望Q0, 实际Q3。原因同上。
6. `GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue` (VectorTop_FwdRight_Q1): 期望Q1, 实际Q2。原因同上。
7. `GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue` (VectorTop_BackRight_Q2): 期望Q2, 实际Q1。原因同上。
8. `GetRcsVectorBased_TopAspect_ReturnsCorrectQuadrantValue` (VectorTop_BackLeft_Q3): 期望Q3, 实际Q0。原因同上。
计划修复问题1, 2, 4, 5, 6, 7, 8。问题3暂时不直接修改观察前述修复效果。
# 提议的解决方案 (由 INNOVATE 模式填充)
1. **针对 `GetRcsValueDbSm(double azimuthDeg, double elevationDeg)` (角度版本):**
* 对于 Top/Bottom 视角,修改 `azimuthDeg = -90` 的象限判断条件,从 `normalizedAzimuthDeg >= -90` 改为 `normalizedAzimuthDeg > -90`,使其落入正确的象限。
2. **针对 `GetRcsValueDbSm(Vector3D observerDirectionInTargetFrame, ...)` (矢量版本):**
* 对于 Top/Bottom 视角,修改 `isLocallyTopQuadrant` 的计算,使其使用在主方向确定步骤中定义的 `localUp` 向量 (对于Top是 `-forwardNorm`对于Bottom是 `forwardNorm`),而不是固定的 `forwardNorm`
# 实施计划 (由 PLAN 模式生成)
实施检查清单:
1. **修改 `GetRcsValueDbSm(double azimuthDeg, double elevationDeg)` 方法中 Top/Bottom 视角的象限确定逻辑:**
* 文件: `ThreatSource/src/Equipment/RcsPattern.cs`
* 目的: 修正 `azimuthDeg = -90` 时 Top 和 Bottom 视角的象限计算错误。
* 具体操作: 在 `if (normalizedElevationDeg > 70)``else if (normalizedElevationDeg < -70)` 的代码块中,将条件 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg >= -90)` 修改为 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg > -90)`
2. **修改 `GetRcsValueDbSm(Vector3D observerDirectionInTargetFrame, ...)` 方法中 `isLocallyTopQuadrant` 的确定逻辑 (针对 Top/Bottom 视角):**
* 文件: `ThreatSource/src/Equipment/RcsPattern.cs`
* 目的: 修正 Top 视角下使用矢量输入时所有象限计算错误,使其与局部坐标轴定义一致。
* 具体操作: 在该方法的步骤 4 中,当 `majorAspectRow == 4 || majorAspectRow == 5` (Top 或 Bottom 视角) 时,将 `isLocallyTopQuadrant` 的计算从 `Vector3D.DotProduct(observerDirNorm, forwardNorm) >= 0;` 修改为 `isLocallyTopQuadrant = Vector3D.DotProduct(observerDirNorm, localUp) >= 0;`
# 当前执行步骤 (由 EXECUTE 模式在开始执行某步骤时更新)
> 正在执行: "关于坐标系问题的分析和文档记录已完成。准备进入 REVIEW 阶段。"
# 任务进度 (由 EXECUTE 模式在每步完成后追加)
* [2024-07-30 10:00:01]
* 步骤:[1. 修改 GetRcsValueDbSm(double azimuthDeg, double elevationDeg) 方法中 Top/Bottom 视角的象限确定逻辑]
* 修改:
* `ThreatSource/src/Equipment/RcsPattern.cs`:
* 在 `if (normalizedElevationDeg > 70)` 代码块 (Top aspect) 中,将 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg >= -90)` 修改为 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg > -90)`.
* 在 `else if (normalizedElevationDeg < -70)` 代码块 (Bottom aspect) 中,将 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg >= -90)` 修改为 `else if (normalizedAzimuthDeg < 0 && normalizedAzimuthDeg > -90)`.
* 更改摘要调整了基于角度的RCS获取方法中Top/Bottom视角的象限确定条件以正确处理-90度方位角的情况。
* 原因:执行计划步骤 [1]
* 阻碍:无
* 用户确认状态:[成功 - 基于后续测试结果推断]
* [2024-07-30 10:00:02]
* 步骤:[2. 修改 GetRcsValueDbSm(Vector3D observerDirectionInTargetFrame, ...) 方法中 isLocallyTopQuadrant 的确定逻辑 (针对 Top/Bottom 视角)]
* 修改:
* `ThreatSource/src/Equipment/RcsPattern.cs`:
* 在 `GetRcsValueDbSm(Vector3D ...)` 方法的步骤 4 中,当 `majorAspectRow == 4 || majorAspectRow == 5` 时,将 `isLocallyTopQuadrant = Vector3D.DotProduct(observerDirNorm, forwardNorm) >= 0;` 修改为 `isLocallyTopQuadrant = Vector3D.DotProduct(observerDirNorm, localUp) >= 0;`.
* 更改摘要修正了基于矢量的RCS获取方法中Top/Bottom视角的 `isLocallyTopQuadrant` 判断逻辑,使其与局部坐标轴定义一致。
* 原因:执行计划步骤 [2]
* 阻碍:无
* 用户确认状态:[成功 - 基于后续测试结果推断]
* [2024-07-30 10:00:03]
* 步骤:[3. 修改 Back_Az180_El30_Q0_TopRight 测试用例的期望值 (方案1)]
* 修改:
* `ThreatSource.Tests/src/Equipment/RcsPatternTests.cs`:
* 将 `[DataRow(180.0, 30.0, 0, DisplayName = "Back_Az180_El30_Q0_TopRight")]` 修改为 `[DataRow(180.0, 30.0, 1, DisplayName = "Back_Az180_El30_Q0_TopRight")]`.
* 更改摘要调整了特定测试用例的期望象限以匹配代码在180度方位角简化处理逻辑下的行为。
* 原因用户选择方案1处理剩余的测试失败。
* 阻碍:无
* 用户确认状态:[成功 - 基于最终测试结果推断]
* [2024-07-30 10:00:04]
* 步骤:[1. 更新任务文件 RcsPattern_TestFixes.md 以包含坐标系讨论的摘要。]
* 修改:
* `RcsPattern_TestFixes.md`:
* 追加了 ## 关于坐标系的讨论 部分总结了RcsPattern类与坐标系局部与世界相关的行为和依赖关系。
* 更改摘要:记录了用户关于坐标系问题的提问以及对此的分析结论。
* 原因:执行计划步骤 [1]
* 阻碍:无
* 用户确认状态:[待确认]
# 最终审查 (由 REVIEW 模式填充)
实施与最终计划包含用户选择的方案1来调整测试用例完全匹配。具体如下
1. **`GetRcsValueDbSm(double azimuthDeg, double elevationDeg)` 方法修改**: Top/Bottom 视角中针对 `-90` 度方位角的象限条件调整正确,符合计划,对应测试已通过。
2. **`GetRcsValueDbSm(Vector3D ...)` 方法修改**: Top/Bottom 视角中 `isLocallyTopQuadrant` 的计算已修正为使用正确的 `localUp` 向量,符合计划,对应测试已通过。
3. **测试用例 `Back_Az180_El30_Q0_TopRight` 修改**: 测试期望值已按照用户选择的方案1从 `0` 调整为 `1`,符合计划,对应测试已通过。
所有先前失败的测试现在均已通过。未发现与计划存在未报告的偏差。
**针对坐标系问题的补充审查 (2024-07-30):**
用户提出的关于坐标系使用的问题RcsPattern 是否使用世界坐标系)已得到解答。分析表明 `RcsPattern` 在目标局部坐标系下工作,不直接依赖世界坐标系。相应的分析和结论已作为补充信息记录在本文档的 `## 关于坐标系的讨论` 部分。此文档更新操作符合计划,内容准确。没有引入新的代码更改或偏差。
## 关于坐标系的讨论 (用户提问 - 2024-07-30)
用户提问:项目使用的坐标系是右手坐标系,+Y 轴向上。RCS 处理的过程中,有使用到世界坐标系吗?
**结论与分析摘要:**
1. **`RcsPattern` 类主要在目标自身(局部)坐标系下运作。** 它不直接与外部的世界坐标系交互或依赖其特定定义(如右手,+Y向上
2. 向量输入方法 `GetRcsValueDbSm(Vector3D observerDirectionInTargetFrame, Vector3D targetForwardVectorInTargetFrame, ...)` 的参数(如 `observerDirectionInTargetFrame` 和目标轴向量)明确要求是在**目标局部坐标系**中定义的。
3. 角度输入方法 `GetRcsValueDbSm(double azimuthDeg, double elevationDeg)` 中的方位角和俯仰角也是相对于**目标自身的参考轴**(如"全局前方参考线"和"全局水平面")定义的,这里的"全局"是指目标级的参考,而非世界坐标系。
4. **世界坐标系的角色**:调用 `RcsPattern` 的外部代码(即您的项目代码)负责处理从世界坐标系到目标局部坐标系的转换。这包括:
* 确定目标在世界坐标系中的姿态(旋转)。
* 计算观察者相对于目标的方向(可能先在世界坐标系中计算)。
* 将此观察方向向量从世界坐标系转换到目标局部坐标系中,然后传递给 `RcsPattern`
* 定义目标自身的 `Forward`、`Up`、`Right` 向量时,需要考虑目标模型本身的轴定义以及其在世界中的姿态,最终得到在目标局部坐标系下表示这些轴的向量(通常是单位向量,如 `(1,0,0)` 等,具体取决于局部坐标系的选取)。
5. **项目坐标系约定 (+Y 向上)**:这个约定主要影响您的项目代码如何进行上述的世界坐标到目标局部坐标的转换,以及如何定义目标的标准姿态或局部轴向。`RcsPattern` 类本身会正确处理您以其参数形式提供的任何(有效的)目标局部坐标系定义。
6. **无需修改 `RcsPattern`**:基于以上分析,`RcsPattern` 类的当前设计是合理的,不需要修改来"适应"特定的世界坐标系。关键在于调用方正确提供目标局部坐标系下的输入。

View File

@ -1,33 +1,28 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ThreatSource.Equipment;
using ThreatSource.Utils; // For Vector3D
using System;
using System.Collections.Generic; // Required for List if RcsMatrixDataSource is directly checked
using System.Linq; // Required for some LINQ operations if used in tests
using ThreatSource.Utils;
namespace ThreatSource.Tests.Equipment
{
[TestClass]
public class RcsPatternTests
{
// This should ideally match the constant in RcsPattern.cs or be sourced from it.
// For testing, we define it here. Ensure it's consistent if RcsPattern.DEFAULT_RCS_DBSM changes.
private const double DEFAULT_RCS_DBSM_IN_PATTERN = -20.0;
private const double TEST_DELTA = 1e-6; // Tolerance for floating-point comparisons
// 此处理想情况下应与 RcsPattern.cs 中的常量匹配,或从该常量获取。
// 为测试目的,我们在此定义。如果 RcsPattern.DEFAULT_RCS_DBSM 发生更改,请确保此处保持一致。
private const double DEFAULT_RCS_DBSM_IN_PATTERN = 10.0;
private const double TEST_DELTA = 1e-6; // 浮点数比较的容差
/// <summary>
/// Helper method to create a test RCS matrix with predictable values.
/// </summary>
/// <param name="rows">Number of rows for the matrix.</param>
/// <param name="cols">Number of columns for the matrix.</param>
/// <param name="valueFunc">Function to generate values. If null, uses (i * 10 + j + 1.0).</param>
/// <returns>A 2D double array representing the RCS matrix.</returns>
/// <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); // Default: e.g., R0C0=1.0, R0C1=2.0,... R1C0=11.0
valueFunc = (r, c) => (r * 10 + c + 1.0);
}
for (int i = 0; i < rows; i++)
@ -40,25 +35,21 @@ namespace ThreatSource.Tests.Equipment
return matrix;
}
// Test methods will be added here
[TestMethod]
public void DefaultConstructor_InitializesCorrectly()
{
// Arrange & Act
var rcsPattern = new RcsPattern();
// Assert
// Check RcsMatrixDataSource (List<List<double>>)
Assert.IsNotNull(rcsPattern.RcsMatrixDataSource, "RcsMatrixDataSource should not be null.");
Assert.AreEqual(6, rcsPattern.RcsMatrixDataSource.Count, "RcsMatrixDataSource should have 6 rows.");
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 row {i} should not be null.");
Assert.AreEqual(4, rcsPattern.RcsMatrixDataSource[i].Count, $"RcsMatrixDataSource row {i} should have 4 columns.");
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}] should be default value.");
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, rcsPattern.RcsMatrixDataSource[i][j], TEST_DELTA, $"RcsMatrixDataSource[{i}][{j}] 应该为默认值。");
}
}
@ -71,7 +62,7 @@ namespace ThreatSource.Tests.Equipment
{
for (int j = 0; j < 4; j++)
{
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, rcsData[i, j], TEST_DELTA, $"RcsMatrixData[{i},{j}] should be default value.");
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, rcsData[i, j], TEST_DELTA, $"RcsMatrixData[{i},{j}] 应该为默认值。");
}
}
}
@ -79,23 +70,20 @@ namespace ThreatSource.Tests.Equipment
[TestMethod]
public void ParameterizedConstructor_WithValidMatrix_InitializesCorrectly()
{
// Arrange
double[,] expectedMatrix = CreateTestMatrix(); // Uses default 6x4 with (i*10+j+1) values
double[,] expectedMatrix = CreateTestMatrix(); // 使用默认的6x4矩阵其值为 (i*10+j+1)
// Act
var rcsPattern = new RcsPattern(expectedMatrix);
var actualMatrixData = rcsPattern.RcsMatrixData;
var actualMatrixDataSource = rcsPattern.RcsMatrixDataSource;
// Assert
// Check RcsMatrixData (double[,])
// 检查 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}] did not match expected.");
Assert.AreEqual(expectedMatrix[i, j], actualMatrixData[i, j], TEST_DELTA, $"RcsMatrixData[{i},{j}] 没有匹配预期。");
}
}
@ -108,7 +96,7 @@ namespace ThreatSource.Tests.Equipment
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}] did not match expected.");
Assert.AreEqual(expectedMatrix[i, j], actualMatrixDataSource[i][j], TEST_DELTA, $"RcsMatrixDataSource[{i}][{j}] 没有匹配预期。");
}
}
}
@ -116,99 +104,94 @@ namespace ThreatSource.Tests.Equipment
[TestMethod]
public void ParameterizedConstructor_WithNullMatrix_ThrowsArgumentNullException()
{
// Arrange
double[,]? nullMatrix = null;
// Act & Assert
Assert.ThrowsException<ArgumentNullException>(() => new RcsPattern(nullMatrix!));
}
[TestMethod]
public void ParameterizedConstructor_WithInvalidDimensions_ThrowsArgumentException()
{
// Arrange
double[,] wrongRowsMatrix = CreateTestMatrix(5, 4);
double[,] wrongColsMatrix = CreateTestMatrix(6, 3);
// Act & Assert
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongRowsMatrix), "Constructor should throw for wrong row count.");
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongColsMatrix), "Constructor should throw for wrong column count.");
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongRowsMatrix), "构造函数应该在行数错误时抛出异常。");
Assert.ThrowsException<ArgumentException>(() => new RcsPattern(wrongColsMatrix), "构造函数应该在列数错误时抛出异常。");
}
[TestMethod]
public void RcsMatrixData_HandlesInvalidDataSource_ReturnsDefault()
{
// Arrange
var rcsPattern = new RcsPattern(); // Starts with a valid default
var rcsPattern = new RcsPattern(); // 以有效的默认值开始
var defaultMatrix = CreateTestMatrix(6, 4, (r,c) => DEFAULT_RCS_DBSM_IN_PATTERN);
// Act & Assert Case 1: DataSource is null
// 执行与断言 案例1DataSource 为 null
rcsPattern.RcsMatrixDataSource = null!;
var rcsDataAfterNullSource = rcsPattern.RcsMatrixData;
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterNullSource, "RcsMatrixData should return default when DataSource is null.");
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterNullSource, "RcsMatrixData 应该在 DataSource 为 null 时返回默认值。");
// Act & Assert Case 2: DataSource has wrong number of rows
rcsPattern.RcsMatrixDataSource = [[1, 2, 3, 4]]; // Only 1 row
// 执行与断言 案例2DataSource 的行数错误
rcsPattern.RcsMatrixDataSource = [[1, 2, 3, 4]]; // 只有 1 行
var rcsDataAfterWrongRowCount = rcsPattern.RcsMatrixData;
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterWrongRowCount, "RcsMatrixData should return default for wrong row count in DataSource.");
CollectionAssert.AreEqual(defaultMatrix, rcsDataAfterWrongRowCount, "RcsMatrixData 应该在 DataSource 中处理行数错误,返回默认值。");
// Act & Assert Case 3: A row in DataSource is null
rcsPattern = new RcsPattern(); // Reset to valid
rcsPattern.RcsMatrixDataSource[2] = null!; // Set one row to null
// 执行与断言 案例3DataSource 中的某行为 null
rcsPattern = new RcsPattern(); // 重置为有效状态
rcsPattern.RcsMatrixDataSource[2] = null!; // 将其中一行设置为 null
var rcsDataAfterNullRow = rcsPattern.RcsMatrixData;
// RcsPattern.ConvertSourceToGrid fills the specific bad row with defaults, others remain.
// So, we need to construct the specific expected matrix for this case.
var expectedMatrixWithOneDefaultRow = new RcsPattern().RcsMatrixData; // Start with all defaults
// The original test matrix for RcsPattern() IS the default matrix. So no change needed for this specific setup.
// If RcsPattern() initialized differently, we would need to adjust 'expectedMatrixWithOneDefaultRow' accordingly.
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRow, rcsDataAfterNullRow, "RcsMatrixData should handle null row in DataSource by defaulting that row.");
// RcsPattern.ConvertSourceToGrid 会用默认值填充特定错误行,其他行保持不变。
// 因此,我们需要为此情况构造特定的预期矩阵。
var expectedMatrixWithOneDefaultRow = new RcsPattern().RcsMatrixData; // 以全默认值开始
// RcsPattern() 的原始测试矩阵就是默认矩阵。因此,此特定设置无需更改。
// 如果 RcsPattern() 的初始化方式不同,我们需要相应地调整 'expectedMatrixWithOneDefaultRow'。
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRow, rcsDataAfterNullRow, "RcsMatrixData 应该在 DataSource 中处理 null 行,通过默认该行。");
// Act & Assert Case 4: A row in DataSource has wrong number of columns
rcsPattern = new RcsPattern(); // Reset to valid
rcsPattern.RcsMatrixDataSource[3] = [1, 2, 3]; // Row 3 has only 3 columns
// 执行与断言 案例4DataSource 中某行的列数错误
rcsPattern = new RcsPattern(); // 重置为有效状态
rcsPattern.RcsMatrixDataSource[3] = [1, 2, 3]; // 第3行只有3列
var rcsDataAfterWrongColCountInRow = rcsPattern.RcsMatrixData;
// Similar to case 3, RcsPattern.ConvertSourceToGrid should default the problematic row.
// 与案例3类似RcsPattern.ConvertSourceToGrid 应将问题行置为默认值。
var expectedMatrixWithOneDefaultRowAgain = new RcsPattern().RcsMatrixData;
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRowAgain, rcsDataAfterWrongColCountInRow, "RcsMatrixData should handle wrong column count in a DataSource row by defaulting that row.");
CollectionAssert.AreEqual(expectedMatrixWithOneDefaultRowAgain, rcsDataAfterWrongColCountInRow, "RcsMatrixData 应该在 DataSource 中处理某行的列数错误,通过默认该行。");
}
// Tests for GetRcsValueDbSm(double azimuthDeg, double elevationDeg)
// Helper for angle-based tests, uses a predictable matrix
// GetRcsValueDbSm(double azimuthDeg, double elevationDeg) 的测试
// 基于角度的测试的辅助方法,使用可预测的矩阵
private RcsPattern CreateAngleTestPattern()
{
// Matrix where RcsMatrix[row, col] = (row + 1) * 100 + (col + 1) * 10 for easy identification
// 矩阵中 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")] // El=75, Az=0 -> Top, Q0 (Right-Top for Top view)
[DataRow(45.0, 0, DisplayName = "Top_Az45_Q0")] // El=75, Az=45 -> Top, Q0
[DataRow(89.9, 0, DisplayName = "Top_Az89.9_Q0")] // El=75, Az=89.9 -> Top, Q0
[DataRow(90.0, 1, DisplayName = "Top_Az90_Q1")] // El=75, Az=90 -> Top, Q1 (Left-Top for Top view)
[DataRow(135.0, 1, DisplayName = "Top_Az135_Q1")] // El=75, Az=135 -> Top, Q1
[DataRow(179.9, 1, DisplayName = "Top_Az179.9_Q1")] // El=75, Az=179.9 -> Top, Q1
[DataRow(-0.1, 3, DisplayName = "Top_Az-0.1_Q3")] // El=75, Az=-0.1 -> Top, Q3 (Right-Bottom for Top view)
[DataRow(-45.0, 3, DisplayName = "Top_Az-45_Q3")] // El=75, Az=-45 -> Top, Q3
[DataRow(-89.9, 3, DisplayName = "Top_Az-89.9_Q3")] // El=75, Az=-89.9 -> Top, Q3
[DataRow(-90.0, 2, DisplayName = "Top_Az-90_Q2")] // El=75, Az=-90 -> Top, Q2 (Left-Bottom for Top view)
[DataRow(-135.0, 2, DisplayName = "Top_Az-135_Q2")] // El=75, Az=-135 -> Top, Q2
[DataRow(-179.9, 2, DisplayName = "Top_Az-179.9_Q2")]// El=75, Az=-179.9 -> Top, Q2
[DataRow(180.0, 2, DisplayName = "Top_Az180_Q2")] // Az 180 is equivalent to -180 for quadrant logic here (becomes Q2)
[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; // Top aspect
int expectedMajorAspectRow = 4; // Top
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, $"Failed for Az: {azimuthDeg}, El: {elevationDeg}. Expected Row {expectedMajorAspectRow}, Col {expectedQuadrantColumn}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"失败 for Az: {azimuthDeg}, El: {elevationDeg}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
[DataTestMethod]
// Similar DataRows as TopAspect, just for elevation -75
// 与顶部视角类似的数据行,仅高程为-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")]
@ -222,139 +205,135 @@ namespace ThreatSource.Tests.Equipment
public void GetRcsAngleBased_BottomAspect_ReturnsCorrectQuadrantValue(double azimuthDeg, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
double elevationDeg = -75.0; // Bottom aspect
int expectedMajorAspectRow = 5; // Bottom
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, $"Failed for Az: {azimuthDeg}, El: {elevationDeg}. Expected Row {expectedMajorAspectRow}, Col {expectedQuadrantColumn}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"失败 for Az: {azimuthDeg}, El: {elevationDeg}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// Front Aspect (Row 0)
// 正面视角 (行 0)
[DataTestMethod]
[DataRow(0.0, 30.0, 0, DisplayName = "Front_Az0_El30_Q0_TopRight")] // Az 0, El > 0 -> Front, Q0 (Right-Top)
[DataRow(44.9, 30.0, 0, DisplayName = "Front_Az44.9_El30_Q0_TopRight")] // Az 44.9, El > 0 -> Front, Q0
[DataRow(0.0, -30.0, 3, DisplayName = "Front_Az0_El-30_Q3_BottomRight")] // Az 0, El < 0 -> Front, Q3 (Right-Bottom)
[DataRow(44.9, -30.0, 3, DisplayName = "Front_Az44.9_El-30_Q3_BottomRight")]// Az 44.9, El < 0 -> Front, Q3
[DataRow(-0.1, 30.0, 1, DisplayName = "Front_Az-0.1_El30_Q1_TopLeft")] // Az -0.1, El > 0 -> Front, Q1 (Left-Top)
[DataRow(-44.9, 30.0, 1, DisplayName = "Front_Az-44.9_El30_Q1_TopLeft")] // Az -44.9, El > 0 -> Front, Q1
[DataRow(-0.1, -30.0, 2, DisplayName = "Front_Az-0.1_El-30_Q2_BottomLeft")] // Az -0.1, El < 0 -> Front, Q2 (Left-Bottom)
[DataRow(-44.9, -30.0, 2, DisplayName = "Front_Az-44.9_El-30_Q2_BottomLeft")]// Az -44.9, El < 0 -> Front, Q2
[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; // Front
int expectedMajorAspectRow = 0;
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Front Aspect - Failed for Az: {azimuthDeg}, El: {elevationDeg}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"正面视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// Back Aspect (Row 1) - Azimuth for back is 135 to -135 (or 135 to 225)
// 背面视角 (行 1) - 背面方位角为 135 到 -135 (或 135 到 225)
[DataTestMethod]
[DataRow(179.9, 30.0, 0, DisplayName = "Back_Az179.9_El30_Q0_TopRight")] // Az 179.9 (>0), El > 0 -> Back, Q0 (Simplified logic)
[DataRow(135.1, 30.0, 0, DisplayName = "Back_Az135.1_El30_Q0_TopRight")] // Az 135.1 (>0), El > 0 -> Back, Q0
[DataRow(179.9, -30.0, 3, DisplayName = "Back_Az179.9_El-30_Q3_BottomRight")]// Az 179.9 (>0), El < 0 -> Back, Q3
[DataRow(135.1, -30.0, 3, DisplayName = "Back_Az135.1_El-30_Q3_BottomRight")]// Az 135.1 (>0), El < 0 -> Back, Q3
[DataRow(-179.9, 30.0, 1, DisplayName = "Back_Az-179.9_El30_Q1_TopLeft")] // Az -179.9 (<0), El > 0 -> Back, Q1
[DataRow(-135.1, 30.0, 1, DisplayName = "Back_Az-135.1_El30_Q1_TopLeft")] // Az -135.1 (<0), El > 0 -> Back, Q1
[DataRow(-179.9, -30.0, 2, DisplayName = "Back_Az-179.9_El-30_Q2_BottomLeft")]// Az -179.9 (<0), El < 0 -> Back, Q2
[DataRow(-135.1, -30.0, 2, DisplayName = "Back_Az-135.1_El-30_Q2_BottomLeft")]// Az -135.1 (<0), El < 0 -> Back, Q2
[DataRow(180.0, 30.0, 1, DisplayName = "Back_Az180_El30_Q0_TopRight")] // Az 180.0, El > 0 -> Back, Q0 (Simplified logic)
[DataRow(-180.0, 30.0, 1, DisplayName = "Back_Az-180_El30_Q1_TopLeft")]// Az -180.0, El > 0 -> Back, Q1 (Simplified logic)
[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; // Back
int expectedMajorAspectRow = 1; // 背面
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Back Aspect - Failed for Az: {azimuthDeg}, El: {elevationDeg}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"背面视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// Right Aspect (Row 3) - Azimuth for right is 45 to 135
// 右侧视角 (行 3) - 右侧方位角为 45 到 135
[DataTestMethod]
[DataRow(45.1, 30.0, 0, DisplayName = "Right_Az45.1_El30_Q0_TopRight")] // Az 45.1 (<90), El > 0 -> Right, Q0
[DataRow(89.9, 30.0, 0, DisplayName = "Right_Az89.9_El30_Q0_TopRight")] // Az 89.9 (<90), El > 0 -> Right, Q0
[DataRow(45.1, -30.0, 3, DisplayName = "Right_Az45.1_El-30_Q3_BottomRight")]// Az 45.1 (<90), El < 0 -> Right, Q3
[DataRow(89.9, -30.0, 3, DisplayName = "Right_Az89.9_El-30_Q3_BottomRight")]// Az 89.9 (<90), El < 0 -> Right, Q3
[DataRow(90.1, 30.0, 1, DisplayName = "Right_Az90.1_El30_Q1_TopLeft")] // Az 90.1 (>=90), El > 0 -> Right, Q1
[DataRow(134.9, 30.0, 1, DisplayName = "Right_Az134.9_El30_Q1_TopLeft")] // Az 134.9 (>=90), El > 0 -> Right, Q1
[DataRow(90.1, -30.0, 2, DisplayName = "Right_Az90.1_El-30_Q2_BottomLeft")] // Az 90.1 (>=90), El < 0 -> Right, Q2
[DataRow(134.9, -30.0, 2, DisplayName = "Right_Az134.9_El-30_Q2_BottomLeft")]// Az 134.9 (>=90), El < 0 -> Right, Q2
[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; // Right
int expectedMajorAspectRow = 3; // 右侧
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Right Aspect - Failed for Az: {azimuthDeg}, El: {elevationDeg}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"右侧视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
// Left Aspect (Row 2) - Azimuth for left is -45 to -135
// 左侧视角 (行 2) - 左侧方位角为 -45 到 -135
[DataTestMethod]
[DataRow(-45.1, 30.0, 1, DisplayName = "Left_Az-45.1_El30_Q1_TopLeft")] // Az -45.1 (not < -90), El > 0 -> Left, Q1
[DataRow(-89.9, 30.0, 1, DisplayName = "Left_Az-89.9_El30_Q1_TopLeft")] // Az -89.9 (not < -90), El > 0 -> Left, Q1
[DataRow(-45.1, -30.0, 2, DisplayName = "Left_Az-45.1_El-30_Q2_BottomLeft")]// Az -45.1 (not < -90), El < 0 -> Left, Q2
[DataRow(-89.9, -30.0, 2, DisplayName = "Left_Az-89.9_El-30_Q2_BottomLeft")]// Az -89.9 (not < -90), El < 0 -> Left, Q2
[DataRow(-90.1, 30.0, 0, DisplayName = "Left_Az-90.1_El30_Q0_TopRight")] // Az -90.1 (< -90), El > 0 -> Left, Q0
[DataRow(-134.9, 30.0, 0, DisplayName = "Left_Az-134.9_El30_Q0_TopRight")] // Az -134.9 (< -90), El > 0 -> Left, Q0
[DataRow(-90.1, -30.0, 3, DisplayName = "Left_Az-90.1_El-30_Q3_BottomRight")]// Az -90.1 (< -90), El < 0 -> Left, Q3
[DataRow(-134.9, -30.0, 3, DisplayName = "Left_Az-134.9_El-30_Q3_BottomRight")]// Az -134.9 (< -90), El < 0 -> Left, Q3
[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; // Left
int expectedMajorAspectRow = 2; // 左侧
double expectedValue = (expectedMajorAspectRow + 1) * 100 + (expectedQuadrantColumn + 1) * 10;
double actualValue = rcsPattern.GetRcsValueDbSm(azimuthDeg, elevationDeg);
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Left Aspect - Failed for Az: {azimuthDeg}, El: {elevationDeg}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"左侧视角 - 失败 for Az: {azimuthDeg}, El: {elevationDeg}.");
}
[TestMethod]
public void GetRcsAngleBased_InvalidAngles_ReturnsDefaultRcs()
{
var rcsPattern = CreateAngleTestPattern(); // Content doesn't matter, expecting default
var rcsPattern = CreateAngleTestPattern();
// Elevation too high/low (should be clamped by method, but good to test boundary behavior of logic)
// The method's internal logic for row selection based on elevation > 70 or < -70 is robust.
// This test is more about extreme azimuths with valid elevations.
Assert.AreEqual(rcsPattern.GetRcsValueDbSm(720, 30), rcsPattern.GetRcsValueDbSm(0, 30), TEST_DELTA, "Azimuth 720 should be same as 0");
Assert.AreEqual(rcsPattern.GetRcsValueDbSm(-720, 30), rcsPattern.GetRcsValueDbSm(0, 30), TEST_DELTA, "Azimuth -720 should be same as 0");
// 高程过高/过低 (应由方法限制,但最好测试逻辑的边界行为)
// 该方法基于高程 > 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 相同");
// Test clamping of elevation
// If elevation is 95, it's clamped to 90. Row 4 (Top). Azimuth 0 -> Col 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 should be clamped to 90, Top Q0.");
Assert.AreEqual(expectedTopQ0, rcsPattern.GetRcsValueDbSm(0, 95), TEST_DELTA, "Elevation 95 应被限制为 90, 顶部 Q0.");
// If elevation is -95, it's clamped to -90. Row 5 (Bottom). Azimuth 0 -> Col 0.
// 如果高程为 -95则限制为 -90。行 5 (底部)。方位角 0 -> 列 0。
double expectedBottomQ0 = (5 + 1) * 100 + (0 + 1) * 10;
Assert.AreEqual(expectedBottomQ0, rcsPattern.GetRcsValueDbSm(0, -95), TEST_DELTA, "Elevation -95 should be clamped to -90, Bottom Q0.");
Assert.AreEqual(expectedBottomQ0, rcsPattern.GetRcsValueDbSm(0, -95), TEST_DELTA, "Elevation -95 应被限制为 -90, 底部 Q0.");
// Test if any unforeseen combination leads to the debug path returning DEFAULT_RCS_DBSM_IN_PATTERN
// This requires finding a case where majorAspectRow or quadrantColumn are out of bounds after logic.
// Given current logic, it's hard to hit the final 'else' that returns DEFAULT_RCS_DBSM_IN_PATTERN
// because azimuth and elevation normalization + selection logic seems to cover all inputs.
// We can however test the specific case where the underlying matrix might be default if not for CreateAngleTestPattern
var defaultPattern = new RcsPattern(); // All values are DEFAULT_RCS_DBSM_IN_PATTERN
// If GetRcsValueDbSm somehow failed to find a cell and returned DEFAULT_RCS_DBSM_IN_PATTERN from its final else,
// this would pass. This isn't a strong test for that path without more specific conditions.
// For now, we assume the existing DataRow tests cover valid cell selection.
// A more robust test for the fallback would be if matrix lookup itself failed, which is not what this method tests.
// 测试是否有任何意外组合导致调试路径返回 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 测试覆盖了有效的单元格选择。
// 对备用逻辑的更稳健测试是矩阵查找本身失败,这不是此方法测试的内容。
}
// Tests for GetRcsValueDbSm(Vector3D observerDirection, ...)
private readonly Vector3D TARGET_FORWARD = new Vector3D(1, 0, 0);
private readonly Vector3D TARGET_UP = new Vector3D(0, 1, 0);
private readonly Vector3D TARGET_RIGHT = new Vector3D(0, 0, 1);
// 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);
// Top Aspect (Row 4) - Observer high above
// 顶部视角 (行 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]
// Azimuths relative to target forward (X-axis), projected onto target's XZ plane (when view is Top)
// For Top view, localRight is targetRight (Z), localUp is -targetForward (-X)
// Q0 (LocalRight, LocalUp) -> Z>0, -X>0 => Z>0, X<0. (e.g. observer at (-1, 10, 1) -> Q0)
// Q1 (LocalLeft, LocalUp) -> -Z>0, -X>0 => Z<0, X<0. (e.g. observer at (-1, 10, -1) -> Q1)
// Q2 (LocalLeft, LocalDown) -> -Z>0, X>0 => Z<0, X>0. (e.g. observer at (1, 10, -1) -> Q2)
// Q3 (LocalRight, LocalDown) -> Z>0, X>0 => Z>0, X>0. (e.g. observer at (1, 10, 1) -> Q3)
[DataRow(-0.1, 10.0, 0.1, 0, DisplayName = "VectorTop_NearFwdSlightRight_Q0")] // X<0 (localUp), Z>0 (localRight) -> Q0 (Obs: -0.1, 10, 0.1)
[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
@ -363,21 +342,18 @@ namespace ThreatSource.Tests.Equipment
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 4; // Top
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 - Failed for Obs: {observerDirection}. Expected Row {expectedMajorAspectRow}, Col {expectedQuadrantColumn}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Top Aspect - 失败 for Obs: {observerDirection}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// Bottom Aspect (Row 5) - Observer far below
// 底部视角 (行 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]
// For Bottom view, localRight is targetRight (Z), localUp is targetForward (X)
// Q0 (LocalRight, LocalUp) -> Z>0, X>0. (e.g. observer at (1, -10, 1) -> Q0)
// Q1 (LocalLeft, LocalUp) -> -Z>0, X>0 => Z<0, X>0. (e.g. observer at (1, -10, -1) -> Q1)
// Q2 (LocalLeft, LocalDown) -> -Z>0, -X>0 => Z<0, X<0. (e.g. observer at (-1, -10, -1) -> Q2)
// Q3 (LocalRight, LocalDown) -> Z>0, -X>0 => Z>0, X<0. (e.g. observer at (-1, -10, 1) -> Q3)
[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
@ -386,29 +362,24 @@ namespace ThreatSource.Tests.Equipment
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ);
int expectedMajorAspectRow = 5; // Bottom
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 - Failed for Obs: {observerDirection}. Expected Row {expectedMajorAspectRow}, Col {expectedQuadrantColumn}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Bottom Aspect - 失败 for Obs: {observerDirection}. 预期行 {expectedMajorAspectRow}, 列 {expectedQuadrantColumn}.");
}
// Front Aspect (Row 0) - Observer in front (positive X relative to target forward if TARGET_FORWARD is (1,0,0))
// Global Azimuth near 0. observerDirection should have a large positive component along TARGET_FORWARD.
// 正面视角 (行 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]
// For Front view (majorAspectRow 0), localRight is targetRight, localUp is targetUp.
// Q0 (LocalRight, LocalUp) -> dot(obs, targetRight) >= 0, dot(obs, targetUp) >= 0.
// If targetRight is Z-axis, targetUp is Y-axis:
// Q0: Z>=0, Y>=0. Example: obs=(10, 1, 1)
// Q1: Z<0, Y>=0. Example: obs=(10, 1, -1)
// Q2: Z<0, Y<0. Example: obs=(10, -1, -1)
// Q3: Z>=0, Y<0. Example: obs=(10, -1, 1)
[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")]
// Test edge cases for dot products being zero
// 测试点积为零的边界情况
[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
@ -416,22 +387,17 @@ namespace ThreatSource.Tests.Equipment
public void GetRcsVectorBased_FrontAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ); // Observer is mostly along positive X
int expectedMajorAspectRow = 0; // Front
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 - Failed for Obs: {observerDirection}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Front Aspect - 失败 for Obs: {observerDirection}.");
}
// Back Aspect (Row 1) - Observer behind (negative X relative to target forward)
// 背面视角 (行 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]
// For Back view (majorAspectRow 1), localRight is -targetRight, localUp is targetUp.
// Q0 (LocalRight, LocalUp) -> dot(obs, -targetRight) >= 0, dot(obs, targetUp) >= 0 => dot(obs, targetRight) <=0, dot(obs, targetUp) >=0
// If targetRight is Z-axis, targetUp is Y-axis:
// Q0: Z<=0, Y>=0. Example: obs=(-10, 1, -1)
// Q1: Z>0, Y>=0. Example: obs=(-10, 1, 1)
// Q2: Z>0, Y<0. Example: obs=(-10, -1, 1)
// Q3: Z<=0, Y<0. Example: obs=(-10, -1, -1)
[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")]
@ -439,22 +405,17 @@ namespace ThreatSource.Tests.Equipment
public void GetRcsVectorBased_BackAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ); // Observer is mostly along negative X
int expectedMajorAspectRow = 1; // Back
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 - Failed for Obs: {observerDirection}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Back Aspect - 失败 for Obs: {observerDirection}.");
}
// Right Aspect (Row 3) - Observer to the right (positive Z relative to target right if TARGET_RIGHT is (0,0,1))
// 右侧视角 (行 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]
// For Right view (majorAspectRow 3), localRight is -targetForward, localUp is targetUp.
// Q0 (LocalRight, LocalUp) -> dot(obs, -targetForward) >= 0, dot(obs, targetUp) >= 0 => dot(obs, targetForward) <=0, dot(obs, targetUp) >=0
// If targetForward is X-axis, targetUp is Y-axis:
// Q0: X<=0, Y>=0. Example: obs=(-1, 1, 10)
// Q1: X>0, Y>=0. Example: obs=(1, 1, 10)
// Q2: X>0, Y<0. Example: obs=(1, -1, 10)
// Q3: X<=0, Y<0. Example: obs=(-1, -1, 10)
[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")]
@ -462,22 +423,17 @@ namespace ThreatSource.Tests.Equipment
public void GetRcsVectorBased_RightAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ); // Observer is mostly along positive Z
int expectedMajorAspectRow = 3; // Right
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 - Failed for Obs: {observerDirection}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Right Aspect - 失败 for Obs: {observerDirection}.");
}
// Left Aspect (Row 2) - Observer to the left (negative Z relative to target right)
// 左侧视角 (行 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]
// For Left view (majorAspectRow 2), localRight is targetForward, localUp is targetUp.
// Q0 (LocalRight, LocalUp) -> dot(obs, targetForward) >= 0, dot(obs, targetUp) >= 0
// If targetForward is X-axis, targetUp is Y-axis:
// Q0: X>=0, Y>=0. Example: obs=(1, 1, -10)
// Q1: X<0, Y>=0. Example: obs=(-1, 1, -10)
// Q2: X<0, Y<0. Example: obs=(-1, -1, -10)
// Q3: X>=0, Y<0. Example: obs=(1, -1, -10)
[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")]
@ -485,11 +441,11 @@ namespace ThreatSource.Tests.Equipment
public void GetRcsVectorBased_LeftAspect_ReturnsCorrectQuadrantValue(double obsX, double obsY, double obsZ, int expectedQuadrantColumn)
{
var rcsPattern = CreateAngleTestPattern();
var observerDirection = new Vector3D(obsX, obsY, obsZ); // Observer is mostly along negative Z
int expectedMajorAspectRow = 2; // Left
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 - Failed for Obs: {observerDirection}.");
Assert.AreEqual(expectedValue, actualValue, TEST_DELTA, $"Vector Left Aspect - 失败 for Obs: {observerDirection}.");
}
[TestMethod]
@ -497,88 +453,51 @@ namespace ThreatSource.Tests.Equipment
{
var rcsPattern = CreateAngleTestPattern();
// Observer directly along an axis (should pick a consistent quadrant)
// Directly Forward (X-axis): Should be Front (Row 0)
// Dot with Up (Y) is 0, Dot with Right (Z) is 0. isLocallyTop=true, isLocallyRight=true. -> Q0
double expectedFrontQ0 = (0 + 1) * 100 + (0 + 1) * 10; // Row 0, Col 0
// 观察者直接沿坐标轴 (应选择一个一致的象限)
// 正前方 (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.");
// Directly Up (Y-axis): Should be Top (Row 4)
// Dot with targetForward (X) is 0. isLocallyTopQuadrant for Top/Bottom is dot(obs, forward) >= 0 -> true.
// Dot with targetRight (Z) is 0. isLocallyRightQuadrant for Top/Bottom is dot(obs, right) >= 0 -> true.
// Row 4, Col 0 (LocalRight, LocalUp for Top view means Z>=0, -X>=0. Obs=(0,1,0) -> Z=0, X=0. -> Q0)
// Correction: For Top view (row 4), localUp is -forwardNorm. observerDirNorm is (0,1,0). localUp is (-1,0,0).
// isLocallyTopQuadrant = DotProduct((0,1,0), (-1,0,0)) = 0 >=0 -> true.
// localRight is rightNorm (0,0,1). isLocallyRightQuadrant = DotProduct((0,1,0), (0,0,1)) = 0 >=0 -> true.
// So Q0 for Top is correct.
double expectedTopQ0 = (4 + 1) * 100 + (0 + 1) * 10; // Row 4, Col 0
// 正上方 (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.");
// Observer direction is Zero vector (should normalize to something, or be handled gracefully)
// Vector3D.Normalize() of Zero vector returns Zero vector. Math.Asin(NaN) if DotProduct is weird.
// Current RcsPattern logic: Normalize of (0,0,0) is (0,0,0).
// Dot products will be 0. Asin(0)=0. GlobalEl=0, GlobalAz=0. -> Front (Row 0).
// localRight=Z, localUp=Y. isLocallyTop=true, isLocallyRight=true -> Q0.
// So, (0,0,0) observer direction should result in Front, Q0
// 零观察者向量: 归一化为 (0,0,0), 结果为正面 (行 0), Q0。
Assert.AreEqual(expectedFrontQ0, rcsPattern.GetRcsValueDbSm(Vector3D.Zero, TARGET_FORWARD, TARGET_UP, TARGET_RIGHT), TEST_DELTA, "Zero observer vector.");
// Test case where projectionOnHorizontalPlane is zero (observer directly above/below, not on main axis for az calc)
// Observer directly along Up vector (0,1,0) has already been tested and gives Top Q0.
// Observer directly along Down vector (0,-1,0) -> Bottom (Row 5)
// For Bottom (row 5), localUp is forwardNorm (X). isLocallyTopQuadrant = dot((0,-1,0), (1,0,0)) = 0 >=0 -> true.
// localRight is rightNorm (Z). isLocallyRightQuadrant = dot((0,-1,0), (0,0,1)) = 0 >=0 -> true.
// So Q0 for Bottom view.
double expectedBottomQ0 = (5+1)*100 + (0+1)*10; // Row 5, Col 0
// 观察者直接向下 (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()
{
// Arrange
// Create a matrix where average is easy to calculate: e.g., all values are 10.0
var testMatrix = CreateTestMatrix(6, 4, (r, c) => 10.0);
var rcsPattern = new RcsPattern(testMatrix);
double expectedAverage = 10.0;
// Act
double actualAverage = rcsPattern.GetAverageRcsDbSm();
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "平均值计算不正确.");
// Assert
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "Average calculation is incorrect.");
// Arrange: Test with varied values
var variedMatrix = CreateTestMatrix(); // Uses (r*10+c+1)
// Sum = (1+2+3+4) + (11+12+13+14) + ... + (51+52+53+54)
// Row sums: 10, 50, 90, 130, 170, 210. Total sum = 660.
// Count = 6 * 4 = 24.
// Expected average = 660 / 24 = 27.5
var variedMatrix = CreateTestMatrix();
rcsPattern = new RcsPattern(variedMatrix);
expectedAverage = 660.0 / 24.0;
// Act
actualAverage = rcsPattern.GetAverageRcsDbSm();
// Assert
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "Average calculation for varied matrix is incorrect.");
Assert.AreEqual(expectedAverage, actualAverage, TEST_DELTA, "平均值计算不正确.");
}
[TestMethod]
public void GetAverageRcsDbSm_WhenDataSourceIsNull_ReturnsDefault()
{
// Arrange
var rcsPattern = new RcsPattern
{
RcsMatrixDataSource = null! // Force invalid state
RcsMatrixDataSource = null!
};
// Act
double actualAverage = rcsPattern.GetAverageRcsDbSm();
// Assert
// When RcsMatrixDataSource is null, RcsMatrixData (getter) will return a default-filled grid.
// So, the average should be DEFAULT_RCS_DBSM_IN_PATTERN.
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, actualAverage, TEST_DELTA, "Average should be default when DataSource is null.");
// 如果 RcsMatrixDataSource 为 nullRcsMatrixData的getter将返回一个用默认值填充的网格因此平均值为默认值。
Assert.AreEqual(DEFAULT_RCS_DBSM_IN_PATTERN, actualAverage, TEST_DELTA, "当DataSource为null, 平均值应为默认值.");
}
}
}

View File

@ -43,7 +43,6 @@ namespace ThreatSource.Tests.Target
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0
@ -105,19 +104,6 @@ namespace ThreatSource.Tests.Target
Assert.AreEqual(0, _tank.Health);
}
[TestMethod]
public void GetRadarCrossSection_ReturnsExpectedValue()
{
// Arrange
Vector3D observerPosition = new(0, 0, 0);
// Act
double rcs = _tank.Properties.RadarCrossSection;
// Assert
Assert.AreEqual(BaseTargetDefaultRCS, rcs);
}
[TestMethod]
public void GetInfraredSignature_ReturnsExpectedValue()
{

View File

@ -46,7 +46,6 @@ namespace ThreatSource.Tests.Indicator
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -37,7 +37,6 @@ namespace ThreatSource.Tests.Indicator
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -36,7 +36,6 @@ namespace ThreatSource.Tests.Indicator
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -39,7 +39,6 @@ namespace ThreatSource.Tests.Indicator
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -233,7 +233,6 @@ namespace ThreatSource.Tests.Jamming
};
var targetProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -52,7 +52,6 @@ namespace ThreatSource.Tests.Jamming
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -64,7 +64,6 @@ namespace ThreatSource.Tests.Jamming
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -51,7 +51,6 @@ namespace ThreatSource.Tests.Jamming
var targetProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -71,7 +71,6 @@ namespace ThreatSource.Tests.Jamming
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0
@ -96,7 +95,6 @@ namespace ThreatSource.Tests.Jamming
var decoySourceProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -49,7 +49,6 @@ namespace ThreatSource.Tests.Jamming
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -61,7 +61,6 @@ namespace ThreatSource.Tests.Jamming
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -59,7 +59,6 @@ namespace ThreatSource.Tests.Jamming
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -231,7 +231,6 @@ namespace ThreatSource.Tests.Jamming
};
var targetProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -38,7 +38,6 @@ namespace ThreatSource.Tests.Jamming
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0
@ -235,7 +234,6 @@ namespace ThreatSource.Tests.Jamming
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0
@ -338,7 +336,6 @@ namespace ThreatSource.Tests.Jamming
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0
@ -448,7 +445,6 @@ namespace ThreatSource.Tests.Jamming
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -108,7 +108,6 @@ namespace ThreatSource.Tests.Missile
};
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -36,7 +36,6 @@ namespace ThreatSource.Tests.Missile
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -36,7 +36,6 @@ namespace ThreatSource.Tests.Missile
var tankProperties = new EquipmentProperties
{
RadarCrossSection = 1.0,
InfraredRadiationIntensity = 10.0,
UltravioletRadiationIntensity = 10.0,
MillimeterWaveRadiationIntensity = 10.0

View File

@ -13,7 +13,6 @@ Width = 3.2 # 宽度 (米)
Height = 2.8 # 高度 (米)
MaxSpeed = 80.0 # 最大速度 (千米/小时)
ArmorThickness = 400.0 # 装甲厚度 (毫米)
RadarCrossSection = 12.0 # 雷达散射截面积 (平方米)
InfraredRadiationIntensity = 2000.0 # 红外辐射强度 (瓦特/球面度)
UltravioletRadiationIntensity = 12.0 # 紫外辐射强度 (瓦特/球面度)
MillimeterWaveRadiationIntensity = 8.0 # 毫米波辐射强度 (瓦特/球面度)
@ -30,4 +29,14 @@ MovingPatternSource = [ # 移动时温度分布
[40, 45, 70],
[35, 40, 75],
[50, 50, 55]
]
]
[Properties.RcsPattern]
RcsMatrixDataSource = [
[10.0, 10.0, 10.0, 10.0], # 前 (Front) [Q1, Q2, Q3, Q4]
[10.0, 10.0, 10.0, 10.0], # 后 (Back)
[13.0, 13.0, 13.0, 13.0], # 左 (Left)
[13.0, 13.0, 13.0, 13.0], # 右 (Right)
[13.0, 13.0, 13.0, 13.0], # 上 (Top)
[13.0, 13.0, 13.0, 13.0] # 下 (Bottom)
]

View File

@ -13,7 +13,6 @@ Width = 3.0 # 宽度 (米)
Height = 4.5 # 高度 (米)
MaxSpeed = 280.0 # 最大速度 (千米/小时, JSON中为280C#中可能是米/秒,需确认)
ArmorThickness = 150.0 # 装甲厚度 (毫米)
RadarCrossSection = 8.0 # 雷达散射截面积 (平方米)
InfraredRadiationIntensity = 3000.0 # 红外辐射强度 (瓦特/球面度)
UltravioletRadiationIntensity = 20.0 # 紫外辐射强度 (瓦特/球面度)
MillimeterWaveRadiationIntensity = 6.0 # 毫米波辐射强度 (瓦特/球面度)
@ -30,4 +29,14 @@ MovingPatternSource = [ # 移动时温度分布
[90, 115, 85],
[40, 50, 45],
[35, 40, 35]
]
]
[Properties.RcsPattern]
RcsMatrixDataSource = [
[10.0, 10.0, 10.0, 10.0], # 前 (Front) [Q1, Q2, Q3, Q4]
[10.0, 10.0, 10.0, 10.0], # 后 (Back)
[13.0, 13.0, 13.0, 13.0], # 左 (Left)
[13.0, 13.0, 13.0, 13.0], # 右 (Right)
[13.0, 13.0, 13.0, 13.0], # 上 (Top)
[13.0, 13.0, 13.0, 13.0] # 下 (Bottom)
]

View File

@ -13,7 +13,6 @@ Width = 3.5 # 宽度 (米)
Height = 2.4 # 高度 (米)
MaxSpeed = 70.0 # 最大速度 (千米/小时)
ArmorThickness = 800.0 # 装甲厚度 (毫米)
RadarCrossSection = 15.0 # 雷达散射截面积 (平方米) - 保留作为通用/回退值
InfraredRadiationIntensity = 250.0 # 红外辐射强度 (瓦特/球面度)
UltravioletRadiationIntensity = 15.0 # 紫外辐射强度 (瓦特/球面度)
MillimeterWaveRadiationIntensity = 10.0 # 毫米波辐射强度 (瓦特/球面度)
@ -33,18 +32,6 @@ MovingPatternSource = [ # 移动时温度分布
]
[Properties.RcsPattern]
# RcsBinsSource 是一个 RcsAngularBin 对象的列表
# 每个对象定义特定角度扇区的RCS值
# 角度单位RCS单位dBsm
# HorAngle: 方位角,相对于目标前方 (0度)
# VerAngle: 俯仰角,相对于目标水平面 (0度)
# 数据基于用户提供的列表,假设非环绕范围的起始角度 < 结束角度
# 原始数据列: ID, HorStartCol2, RCSdBsmCol3, UnkCol4, HorEndCol5, VerStartCol6, VerEndCol7, AspectCol8
# 新的RCS矩阵表示
# 用户将填充实际的RCS值 (单位: dBsm)
[Properties.RcsPattern.RcsMatrix]
# Data 是一个 6x4 的二维数组
# 行顺序 (索引 0-5): 前, 后, 左, 右, 上, 下
# 列顺序 (索引 0-3): 对应每个主方向观察视角下的标准笛卡尔坐标系第1, 2, 3, 4象限
@ -53,11 +40,11 @@ MovingPatternSource = [ # 移动时温度分布
# 列1 (Q2): 左上区域
# 列2 (Q3): 左下区域
# 列3 (Q4): 右下区域
Data = [
[0.0, 0.0, 0.0, 0.0], # 前 (Front) [Q1, Q2, Q3, Q4]
[0.0, 0.0, 0.0, 0.0], # 后 (Back)
[0.0, 0.0, 0.0, 0.0], # 左 (Left)
[0.0, 0.0, 0.0, 0.0], # 右 (Right)
[0.0, 0.0, 0.0, 0.0], # 上 (Top)
[0.0, 0.0, 0.0, 0.0] # 下 (Bottom)
RcsMatrixDataSource = [
[10.0, 10.0, 10.0, 10.0], # 前 (Front) [Q1, Q2, Q3, Q4]
[10.0, 10.0, 10.0, 10.0], # 后 (Back)
[13.0, 13.0, 13.0, 13.0], # 左 (Left)
[13.0, 13.0, 13.0, 13.0], # 右 (Right)
[13.0, 13.0, 13.0, 13.0], # 上 (Top)
[13.0, 13.0, 13.0, 13.0] # 下 (Bottom)
]

View File

@ -1,9 +1,5 @@
using System.IO;
using Tomlyn;
using Tomlyn.Model; // Required for TomlModelOptions
using System;
using System.Diagnostics;
// No longer need Tomlyn.Syntax for exceptions if ConvertTo is removed
namespace ThreatSource.Data
{

View File

@ -5,7 +5,6 @@ using ThreatSource.Jammer;
using ThreatSource.Simulation;
using AirTransmission;
namespace ThreatSource.Data
{
/// <summary>

View File

@ -1,5 +1,4 @@
using ThreatSource.Simulation;
using ThreatSource.Utils;
namespace ThreatSource.Equipment
{

View File

@ -1,5 +1,4 @@
using ThreatSource.Simulation;
using ThreatSource.Utils;
namespace ThreatSource.Equipment
{
@ -111,7 +110,6 @@ namespace ThreatSource.Equipment
statusInfo.ExtendedProperties["Length"] = Properties.Length;
statusInfo.ExtendedProperties["Width"] = Properties.Width;
statusInfo.ExtendedProperties["Height"] = Properties.Height;
statusInfo.ExtendedProperties["RadarCrossSection"] = Properties.RadarCrossSection;
statusInfo.ExtendedProperties["InfraredRadiationIntensity"] = Properties.InfraredRadiationIntensity;
statusInfo.ExtendedProperties["MillimeterWaveRadiationIntensity"] = Properties.MillimeterWaveRadiationIntensity;
statusInfo.ExtendedProperties["UltravioletRadiationIntensity"] = Properties.UltravioletRadiationIntensity;

View File

@ -1,5 +1,3 @@
using ThreatSource.Utils;
using ThreatSource.Equipment; // Ensure RcsPattern and ThermalPattern are findable
namespace ThreatSource.Equipment
{
@ -104,14 +102,6 @@ namespace ThreatSource.Equipment
/// </remarks>
public double ArmorThickness { get; set; }
/// <summary>
/// 获取或设置目标的雷达散射截面积
/// </summary>
/// <remarks>
/// 单位:平方米
/// </remarks>
public double RadarCrossSection { get; set; }
/// <summary>
/// 获取或设置目标的红外辐射强度
/// </summary>
@ -184,10 +174,10 @@ namespace ThreatSource.Equipment
SetDefaultValues();
Type = "Tank"; // 默认类型为坦克
ThermalPattern = new ThermalPattern(
new double[ThreatSource.Equipment.ThermalPattern.GRID_SIZE, ThreatSource.Equipment.ThermalPattern.GRID_SIZE],
new double[ThreatSource.Equipment.ThermalPattern.GRID_SIZE, ThreatSource.Equipment.ThermalPattern.GRID_SIZE]
new double[ThermalPattern.GRID_SIZE, ThermalPattern.GRID_SIZE],
new double[ThermalPattern.GRID_SIZE, ThermalPattern.GRID_SIZE]
);
RcsPattern = new RcsPattern(); // Initialize with empty list, to be filled by deserializer
RcsPattern = new RcsPattern();
}
/// <summary>
@ -208,7 +198,6 @@ namespace ThreatSource.Equipment
Height = 2.4;
MaxSpeed = 70.0;
ArmorThickness = 800.0;
RadarCrossSection = 15.0;
InfraredRadiationIntensity = 2500.0;
UltravioletRadiationIntensity = 15.0;
MillimeterWaveRadiationIntensity = 10.0;

View File

@ -1,4 +1,3 @@
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Equipment

View File

@ -1,8 +1,5 @@
using ThreatSource.Utils; // 假设 Vector3D 在此命名空间下
using System.Linq; // For Average, if used later
using System.Collections.Generic; // Required for List<T>
using System; // Required for ArgumentNullException, ArgumentException, InvalidOperationException
using System.Diagnostics; // Required for Debug.WriteLine
using ThreatSource.Utils;
using System.Diagnostics;
namespace ThreatSource.Equipment
{
@ -15,7 +12,11 @@ namespace ThreatSource.Equipment
{
private const int RCS_MATRIX_ROWS = 6;
private const int RCS_MATRIX_COLS = 4;
private const double DEFAULT_RCS_DBSM = -20.0; // 示例默认值 -20 dBsm
/// <summary>
/// 默认RCS值 (dBsm)。
/// </summary>
public const double DEFAULT_RCS_DBSM = 10.0; // 默认值 10 dBsm
/// <summary>
/// RCS数据矩阵的源数据 (主要供Tomlyn等TOML解析器使用)。

View File

@ -1,7 +1,4 @@
using ThreatSource.Simulation;
using ThreatSource.Utils;
using ThreatSource.Jammer;
using System;
namespace ThreatSource.Equipment
{

View File

@ -1,4 +1,3 @@
using System;
using ThreatSource.Utils;
namespace ThreatSource.Guidance

View File

@ -1,11 +1,9 @@
using System;
using System.Linq; // Add Linq for FirstOrDefault
using ThreatSource.Equipment;
using ThreatSource.Utils;
using AirTransmission;
using ThreatSource.Jammer; // 添加引用
using ThreatSource.Jammer;
using ThreatSource.Simulation;
using System.Diagnostics; // Add Simulation namespace
using System.Diagnostics;
namespace ThreatSource.Guidance
{

View File

@ -1,11 +1,8 @@
using System;
using ThreatSource.Simulation;
using ThreatSource.Equipment;
using ThreatSource.Utils;
using ThreatSource.Jammer;
using System.Diagnostics;
using AirTransmission; // 添加引用
using System.Linq;
namespace ThreatSource.Guidance
{

View File

@ -1,8 +1,5 @@
using ThreatSource.Equipment;
using System.Diagnostics;
using ThreatSource.Simulation;
using System.Collections.Generic; // For Queue and List
using System.Linq; // For OrderByDescending
namespace ThreatSource.Guidance
{

View File

@ -5,8 +5,6 @@ using ThreatSource.Indicator;
using ThreatSource.Jammer;
using System.Diagnostics;
using AirTransmission;
using System.Transactions;
using System.Linq;
namespace ThreatSource.Guidance
{

View File

@ -5,7 +5,6 @@ using ThreatSource.Jammer;
using ThreatSource.Simulation;
using AirTransmission;
namespace ThreatSource.Guidance
{
/// <summary>
@ -551,7 +550,63 @@ namespace ThreatSource.Guidance
if (distance <= config.MaxDetectionRange && distance > 0)
{
double liveSmokeTransmittance = CalculateLiveSmokeTransmittance(missilePosition, target.KState.Position);
double snr_dB = CalculateSNR(distance, target.Properties.RadarCrossSection, liveSmokeTransmittance);
// --- 开始RCS获取逻辑 ---
double rcsDbSm = RcsPattern.DEFAULT_RCS_DBSM;
double rcsLinear;
Vector3D targetForward = Vector3D.Zero;
Vector3D targetRight = Vector3D.Zero;
Vector3D targetUp = Vector3D.Zero;
Vector3D observerVector = Vector3D.Zero;
bool isTargetOrientationValid = false;
if (target.KState != null)
{
targetForward = target.KState.Orientation.ToVector().Normalize();
if (targetForward.MagnitudeSquared() > 1e-10) // 确保从Orientation得到的Forward有效
{
Vector3D worldUpRef = Vector3D.UnitY;
if (Math.Abs(Vector3D.DotProduct(targetForward, worldUpRef)) > 0.99) // 目标几乎垂直运动
{
worldUpRef = Vector3D.UnitZ;
}
targetRight = Vector3D.CrossProduct(targetForward, worldUpRef).Normalize();
if (targetRight.MagnitudeSquared() > 1e-10) // 确保Right向量有效
{
targetUp = Vector3D.CrossProduct(targetRight, targetForward).Normalize();
if (targetUp.MagnitudeSquared() > 1e-10) // 确保Up向量有效
{
isTargetOrientationValid = true;
}
}
}
}
if (!isTargetOrientationValid)
{
Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 方向无效或 KState 为 null. 使用默认RCS ({RcsPattern.DEFAULT_RCS_DBSM} dBsm) 进行线性转换.");
}
if (target.Properties.RcsPattern != null)
{
if (isTargetOrientationValid)
{
observerVector = missilePosition - target.KState!.Position; // 从目标指向导弹 (观察者)
Debug.WriteLine($"[RCS获取] Target {target.Id}: observerVector: {observerVector}, targetForward: {targetForward}, targetUp: {targetUp}, targetRight: {targetRight}");
rcsDbSm = target.Properties.RcsPattern.GetRcsValueDbSm(observerVector, targetForward, targetUp, targetRight);
}
}
else
{
Debug.WriteLine($"[MMW_GUIDANCE] Target {target.Id}: 没有 RcsPattern 对象. 使用默认RCS ({RcsPattern.DEFAULT_RCS_DBSM} dBsm) 进行线性转换.");
}
// 单位转换 (dBsm -> 平方米)
rcsLinear = Math.Pow(10, rcsDbSm / 10.0);
Debug.WriteLine($"[RCS获取] Target {target.Id}: RCS_dBsm: {rcsDbSm}, RCS_Linear: {rcsLinear}, ");
double snr_dB = CalculateSNR(distance, rcsLinear, liveSmokeTransmittance);
if (currentMode == WorkMode.Search)
{
@ -559,7 +614,7 @@ namespace ThreatSource.Guidance
{
if (distance < minDistance)
{
targetPosition = target.KState.Position;
targetPosition = target.KState!.Position;
minDistance = distance;
foundTarget = true;
currentSNR_dB = snr_dB;
@ -572,7 +627,7 @@ namespace ThreatSource.Guidance
if (LastTargetPosition != null)
{
double proximityThreshold = (target.Properties.Length > 0) ? target.Properties.Length * 5.0 : 100.0;
isPotentiallyTrackedTarget = Vector3D.Distance(target.KState.Position, LastTargetPosition.Value) < proximityThreshold;
isPotentiallyTrackedTarget = Vector3D.Distance(target.KState!.Position, LastTargetPosition.Value) < proximityThreshold;
}
if (IsTargetInBeam(missileVelocity, toTarget) && isPotentiallyTrackedTarget)
@ -582,7 +637,7 @@ namespace ThreatSource.Guidance
if (snr_dB >= requiredSNR_dB)
{
targetPosition = target.KState.Position;
targetPosition = target.KState!.Position;
foundTarget = true;
currentSNR_dB = snr_dB;
break;

View File

@ -1,4 +1,3 @@
using ThreatSource.Utils;
using ThreatSource.Jammable;
namespace ThreatSource.Indicator

View File

@ -3,7 +3,6 @@ using ThreatSource.Utils;
using ThreatSource.Missile;
using ThreatSource.Jammer;
using System.Diagnostics;
using System; // Added for Action
namespace ThreatSource.Indicator
{

View File

@ -1,5 +1,4 @@
using ThreatSource.Simulation;
using System;
using ThreatSource.Utils;
using System.Diagnostics;
using ThreatSource.Jammer;

View File

@ -1,9 +1,6 @@
using ThreatSource.Jammer;
using System.Diagnostics;
using ThreatSource.Utils;
using System;
using System.Collections.Generic;
using System.Linq;
namespace ThreatSource.Jammable
{

View File

@ -1,7 +1,4 @@
using ThreatSource.Jammer;
using System; // Added for Action delegate
using System.Collections.Generic;
using System.Linq; // Added for Linq methods like Any() and FirstOrDefault()
namespace ThreatSource.Jammable
{

View File

@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using ThreatSource.Simulation;
using ThreatSource.Utils;
namespace ThreatSource.Jammer
{

View File

@ -1,5 +1,6 @@
using ThreatSource.Utils;
using ThreatSource.Simulation;
namespace ThreatSource.Jammer
{
/// <summary>

View File

@ -1,6 +1,4 @@
using ThreatSource.Simulation;
using ThreatSource.Utils;
using System; // For DateTime
namespace ThreatSource.Jammer
{

View File

@ -1,7 +1,4 @@
using System.Collections.Generic;
using System.Linq;
using ThreatSource.Simulation;
using ThreatSource.Utils;
namespace ThreatSource.Jammer
{

View File

@ -1,6 +1,7 @@
using ThreatSource.Jammable;
using ThreatSource.Jammer;
using ThreatSource.Simulation;
namespace ThreatSource.Sensor
{
/// <summary>

View File

@ -1,5 +1,6 @@
using ThreatSource.Utils;
using System.Diagnostics;
namespace ThreatSource.Sensor
{
/// <summary>

View File

@ -1,8 +1,3 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ThreatSource.Utils;
namespace ThreatSource.Simulation
{
/// <summary>

View File

@ -1,5 +1,3 @@
using ThreatSource.Utils;
namespace ThreatSource.Simulation
{
/// <summary>

View File

@ -1,6 +1,6 @@
using ThreatSource.Utils;
using ThreatSource.Jammer; // For Jammer configs
using AirTransmission; // Added for WeatherType enum
using ThreatSource.Jammer;
using AirTransmission;
namespace ThreatSource.Simulation
{

View File

@ -1,5 +1,3 @@
using ThreatSource.Utils;
namespace ThreatSource.Simulation
{
/// <summary>

View File

@ -1,6 +1,4 @@
using System;
using System.Diagnostics;
using System.Numerics;
namespace ThreatSource.Utils
{

View File

@ -1,4 +1,3 @@
using System.Diagnostics;
using System.Numerics;
namespace ThreatSource.Utils

View File

@ -1,4 +1,4 @@
using System; // Mathのため、System名前空間が必要
using System;
namespace ThreatSource.Utils
{