更新:1、航迹关联时增加高度门限判断,同时加大统计距离门限,提高机动目标关联概率;

2、修复TAS波束控制时,数据率判断有误的问题;
3、更新文档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
2026-09-22 09:20:12 +08:00
parent a76da34756
commit e0bf227be3
9 changed files with 237 additions and 5070 deletions
+1
View File
@@ -4,4 +4,5 @@ debug/
release/ release/
build/ build/
rdp_logs/ rdp_logs/
design_docs/
.mnemon/ .mnemon/
+1
View File
@@ -42,6 +42,7 @@
- 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 编译警告:源码文件改为带 BOM 的 UTF-8 编码;signed/unsigned 不匹配处改用 `size_t` 循环变量或显式 `static_cast<int>` 比较;double/float、__int64/double 等窄化转换改为 `static_cast` 显式转换,保持原有计算逻辑不变(涉及 `data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`tas_ctrl.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`track_index_mangement.cpp` 等)。 - 修复 MSVC `/W3` 下全部 C4819 / C4018 / C4244 编译警告:源码文件改为带 BOM 的 UTF-8 编码;signed/unsigned 不匹配处改用 `size_t` 循环变量或显式 `static_cast<int>` 比较;double/float、__int64/double 等窄化转换改为 `static_cast` 显式转换,保持原有计算逻辑不变(涉及 `data_process.cpp`、`dot_coh.cpp`、`dot_coh_tas.cpp`、`tas_ctrl.cpp`、`track_asso.cpp`、`track_asso_tas.cpp`、`track_init.cpp`、`track_init_direct_tracking.cpp`、`track_index_mangement.cpp` 等)。
- 修复 `TAS_Ctrl::tas_beam_output()` 把航迹向量下标 `earliest_track_idx` 误赋给 `Tracking_beam->TAS_track_index` 的问题(1521592):现输出实际航迹号 `(*trust_track)[earliest_track_idx].Track_Index`,避免跟踪波束批号与目标不符。 - 修复 `TAS_Ctrl::tas_beam_output()` 把航迹向量下标 `earliest_track_idx` 误赋给 `Tracking_beam->TAS_track_index` 的问题(1521592):现输出实际航迹号 `(*trust_track)[earliest_track_idx].Track_Index`,避免跟踪波束批号与目标不符。
- 修正 README.md(第 2、5.2、8.1、8.3、9.3、13 节)与 `docs/data_process_flow_report.md`(第 0、1、6.1~6.4 节、附录)中航迹起始的步骤顺序描述(纯文档勘误,无代码改动):两份文档原写作「航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批」,与 `track_init.cpp`(L49-126)每圈的实际执行顺序相反;现更正为「① 点迹先与 ≥2 点临时航迹关联(延长已有临时航迹)→ ② 再与 1 点航迹头关联(两点卡尔曼初始化出 2 点临时航迹)→ ③ 两个阶段都结束后统一删除被消费(`Use_Flag==1`)的点迹、剩余点迹逐条转为新航迹头 → ④ 长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹」,并同步修正流程图与状态机;另补充两点此前的描述缺失:两个关联阶段互不排斥(航迹头阶段不检查临时航迹阶段留下的 `Use_Flag`,同一点迹可同时延长一条临时航迹并开启一条 2 点临时航迹)、两类关联都不做一对一分配(同一条临时航迹可被同圈多个点延长)。
### Removed ### Removed
+52 -34
View File
@@ -53,7 +53,7 @@
- **双模式处理管线**:data_preprocess 按输入类型与扫描圈完成标志返回 0/1/2/3(TWS 以方位波位号 == 0 判为一圈完成,机扫转台反转不再误判),track_process 据此调度 TWS 凝聚/关联/起始或 TAS 凝聚/关联。 - **双模式处理管线**:data_preprocess 按输入类型与扫描圈完成标志返回 0/1/2/3(TWS 以方位波位号 == 0 判为一圈完成,机扫转台反转不再误判),track_process 据此调度 TWS 凝聚/关联/起始或 TAS 凝聚/关联。
- **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。 - **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。
- **逻辑法航迹起始**:航迹头(单点)→ 点-头速度法关联(两点)→ 点-临时航迹 EKF 波门 + 折角约束关联(三点及以上)→ 三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。 - **逻辑法航迹起始**:每圈按固定顺序推进——① 未关联点迹先与 ≥2 点临时航迹做 EKF 波门 + 折角约束关联(延长临时航迹),② 再与 1 点航迹头做速度法关联(两点卡尔曼初始化出 2 点临时航迹),③ 两步都做完后删除被消费的点迹、剩余点迹转成新航迹头,④ 长度达标的临时航迹经三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
- **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。 - **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。
- **最近邻数据关联**:TWS 按航迹距离分段收紧模型 3 波门并附加距离门限;TAS 附加按距离分段的高度门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。 - **最近邻数据关联**:TWS 按航迹距离分段收紧模型 3 波门并附加距离门限;TAS 附加按距离分段的高度门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。
- **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。 - **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。
@@ -253,7 +253,7 @@ classDiagram
| Dot_Coh_TAS | TAS 点迹凝聚(单帧,距离/方位/速度三维门限) | | Dot_Coh_TAS | TAS 点迹凝聚(单帧,距离/方位/速度三维门限) |
| Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出;未关联航迹外推;高度平滑;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 | | Track_Asso | TWS 点航关联 + IMM 滤波:模型交互 → 门限筛选(NN)→ 各模型 EKF → 概率更新 → 模型输出;未关联航迹外推;高度平滑;ΔT≤0(乱序/重复时间戳)的候选取出后不更新滤波、不刷新航迹,仅消费该点迹 |
| Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异) | | Track_Asso_Tas | TAS 航迹关联(只处理指定批号 tas_track_idx,门限与 TWS 略有差异) |
| Track_Init | 逻辑法航迹起始:航迹头维护、点-头关联(两点)、点-临时航迹关联(三点及以上)、起批(三点卡尔曼初始化)、屏蔽区判断、临时航迹消亡 | | Track_Init | 逻辑法航迹起始,每圈顺序固定:点-临时航迹关联(延长 ≥2 点临时航迹)→ 点-航迹头关联(两点起始)→ 剩余点转新航迹头 → 长度达标者起批(三点卡尔曼初始化);另含起批屏蔽区判断与临时航迹消亡 |
| Track_Die / Track_Die_Tas | 可靠航迹消亡:外推轮数超限或手动删除标志置位时输出消亡批号并删除 | | Track_Die / Track_Die_Tas | 可靠航迹消亡:外推轮数超限或手动删除标志置位时输出消亡批号并删除 |
| TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output:数据率门控 + 队内选择最早 CPI 目标)、调度时刻记录(last_track_time) | | TAS_Ctrl | TAS 波束控制:手动目标入队(tas_target_add)、失效目标出队(tas_target_del)、跟踪波束预测输出(tas_beam_output:数据率门控 + 队内选择最早 CPI 目标)、调度时刻记录(last_track_time) |
| Track_Ind_Mangement | 航迹号 1~500 的顺序分配与回绕复用 | | Track_Ind_Mangement | 航迹号 1~500 的顺序分配与回绕复用 |
@@ -358,7 +358,7 @@ flowchart TD
C --> D["dot_coh 两帧滑窗凝聚 → point_recv"] C --> D["dot_coh 两帧滑窗凝聚 → point_recv"]
D --> E["track_asso: IMM 交互 → 点航波门筛选(NN)→ EKF 滤波 → 模型输出"] D --> E["track_asso: IMM 交互 → 点航波门筛选(NN)→ EKF 滤波 → 模型输出"]
E --> F["track_die: 外推超限/手动删除 → 输出消亡批号"] E --> F["track_die: 外推超限/手动删除 → 输出消亡批号"]
F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 起批转可靠航迹"] F --> G["track_init: 点-临时航迹关联 → 点-航迹头关联 → 剩余点转新航迹头 → 起批转可靠航迹"]
G --> H["输出 Trust_Track_Output(含起批历史点)"] G --> H["输出 Trust_Track_Output(含起批历史点)"]
H --> I["Beam_Ctrl: 手动 TAS 目标入队 / 波束输出"] H --> I["Beam_Ctrl: 手动 TAS 目标入队 / 波束输出"]
``` ```
@@ -382,20 +382,23 @@ flowchart TD
```mermaid ```mermaid
flowchart TD flowchart TD
A["未关联点迹"] --> B["转航迹头 Temp_track(1点) buff_round=1"] A["本圈未关联点迹(point_recv → point_process)"] --> A1["临时航迹所有点的 buff_round +1"]
B --> C{"下一帧点迹"} A1 --> B["① 点-临时航迹关联(≥2 点临时航迹、末点 buff_round≥2):EKF 统计距离波门 d²≤64、折角<60°、径向速度同号、|Δr|≥5m → 复制原航迹并追加新点"]
C --> D["点-航迹头关联:V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT、径向速度同号、dis ≥ |vr_point|·ΔT 且 dis ≥ |v_track_head|·ΔT → 两点卡尔曼初始化(航迹头→2 点临时航迹)"] B --> C["② 点-航迹头关联(1 点航迹头、buff_round≥2):V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT、径向速度同号、dis ≥ |vr_point|·ΔT 且 dis ≥ |v_track_head|·ΔT → 两点卡尔曼初始化(航迹头 + 新点 → 2 点临时航迹)"]
D --> E{"后续点迹"} C --> D["③ 删除 Use_Flag==1 的点迹,剩余点全部转为新航迹头 Temp_track(1 点、buff_round=1)"]
E --> F["点-临时航迹关联:EKF 统计距离波门 d²≤64、折角<60°、径向速度同号、|Δr|≥5m"] D --> E{"④ 长度 ≥ track_start_point_num(3~9) 且 ≤10 且不在起批屏蔽区 ?"}
F --> G{"长度 ≥ track_start_point_num(3~9) 且 ≤10"} E -- 是 --> F["重复航迹去重(保留末点 d 更小者)→ 三点卡尔曼初始化"]
G -- 是 --> H["屏蔽区判断 → 重复航迹去重(保留末点 d 更小者)→ 三点卡尔曼初始化"] F --> G["转可靠航迹:分配航迹号 1~500,IMM 三模型同状态初始化 u=1/3"]
H --> I["转可靠航迹:分配航迹号 1~500,IMM 三模型同状态初始化 u=1/3"] G --> H["输出前 track_start_point_num 个历史点 Trust_Track_Output[i][0..L-1]"]
I --> J["输出前 track_start_point_num 个历史点 Trust_Track_Output[i][0..L-1]"] E -- 否 --> I["留在临时航迹表,下一圈继续关联"]
G -- 否 --> K{"buff_round>2 或 长度≥10 ?"} H --> J["⑤ 临时航迹消亡:末点 buff_round>2 或 长度≥10"]
K -- 是 --> L["临时航迹消亡"] I --> J
K -- 否 --> E
``` ```
> 顺序说明:`track_init_process_logic()` 中四步的先后顺序固定、不可颠倒——先延长已有临时航迹,再与航迹头配对,两步都走完才生成新航迹头,最后才把长度达标的临时航迹转为可靠航迹(因此当圈新建的 2 点临时航迹不可能当圈起批,长度刚达标的临时航迹则可以当圈起批);临时航迹消亡在第 ⑤ 步、即全部起始动作之后执行。
>
> 第 ① ② 步**互不排斥**:两者都只把命中的点迹标记 `Use_Flag=1`,航迹头关联并不检查临时航迹关联留下的标记,因此同一点迹可能既延长一条临时航迹、又开启一条新的两点临时航迹;两类关联也都不做一对一分配(同一条临时航迹可被本圈多个点延长,同一个航迹头也可与本圈多个点配对)。只有走完两步后才统一删除 `Use_Flag==1` 的点迹,其余点迹成为新航迹头。
--- ---
## 9. 算法实现详解 ## 9. 算法实现详解
@@ -447,7 +450,34 @@ $$
### 9.3 航迹起始(逻辑法) ### 9.3 航迹起始(逻辑法)
#### (1) 点-航迹头关联(两点起始) 每圈(model=1)在 `track_init_process_logic()` 中按**固定顺序**执行下列四步,顺序不可颠倒:
1. **点-临时航迹关联**:本圈未关联点迹优先延长已有的 ≥2 点临时航迹(判据见 (1));
2. **点-航迹头关联**:随后点迹与 1 点航迹头配对,产生新的 2 点临时航迹(判据见 (2));
3. **剩余点迹转航迹头**:前两步只把命中的点迹标记为已使用(`Use_Flag=1`),两步都做完后才统一删除这些点迹,其余点迹逐条转为新航迹头(见 (3));
4. **转可靠航迹(起批)**:长度达标的临时航迹经三点卡尔曼初始化转为可靠航迹(见 (4));起批动作完成后才消亡不满足条件的临时航迹。
> 两点补充:① 第 1、2 步**互不排斥**——航迹头关联不检查临时航迹关联留下的 `Use_Flag`,因此同一点迹可能既延长一条临时航迹、又开启一条新的两点临时航迹;两类关联也都不做一对一分配(同一条临时航迹可被本圈多个点延长,同一个航迹头也可与本圈多个点配对)。② 每圈入口先把**所有**临时航迹所有点的 `buff_round` 加 1 再做关联,新建点(航迹头或被关联上的新点)`buff_round = 1`,因此当圈新建的 2 点临时航迹要到下一圈末点 `buff_round` 变为 2 之后才参与第 1 步。
#### (1) 点-临时航迹关联(延长 ≥2 点临时航迹)
对 ≥ 2 点的临时航迹(末点 buff_round ≥ 2),用航迹末点状态做 EKF 一步预测,计算三维(距离-方位-径向速度)马氏统计距离 d(见 9.5.3),并要求 |r_point − r_temp| ≥ 5 m,同时满足:
$$
d^2 \le TRACK\_START\_THRESHOLD^2 = 64,\qquad
\alpha < ALPHA\_START = 60°,\qquad
v_{r,point}\cdot v_{r,track}>0
$$
折角 α 为最近三点连线夹角(度):
$$
\alpha=\arccos\left(\frac{\vec{D_{12}}\cdot\vec{D_{23}}}{\|\vec{D_{12}}\|\,\|\vec{D_{23}}\|}\right)\cdot\frac{180}{\pi}
$$
关联成功后**复制原临时航迹并追加该点迹**,新航迹的 4 维状态用最近两点做**两点卡尔曼初始化**(见 9.5.2)重新得到。
#### (2) 点-航迹头关联(两点起始)
对单点航迹头(buff_round ≥ 2),量测点需同时满足以下四个条件(7d5a56f 起的判据): 对单点航迹头(buff_round ≥ 2),量测点需同时满足以下四个条件(7d5a56f 起的判据):
@@ -462,25 +492,13 @@ $$
> 历史行为(已注释停用):原先按扫描体制与距离分段收紧速度门限——近程 r≥1000 用全速、r<1000 用半速;中远程 r≥2000 用全速、r<2000 取下限 1/2·V_min·ΔT / 上限 1/3·V_max·ΔT。 > 历史行为(已注释停用):原先按扫描体制与距离分段收紧速度门限——近程 r≥1000 用全速、r<1000 用半速;中远程 r≥2000 用全速、r<2000 取下限 1/2·V_min·ΔT / 上限 1/3·V_max·ΔT。
关联成功用 **两点卡尔曼初始化**(见 9.5.2)。 关联成功用 **两点卡尔曼初始化**(见 9.5.2)生成 2 点临时航迹:第 1 点为原航迹头,第 2 点为该点迹。
#### (2) 点-临时航迹关联(三点及以上) #### (3) 剩余点迹转航迹头
对 ≥ 2 点的临时航迹,用航迹末点状态做 EKF 一步预测,计算三维(距离-方位-径向速度)马氏统计距离 d(见 9.5.3),并要求 |r_point − r_head| ≥ 5 m,同时满足: 第 (1)(2) 步结束后统一删除 `Use_Flag==1` 的点迹;剩余点迹回写 `point_recv`(随后被整体清空),并**逐条新建 1 点临时航迹(航迹头)**:位置直接取点迹的直角坐标、速度置 0、`buff_round = 1`。新航迹头当圈不再参与任何关联,只能从下一圈开始按第 (2) 步配对。
$$ #### (4) 转可靠航迹(起批)
d^2 \le TRACK\_START\_THRESHOLD^2 = 64,\qquad
\alpha < ALPHA\_START = 60°,\qquad
v_{r,point}\cdot v_{r,track}>0
$$
折角 α 为最近三点连线夹角(度):
$$
\alpha=\arccos\left(\frac{\vec{D_{12}}\cdot\vec{D_{23}}}{\|\vec{D_{12}}\|\,\|\vec{D_{23}}\|}\right)\cdot\frac{180}{\pi}
$$
#### (3) 转可靠航迹(起批)
临时航迹长度 N 满足 track_start_point_num ≤ N ≤ 10(起批点数由参数给定,典型 3~4,接口层校验 3~9)且不在起批屏蔽区内(屏蔽区为距离-方位矩形区域,最多 30 个)时进入起批队列;同源(前三点时间/距离相同)的重复航迹只保留 d 较小者(比较下标 `track_start_point_num-1` 处的 d)。起批时令 L = track_start_point_num,用航迹中下标 L-3、L-2、L-1 的三个点做 **三点卡尔曼初始化** 得到 6 维状态(航迹长度恰为 L 时即最近三点),构造 Trust_Track: 临时航迹长度 N 满足 track_start_point_num ≤ N ≤ 10(起批点数由参数给定,典型 3~4,接口层校验 3~9)且不在起批屏蔽区内(屏蔽区为距离-方位矩形区域,最多 30 个)时进入起批队列;同源(前三点时间/距离相同)的重复航迹只保留 d 较小者(比较下标 `track_start_point_num-1` 处的 d)。起批时令 L = track_start_point_num,用航迹中下标 L-3、L-2、L-1 的三个点做 **三点卡尔曼初始化** 得到 6 维状态(航迹长度恰为 L 时即最近三点),构造 Trust_Track:
@@ -851,7 +869,7 @@ $$
- 起批判据对马氏距离平方再次平方(`d*d ≤ TRACK_START_THRESHOLD²`),实际门限为 `d ≤ 8`,按字面 `d² ≤ 64` 理解会严重放宽;调 `TRACK_START_THRESHOLD` 时需注意这一层。 - 起批判据对马氏距离平方再次平方(`d*d ≤ TRACK_START_THRESHOLD²`),实际门限为 `d ≤ 8`,按字面 `d² ≤ 64` 理解会严重放宽;调 `TRACK_START_THRESHOLD` 时需注意这一层。
- 折角计算 `acos` 无定义域钳位,浮点越界产生 NaN 时条件恒假(静默丢弃该关联)。 - 折角计算 `acos` 无定义域钳位,浮点越界产生 NaN 时条件恒假(静默丢弃该关联)。
- 起批输出数组行号可能越过容量:容量判据用 `trust_track->size() < MAX_TRACK_INDEX`,而输出行计数在 track_asso 阶段(track_die 删除航迹之前)已推进,极端场景(当圈消亡 ≥ 2 条且起批占满配额)可写到 `MAX_TRACK_NUM` 之外。 - 起批输出数组行号可能越过容量:容量判据用 `trust_track->size() < MAX_TRACK_INDEX`,而输出行计数在 track_asso 阶段(track_die 删除航迹之前)已推进,极端场景(当圈消亡 ≥ 2 条且起批占满配额)可写到 `MAX_TRACK_NUM` 之外。
- 起批输出有多个字段未赋值(见 9.3(3) 与 docs/track_init_readme.md 第 14 节),宿主不应依赖这些字段。 - 起批输出有多个字段未赋值(见 9.3(4) 与 docs/track_init_readme.md 第 14 节),宿主不应依赖这些字段。
--- ---
@@ -895,7 +913,7 @@ $$
|---|---| |---|---|
| CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) | | CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) |
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示) | | docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示) |
| docs/data_process_flow_report.html | 上文同内容的 HTML 渲染版 | | docs/data_process_flow_report.html | 上文同内容的 HTML 渲染版;⚠ 该文件的航迹起始步骤顺序仍是修正前的旧表述(未随 md 同步,本次未改动),以 md 与源码为准 |
| docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 | | docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 |
| archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 | | archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 |
| archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) | | archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) |
+2 -2
View File
@@ -52,7 +52,7 @@
#define TRACK_DIE_ROUND 4 // 航迹消亡时间 #define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 96 // #define TRACK_DIE_ROUND_TAS 96 //
#define ASSO_THORD 3 #define ASSO_THORD 4
#define H_F_WIN_LEN 3 #define H_F_WIN_LEN 3
@@ -101,7 +101,7 @@
#define TRACK_DIE_ROUND 4 // 航迹消亡时间 #define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 5 #define TRACK_DIE_ROUND_TAS 5
#define ASSO_THORD 3 #define ASSO_THORD 4
#define H_F_WIN_LEN 3 #define H_F_WIN_LEN 3
+6 -5
View File
@@ -263,6 +263,7 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
long long CPI_time = 0; long long CPI_time = 0;
long long earliest_CPI_time = LLONG_MAX; long long earliest_CPI_time = LLONG_MAX;
float delta_T = 0; float delta_T = 0;
float delta_T_last_track = 0;
double H_track = 0; double H_track = 0;
int earliest_track_idx=-1; int earliest_track_idx=-1;
int earliest_tas_queue_idx=-1; int earliest_tas_queue_idx=-1;
@@ -281,13 +282,14 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
if((*trust_track)[track_idx].Track_Index == tas_target_queue[tas_idx].Index) if((*trust_track)[track_idx].Track_Index == tas_target_queue[tas_idx].Index)
{ {
CPI_time = (*trust_track)[track_idx].T_track; CPI_time = (*trust_track)[track_idx].T_track;
delta_T = (latest_timestamp - CPI_time) / 1000.0f; delta_T_last_track = (latest_timestamp - tas_target_queue[tas_idx].last_track_time) / 1000.0f;
//查找最早的CPI时间, 航迹时间与当前最新时间戳的差值大于 DATA_RATE_TAS 时才输出TAS跟踪波束 //查找CPI时间最早,且满足TAS数据率的目标
if(CPI_time < earliest_CPI_time && (latest_timestamp - tas_target_queue[tas_idx].last_track_time) / 1000.0f > Work_Parameter.DATA_RATE_TAS && delta_T > Work_Parameter.DATA_RATE_TAS) if(CPI_time < earliest_CPI_time && (delta_T_last_track > Work_Parameter.DATA_RATE_TAS))
{ {
earliest_CPI_time = CPI_time; earliest_CPI_time = CPI_time;
H_track = (*trust_track)[track_idx].Height; H_track = (*trust_track)[track_idx].Height;
delta_T = (latest_timestamp - CPI_time) / 1000.0f;
earliest_track_idx = track_idx; earliest_track_idx = track_idx;
earliest_tas_queue_idx = tas_idx; earliest_tas_queue_idx = tas_idx;
@@ -295,7 +297,6 @@ void TAS_Ctrl::tas_beam_output(std::vector<Trust_Track> *trust_track
for(int ii=0;ii<6;ii++){ for(int ii=0;ii<6;ii++){
X_now[ii]=(*trust_track)[track_idx].X[ii]; X_now[ii]=(*trust_track)[track_idx].X[ii];
} }
} }
break; break;
+95 -144
View File
@@ -417,157 +417,108 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
//遍历所有点迹航迹 计算点迹和航迹的统计距离 //遍历所有点迹航迹 计算点迹和航迹的统计距离
for (size_t loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++) for (size_t loop_of_track = 0; loop_of_track<trust_track->size();loop_of_track++)
{ {
// if((*trust_track)[loop_of_track].manual_tracking_flag==0) (*trust_track)[loop_of_track].point_flag = 0; //航迹的point_flag置为0 关联上点后再置为1
// {
(*trust_track)[loop_of_track].point_flag = 0; //航迹的point_flag置为0 关联上点后再置为1
for (size_t loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++) for (size_t loop_of_point = 0; loop_of_point<point_process.size();loop_of_point++)
{ {
//时间差 //时间差
double delta_T = (point_process[loop_of_point].CPI_Time - (*trust_track)[loop_of_track].T_track)/1000.0; double delta_T = (point_process[loop_of_point].CPI_Time - (*trust_track)[loop_of_track].T_track)/1000.0;
//航迹X P //航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6]; double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double)); memcpy(X1,(*trust_track)[loop_of_track].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[loop_of_track].X2,6*sizeof(double)); memcpy(X2,(*trust_track)[loop_of_track].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[loop_of_track].X3,6*sizeof(double)); memcpy(X3,(*trust_track)[loop_of_track].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[loop_of_track].P1,6*6*sizeof(double)); memcpy(P1,(*trust_track)[loop_of_track].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[loop_of_track].P2,6*6*sizeof(double)); memcpy(P2,(*trust_track)[loop_of_track].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[loop_of_track].P3,6*6*sizeof(double)); memcpy(P3,(*trust_track)[loop_of_track].P3,6*6*sizeof(double));
double v_track = sqrt(pow((*trust_track)[loop_of_track].X[1],2)+pow((*trust_track)[loop_of_track].X[4],2)); double v_track = sqrt(pow((*trust_track)[loop_of_track].X[1],2)+pow((*trust_track)[loop_of_track].X[4],2));
double h_track = (*trust_track)[loop_of_track].Height; double h_track = (*trust_track)[loop_of_track].Height;
double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2)); double r_track = sqrt(pow((*trust_track)[loop_of_track].X[0],2)+pow((*trust_track)[loop_of_track].X[3],2));
//量测信息 //量测信息
double Z[3] = {point_process[loop_of_point].Range, double Z[3] = {point_process[loop_of_point].Range,
point_process[loop_of_point].Azimuth, point_process[loop_of_point].Azimuth,
point_process[loop_of_point].Velocity}; point_process[loop_of_point].Velocity};
double prt = point_process[loop_of_point].PRF_index; double prt = point_process[loop_of_point].PRF_index;
double freq_ind = point_process[loop_of_point].Freq_index; double freq_ind = point_process[loop_of_point].Freq_index;
double h_point = point_process[loop_of_point].Height; double h_point = point_process[loop_of_point].Height;
//计算统计距离d //计算统计距离d
double d1,d2,d3; double d1,d2,d3;
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter); IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter);
//计算高度门限 //判定统计距离门限,thd_d
// double d_h; bool thd_d = false;
// if (r_track<1000)
// {
// if(abs(h_track-h_point)<=150)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// } if( (r_track <= 500 && \
// else if (r_track<2000 && r_track>=1000) (d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD / 1000.0)) || \
// { ((r_track > 500 && r_track < 1000) && \
// if(abs(h_track-h_point)<=200) (d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD / 100.0)) || \
// { (r_track >= 1000 && \
// d_h = 1; (d1*d1 < ASSO_THORD*ASSO_THORD || d2*d2 < ASSO_THORD*ASSO_THORD || d3*d3 < ASSO_THORD*ASSO_THORD))\
// } ){
// else thd_d = true;
// {
// d_h = 0;
// }
// }
// else if(r_track>=2000 && r_track<3000 )
// {
// if(abs(h_track-h_point)<=200)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// }
// else if(r_track>=3000 && r_track<5000)
// {
// if(abs(h_track-h_point)<=300)
// {
// d_h = 1;
// }
// else
// {
// d_h = 0;
// }
// }
// else
// {
// d_h = 1;
// }
// std::cout << delta_T<<" "<<d1<<" "<<d2<<" "<<d3;
// std::cout << h_track<<" "<<h_point<<" "<<d_h;
// d_h = 1;
//计算俯仰门限
bool d_p = true;
// double p_track = atan2(h_track, r_track); //航迹俯仰
// double p_point = asin(h_point/point_process[loop_of_point].Range); //点迹俯仰
// if( (r_track > 3000 && abs(p_track - p_point)*180/PI <= 7) || \
// (r_track > 1000 && r_track <= 3000 && abs(p_track - p_point)*180/PI <= 5) || \
// (r_track <= 1000 && abs(p_track - p_point)*180/PI <= 10)){
// d_p = true;
// }
//计算距离门限,R=航迹点与量测点的距离,VT=航向速度乘以扫描时间差
double& t_x0 = (*trust_track)[loop_of_track].X[0];
double& t_y0 = (*trust_track)[loop_of_track].X[3];
double a_track = atan2(t_y0, t_x0); //航迹方位角
double& r_point = point_process[loop_of_point].Range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].Azimuth); //方位差
if(a_delta > PI){
a_delta = 2*PI - a_delta;
}
double R = sqrt(r_track * r_track + r_point*r_point - 2*r_track*r_point*cos(a_delta));
double VT = v_track*delta_T;
bool d_r = true;
if(VT < 100 && R > 300){
//VT<100m,使用固定门限300m
d_r = false;
}else if((VT>=100 && VT<=300 && R/VT>3)){
d_r = false;
}else if(VT>300 && R > 1200){
d_r = false;
}
//小于关联门限 保存关联信息
if(
( r_track<=500 && (d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD/1000.0) && d_p && d_r)//
||( (r_track>500&&r_track<1000) && (d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD/100.0) && d_p && d_r)//
||( r_track>=1000 && (d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD) && d_p && d_r)//
)
{
//std::cout << "asooooooooooo";
struct asso_info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
associated_info_tmp.track_index=static_cast<int>(loop_of_track)+1;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info.push_back(associated_info_tmp);
}
} }
//判定高度门限,thd_h
bool thd_h = false;
double delta_h = abs(h_track-h_point);
if( (r_track <= 1000 && delta_h <= 150) || \
((r_track > 1000 && r_track <= 2000) && delta_h <= 200) || \
((r_track > 2000 && r_track <= 3000) && delta_h <= 250) || \
((r_track > 3000 && r_track <= 5000) && delta_h <= 300) || \
r_track > 5000
){
thd_h = true;
}
//判定距离门限,R=航迹点与量测点的距离,VT=航向速度乘以扫描时间差
double& t_x0 = (*trust_track)[loop_of_track].X[0];
double& t_y0 = (*trust_track)[loop_of_track].X[3];
double a_track = atan2(t_y0, t_x0); //航迹方位角
double& r_point = point_process[loop_of_point].Range; //点迹距离
double a_delta = abs(a_track - point_process[loop_of_point].Azimuth); //方位差
if(a_delta > PI){
a_delta = 2*PI - a_delta;
}
double R = sqrt(r_track * r_track + r_point*r_point - 2*r_track*r_point*cos(a_delta));
double VT = v_track*delta_T;
bool thd_r = true;
if(VT < 100 && R > 300){
//VT<100m,使用固定门限300m
thd_r = false;
}else if((VT>=100 && VT<=300 && R/VT>3)){
thd_r = false;
}else if(VT>300 && R > 1200){
thd_r = false;
}
//小于关联门限 保存关联信息
if(thd_d && thd_h && thd_r)
{
//std::cout << "asooooooooooo";
struct asso_info associated_info_tmp;
associated_info_tmp.d1=d1;
associated_info_tmp.d2=d2;
associated_info_tmp.d3=d3;
associated_info_tmp.point_index=static_cast<int>(loop_of_point)+1;
associated_info_tmp.track_index=static_cast<int>(loop_of_track)+1;
if(d1<=d2&&d1<=d3)
associated_info_tmp.d_min=d1;
else if(d2<=d1&& d2<=d3)
associated_info_tmp.d_min=d2;
else
associated_info_tmp.d_min=d3;
associated_info.push_back(associated_info_tmp);
}
}
// } // }
} }
+17 -54
View File
@@ -289,65 +289,28 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
double d1,d2,d3; double d1,d2,d3;
IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter); IMM_d_cal(v_track,X1,P1,X2,P2, X3,P3,Z,prt,freq_ind,delta_T,&d1,&d2,&d3,Work_Parameter);
//计算高度门限 //判定统计距离门限,thd_d
double d_h; bool thd_d = false;
if (r_track<1000)
{
if(abs(h_track-h_point)<=150)
{
d_h = 1;
}
else
{
d_h = 0;
}
} if(d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD){
else if (r_track<2000 && r_track>=1000) thd_d = true;
{
if(abs(h_track-h_point)<=200)
{
d_h = 1;
}
else
{
d_h = 0;
}
}
else if(r_track>=2000 && r_track<3000 )
{
if(abs(h_track-h_point)<=200)
{
d_h = 1;
}
else
{
d_h = 0;
}
}
else if(r_track>=3000 && r_track<5000)
{
if(abs(h_track-h_point)<=300)
{
d_h = 1;
}
else
{
d_h = 0;
}
}
else
{
d_h = 1;
} }
// std::cout << "T d" <<point_process[loop_of_point].CPI_Time/1000<<" "<<T_track/1000<<" "<<delta_T<<" "<<d1<<" "<<d2<<" "<<d3; //判定高度门限,thd_h
// std::cout << "h_track" <<h_track<<" h_point"<<h_point<<" d_h"<<d_h; bool thd_h = false;
double delta_h = abs(h_track-h_point);
if( (r_track <= 1000 && delta_h <= 150) || \
((r_track > 1000 && r_track <= 2000) && delta_h <= 200) || \
((r_track > 2000 && r_track <= 3000) && delta_h <= 250) || \
((r_track > 3000 && r_track <= 5000) && delta_h <= 300) || \
r_track > 5000
){
thd_h = true;
}
//小于关联门限 保存关联信息 //小于关联门限 保存关联信息
if((d1*d1<ASSO_THORD*ASSO_THORD || d2*d2<ASSO_THORD*ASSO_THORD || d3*d3<ASSO_THORD*ASSO_THORD) if(thd_d && thd_h)
&& d_h == 1)//
{ {
// std::cout << "TAS asoooooo111" ; // std::cout << "TAS asoooooo111" ;
File diff suppressed because one or more lines are too long
+63 -127
View File
@@ -12,7 +12,7 @@
| ② 调度 | 模式码 | 分流到 TWS / TAS 通道 | — | | ② 调度 | 模式码 | 分流到 TWS / TAS 通道 | — |
| ③ 凝聚 | 单 CPI 或相邻两 CPI 点迹 | 同类点合并(保幅度大者) | 凝聚点迹表 | | ③ 凝聚 | 单 CPI 或相邻两 CPI 点迹 | 同类点合并(保幅度大者) | 凝聚点迹表 |
| ④ 关联 | 凝聚点迹 + 已有航迹 | IMM 交互 → 波门筛选 → 最近邻一对一 → EKF 更新 | 更新航迹 + 剩余点迹 | | ④ 关联 | 凝聚点迹 + 已有航迹 | IMM 交互 → 波门筛选 → 最近邻一对一 → EKF 更新 | 更新航迹 + 剩余点迹 |
| ⑤ 起始 | 剩余(未关联)点迹 | 逻辑法:航迹头 → 2 点 → ≥3 点 → 起批 | 新可靠航迹 | | ⑤ 起始 | 剩余(未关联)点迹 | 逻辑法(每圈顺序固定):点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 达标临时航迹起批 | 新可靠航迹 + 更新后的临时航迹表 |
| ⑥ 外推/消亡 | 未关联航迹 | 按数据率预测一帧;超限则消亡 | 补点航迹 / 消亡批号 | | ⑥ 外推/消亡 | 未关联航迹 | 按数据率预测一帧;超限则消亡 | 补点航迹 / 消亡批号 |
| ⑦ 波束控制 | TAS 队列 + 航迹 | 数据率门控 + 队列调度 → 位置外推 | 跟踪波束指令 | | ⑦ 波束控制 | TAS 队列 + 航迹 | 数据率门控 + 队列调度 → 位置外推 | 跟踪波束指令 |
@@ -38,7 +38,7 @@ flowchart TD
M1 --> A1["IMM 交互 + 波门筛选 + 最近邻关联 + EKF 更新"] M1 --> A1["IMM 交互 + 波门筛选 + 最近邻关联 + EKF 更新"]
A1 --> A2["未关联航迹外推 / 超限消亡"] A1 --> A2["未关联航迹外推 / 超限消亡"]
A2 --> A3["剩余点迹 → 逻辑法航迹起始"] A2 --> A3["剩余点迹 → 逻辑法起始:点×临时航迹 → 点×航迹头 → 剩余点转航迹头 → 起批"]
A3 --> OUT1["输出更新航迹 + 起批航迹历史点 + 消亡批号"] A3 --> OUT1["输出更新航迹 + 起批航迹历史点 + 消亡批号"]
M3 --> B1["TAS 凝聚(单帧)"] M3 --> B1["TAS 凝聚(单帧)"]
@@ -254,106 +254,51 @@ VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
--- ---
## 5. IMM + EKF 关键逻辑 ## 5. 航迹起始(逻辑法)
### 5.1 IMM 框架 ### 5.1 状态机(每圈按 ①→⑤ 的固定顺序执行)
```mermaid
flowchart LR
A["上一拍三模型状态/协方差 + 模型概率 u"] --> B["交互 mix:算归一化常数 c̄j 与混合概率"]
B --> C["得到三个混合初值 X0j / P0j"]
C --> D["各子模型独立预测(Singer F 和 Qj)"]
D --> E["各子模型 EKF 量测更新 → 新息协方差 Sj、马氏距离 dj"]
E --> F["似然 Λj = N(dj; 0, Sj),模型概率 μj' ∝ Λj·c̄j"]
F --> G["融合输出:X̂ = Σμj'X̂j<br/>P = Σμj'[Pj + (X̂j−X̂)(X̂j−X̂)ᵀ]"]
```
- 状态布局 `[x, vx, ax, y, vy, ay]`,x/y 两轴解耦;
- 模型转移概率矩阵固定:
```
Pt = [ 0.8 0.15 0.05
0.3 0.4 0.3
0.05 0.15 0.8 ]
```
- 机动相关时间常数 α = 0.2;
- 数值保护:归一化常数 c̄j ≤ 1e-12 时钳位;似然分母(det S)≤ 1e-12 时该模型似然置 0;概率分母异常时**保持上一拍模型概率**;EKF 似然用 3×3 新息协方差(保证与量测维数一致)。
### 5.2 三个 Singer 模型的过程噪声强度分档
按航迹速度 v_track 切换 q 值(模型 1 恒用 slow):
| 模型 | TWS 通道 | TAS 通道 |
|---|---|---|
| 模型 1(低机动) | `Model1_Q_slow`(恒定) | 同左 |
| 模型 2(中机动) | v≤5:slow/20;5<v≤30:slow;v>30:fast | v≤5:slow/20;50<v≤100:10·fast;其余:fast |
| 模型 3(高机动) | v≤5:slow/20;5<v≤20:slow;20<v≤50:fast;50<v≤100:2·fast;v>100:10·fast | v≤5:slow/20;50<v≤100:10·fast;其余:fast |
> 语义:低速目标按"低机动"处理(把中/高机动模型噪声压到极小,避免噪声被当成机动),速度越高逐步放大高机动模型的过程噪声。`Model1_Q_fast` 字段不参与计算。
### 5.3 量测与残差修正(EKF)
量测向量 Z = [r, θ, v_r],量测函数由预测状态给出:
```
h(X) = [ √(x²+y²), atan2(y,x) 归一化到 [0,2π), −(x·vx + y·vy)/√(x²+y²) ]
```
在预测点处线性化得到雅可比 H(3×6),量测噪声 R = diag(σr², σa², σv²)(10 m / 0.02 rad / 2 m/s),S = H·P·Hᵀ + R。
**两处残差修正(关键)**
1. **方位环绕**:νθ 归一到 [−π, π),避免 0°/360° 附近的假大残差;
2. **多普勒盲速折叠**:`νv ← νv − round(νv / v_bind) · v_bind`,其中
```
v_bind = 150000 / (f_GHz · PRI_µs), f_GHz = 9.2 + 0.02 · 频点号
```
PRI ≤ 0 或频率非法时返回安全值 1.0(接口约定 PRI 单位 µs 且不允许为 0)。
判据(关联)与模型似然共用同一个 `d = νᵀS⁻¹ν`,且滤波函数把 3×3 新息协方差 S 一并回传给似然使用。
### 5.4 航迹初始化
| 场景 | 维数 | 做法 |
|---|---|---|
| 两点(航迹头 + 1 点,临时航迹) | 4 维 [x, vx, y, vy] | 位置取末点,速度取两点差分;协方差按 Z1 处的**转换量测协方差** R 填充,速度项按 R/T、2R/T² 传播 |
| 三点(起批,可靠航迹) | 6 维 | 位置取末点、速度取末两点差分、加速度取两段速度差分;协方差按 R0/R1/R2 误差传播构造 |
| 起批时 IMM 初始化 | — | 三个子模型用同一状态/协方差,模型概率 u = [1/3, 1/3, 1/3] |
**转换量测协方差**(极坐标量测转直角坐标时的等效噪声,含方位非线性引起的附加项,由 σr=10 m、σa=0.02 rad 决定)用于上述初始化与临时航迹统计距离计算。
> 约束:两点/三点初始化对时间差 T 无除零保护,因此**起批路径要求时间戳严格递增、不允许重复**。
---
## 6. 航迹起始(逻辑法)
### 6.1 状态机
```mermaid ```mermaid
flowchart TD flowchart TD
A["本圈未被可靠航迹关联的点迹"] --> B["全部转成'航迹头'(1 点,buff_round=1)"] A["本圈未被可靠航迹关联的点迹"] --> A0["临时航迹所有点的 buff_round +1"]
B --> C{"下一圈的点迹"} A0 --> B["① 点 × ≥2 点临时航迹关联(三点及以上)<br/>EKF 统计距离 + 折角 + 多普勒同号<br/>命中 → 复制原航迹 + 追加新点"]
C --> D["点-航迹头关联(两点起始)<br/>速度区间 + 多普勒一致性"] B --> C["② 点 × 1 点航迹头关联(两点起始)<br/>速度区间 + 多普勒一致性<br/>命中 → 两点卡尔曼初始化 → 2 点临时航迹"]
D -->|命中| E["两点卡尔曼初始化 → 2 点临时航迹"] C --> D["③ 删除 Use_Flag==1 的点迹<br/>剩余点全部转成新航迹头(1 点,buff_round=1)<br/>当圈不再参与关联"]
D -->|未命中| B D --> E{"④ 长度 ≥ 起批点数<br/>且 ≤10 且不在起批屏蔽区?"}
E --> F{"后续圈的点迹"} E -- 是 --> F["同源去重(保留末点 d 更小者)"]
F --> G["点-临时航迹关联(三点及以上)<br/>EKF 统计距离 + 折角 + 多普勒同号"] F --> G["三点卡尔曼初始化 → 分配航迹号 1~500<br/>IMM 三模型同状态,u=1/3"]
G -->|命中| H["航迹延长(复制原航迹 + 追加新点)"] G --> H["输出该航迹的历史点(前 N 个点)"]
G -->|未命中| F E -- 否 --> I["留在临时航迹表,下一圈继续关联"]
H --> I{"长度 ≥ 起批点数<br/>且不在起批屏蔽区?"} H --> J["⑤ 临时航迹消亡(不对外输出)<br/>末点 buff_round > 2 或 长度 ≥ 10"]
I -- 是 --> J["同源去重(保留末点 d 更小者)"] I --> J
J --> K["三点卡尔曼初始化 → 分配航迹号 1~500<br/>IMM 三模型同状态,u=1/3"]
K --> L["输出该航迹的历史点(前 N 个点)"]
I -- 否 --> F
F --> M{"末点 buff_round > 2<br/>或长度 ≥ 10 ?"}
M -- 是 --> N["临时航迹消亡(不对外输出)"]
``` ```
### 6.2 关联判据汇总 **每圈顺序不可颠倒(`track_init.cpp` L49-126)**:点迹**先**做点-临时航迹关联(延长已有 ≥2 点临时航迹),**再**做点-航迹头关联(产生新的 2 点临时航迹),两阶段都结束后才把未被消费的点迹转成新航迹头,最后才把长度达标的临时航迹转可靠航迹,消亡放在最末。由此可得:
**阶段一:点 ↔ 航迹头(两点起始)** - 当圈新建的 2 点临时航迹**不可能当圈起批**——长度不足,且新点 `buff_round=1` 要到下一圈才具备关联资格;
- 当圈刚被延长到达标长度的临时航迹**可以当圈起批**(第 ④ 步在第 ① 步之后执行);
- 两个关联阶段**互不排斥**:都只把命中的点迹标记 `Use_Flag=1`,航迹头阶段不检查临时航迹阶段留下的标记,因此同一点迹可同时延长一条临时航迹、又开启一条 2 点临时航迹,只有走完两阶段后才统一删除被标记的点迹。
> 第 ⑤ 步的消亡判据对**未起批**的临时航迹生效;已起批的航迹在第 ④ 步中已从临时航迹表移除。
### 5.2 关联判据汇总
每圈按 **① 点-临时航迹关联 → ② 点-航迹头关联** 的顺序依次执行,两阶段的关联判据如下。
**阶段①:点 ↔ ≥2 点临时航迹(三点及以上)**
```
|r_point − r_末点| ≥ 5 m 距离差硬门限(近距点不参与)
d ≤ 8(代码写作 d² ≤ TRACK_START_THRESHOLD²=64) 3 维 EKF 统计距离波门
α < ALPHA_START = 60° 折角:最近三点连线夹角
v_r,point · v_r,末点 > 0 多普勒同号
```
- d 由 4 维 CV 状态一步预测 + 「距离-方位-径向速度」EKF 得到(过程噪声 0.03²、同样做方位环绕与盲速折叠);
- 折角 α 为向量 D12(前一点→末点)与 D23(末点→新点)的夹角;α 越小越接近直线;
- 门限同样写作 `d² ≤ TRACK_START_THRESHOLD²`,而 d 本身已是马氏距离平方,实际生效为 d ≤ 8(与可靠航迹关联门限的语义一致)。
**阶段②:点 ↔ 航迹头(两点起始)**
用航迹头位置与点迹的平面距离 `dis` 与 ΔT(秒): 用航迹头位置与点迹的平面距离 `dis` 与 ΔT(秒):
@@ -369,27 +314,14 @@ dis ≥ |v_r,head| · ΔT 同上,用航迹头的多普勒约
- 判据隐含要求 ΔT > 0(ΔT ≤ 0 时 `dis ≤ V_MAX·ΔT` 必然不成立); - 判据隐含要求 ΔT > 0(ΔT ≤ 0 时 `dis ≤ V_MAX·ΔT` 必然不成立);
- 近程/中远程分支当前取值相同(都是 V_MAX),**实际不做距离分段**。 - 近程/中远程分支当前取值相同(都是 V_MAX),**实际不做距离分段**。
**阶段二:点 ↔ ≥2 点临时航迹(三点及以上)** ### 5.3 临时航迹的"圈数"生命周期
``` `buff_round` 只在起始流程里自增,并且**每次调用(= 每圈)先把所有临时航迹所有点的 buff_round +1,再做关联**;因此关联判据看到的是自增后的圈数,当圈刚建立/刚追加的点(buff_round=1)当圈不可能再被关联:
|r_point − r_末点| ≥ 5 m 距离差硬门限(近距点不参与)
d ≤ 8(代码写作 d² ≤ TRACK_START_THRESHOLD²=64) 3 维 EKF 统计距离波门
α < ALPHA_START = 60° 折角:最近三点连线夹角
v_r,point · v_r,末点 > 0 多普勒同号
```
- d 由 4 维 CV 状态一步预测 + 「距离-方位-径向速度」EKF 得到(过程噪声 0.03²、同样做方位环绕与盲速折叠);
- 折角 α 为向量 D12(前一点→末点)与 D23(末点→新点)的夹角;α 越小越接近直线;
- 同样存在"门限对马氏距离平方再平方"的语义,实际生效为 d ≤ 8。
### 6.3 临时航迹的"圈数"生命周期
`buff_round` 只在起始流程里自增,并且**每次调用(= 每圈)先把所有临时航迹所有点的 buff_round +1,再做关联**:
| 动作 | 效果 | | 动作 | 效果 |
|---|---| |---|---|
| 新点加入 | buff_round = 1 | | 新点加入 | buff_round = 1 |
| 关联资格 | 只看**末点**,要求 buff_round ≥ 2 → **上一圈加入的点本圈才能被延续**,同一航迹每圈最多吸收一个点 | | 关联资格 | 只看**末点**,要求 buff_round ≥ 2 → **上一圈加入的点本圈才能被延续**;同一圈内一条临时航迹可被多个点同时延续(每个点各复制出一条新航迹,见 6.4),并非一对一 |
| 消亡条件 | 末点 buff_round > 2(连续 2 圈无新点)或长度 ≥ 10 | | 消亡条件 | 末点 buff_round > 2(连续 2 圈无新点)或长度 ≥ 10 |
**时间轴示例(以第 k 圈加入新点为起点)** **时间轴示例(以第 k 圈加入新点为起点)**
@@ -402,13 +334,14 @@ v_r,point · v_r,末点 > 0 多普勒同号
即:**允许中间空 1 圈**,最大关联间隔 2 圈;生命周期完全由"圈数"驱动,与绝对时间无关。 即:**允许中间空 1 圈**,最大关联间隔 2 圈;生命周期完全由"圈数"驱动,与绝对时间无关。
### 6.4 航迹分裂与去重 ### 5.4 航迹分裂与去重
- 关联阶段**不做一对一分配**:一个点满足多条临时航迹条件时,每条都各自复制出一条新航迹;多条点命中同一条航迹时也各自复制 → 同一圈会产生大量同源临时航迹; - 关联阶段**不做一对一分配**:一个点满足多条临时航迹条件时,每条都各自复制出一条新航迹;多个点命中同一条临时航迹时也各自复制;点-航迹头阶段同理(一个航迹头可与多个点各生成一条 2 点临时航迹)→ 同一圈会产生大量同源临时航迹;
- 两个关联阶段也**互不排斥**:点-航迹头阶段不检查点-临时航迹阶段留下的 `Use_Flag`,同一点迹可同时参与两边的分裂试探;
- 起批前做**同源去重**:比较两条候选航迹的**前三个点**,只要有任一点的"时间 T + 距离 r"完全相同即判为同源,保留末点统计距离 d 更小者; - 起批前做**同源去重**:比较两条候选航迹的**前三个点**,只要有任一点的"时间 T + 距离 r"完全相同即判为同源,保留末点统计距离 d 更小者;
- 这样既保证不漏关联(允许分裂试探),又不会让同一批点重复起批。 - 这样既保证不漏关联(允许分裂试探),又不会让同一批点重复起批。
### 6.5 起批与输出 ### 5.5 起批与输出
| 条件 | 说明 | | 条件 | 说明 |
|---|---| |---|---|
@@ -422,9 +355,9 @@ v_r,point · v_r,末点 > 0 多普勒同号
--- ---
## 7. 外推、消亡与航迹号管理 ## 6. 外推、消亡与航迹号管理
### 7.1 消亡条件 ### 6.1 消亡条件
| 通道 | 条件(满足其一即消亡) | | 通道 | 条件(满足其一即消亡) |
|---|---| |---|---|
@@ -434,7 +367,7 @@ v_r,point · v_r,末点 > 0 多普勒同号
消亡时把批号写入消亡输出数组并从航迹表删除;**临时航迹消亡不对外输出**(起批失败的中间态对宿主不可见)。 消亡时把批号写入消亡输出数组并从航迹表删除;**临时航迹消亡不对外输出**(起批失败的中间态对宿主不可见)。
### 7.2 航迹号分配 ### 6.2 航迹号分配
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -454,9 +387,9 @@ flowchart TD
--- ---
## 8. TAS 通道(model=3) ## 7. TAS 通道(model=3)
### 8.1 与 TWS 通道的差异 ### 7.1 与 TWS 通道的差异
| 项 | TWS | TAS | | 项 | TWS | TAS |
|---|---|---| |---|---|---|
@@ -467,7 +400,7 @@ flowchart TD
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` | | 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
| 输出 | 全部航迹 | 仅该批号航迹 | | 输出 | 全部航迹 | 仅该批号航迹 |
### 8.2 TAS 关联与更新 ### 7.2 TAS 关联与更新
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -501,9 +434,9 @@ flowchart TD
--- ---
## 9. TAS 波束控制(外部接口 Beam_Ctrl) ## 8. TAS 波束控制(外部接口 Beam_Ctrl)
### 9.1 队列管理 ### 8.1 队列管理
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -527,7 +460,7 @@ flowchart TD
> 自动开启/退出 TAS 的逻辑(按距离/速度/高度阈值)已全部注释停用,**只支持手动**。 > 自动开启/退出 TAS 的逻辑(按距离/速度/高度阈值)已全部注释停用,**只支持手动**。
### 9.2 波束输出调度(关键) ### 8.2 波束输出调度(关键)
```mermaid ```mermaid
flowchart TD flowchart TD
@@ -550,9 +483,9 @@ flowchart TD
--- ---
## 10. 参数脉络(决定行为的旋钮) ## 9. 参数脉络(决定行为的旋钮)
### 10.1 编译期宏(随扫描体制切换) ### 9.1 编译期宏(随扫描体制切换)
| 宏 | 相扫 | 机扫 | 作用点 | | 宏 | 相扫 | 机扫 | 作用点 |
|---|---|---|---| |---|---|---|---|
@@ -570,7 +503,7 @@ flowchart TD
| `FREQ0` | 9.2 GHz | 同左 | 盲速计算的基准频率 | | `FREQ0` | 9.2 GHz | 同左 | 盲速计算的基准频率 |
| `RDP_LOG_ENABLE / RDP_LOG_DIR` | — | — | 日志落盘开关与目录 | | `RDP_LOG_ENABLE / RDP_LOG_DIR` | — | — | 日志落盘开关与目录 |
### 10.2 运行时参数(宿主传入,随任务变化) ### 9.2 运行时参数(宿主传入,随任务变化)
| 参数 | 影响环节 | | 参数 | 影响环节 |
|---|---| |---|---|
@@ -589,7 +522,7 @@ flowchart TD
--- ---
## 11. 关键约定与注意事项 ## 10. 关键约定与注意事项
**时序与时间戳** **时序与时间戳**
@@ -635,5 +568,8 @@ flowchart TD
| 起始输出历史点 | "临时航迹的全部 L 个点" | 实际输出**前 `track_start_point_num` 个点** | | 起始输出历史点 | "临时航迹的全部 L 个点" | 实际输出**前 `track_start_point_num` 个点** |
| 起始输入过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 结束前已把 `Use_Flag==1` 的点迹从 `point_recv` 删除;`track_init` 入口(L59-70)也会再剔除一次 `Use_Flag==1` 的点,两道处理叠加,效果一致 | | 起始输入过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 结束前已把 `Use_Flag==1` 的点迹从 `point_recv` 删除;`track_init` 入口(L59-70)也会再剔除一次 `Use_Flag==1` 的点,两道处理叠加,效果一致 |
| 日志开关 | "RDP_LOG_ENABLE 默认开启" | `parameters.h` 中该宏当前为**注释状态**(不落盘,日志进控制台) | | 日志开关 | "RDP_LOG_ENABLE 默认开启" | `parameters.h` 中该宏当前为**注释状态**(不落盘,日志进控制台) |
| 起始步骤顺序 | "航迹头(1 点)→ 点-航迹头关联(2 点)→ 点-临时航迹关联(≥3 点)→ 起批" | 每圈实际顺序为**点 × ≥2 点临时航迹关联 → 点 × 1 点航迹头关联 → 剩余点转新航迹头 → 长度达标的临时航迹起批**,消亡最后执行(`track_init.cpp` L49-126,见第 6 节) |
> 后续:README 与 `track_init_readme.md`(航迹起始专项说明)已按上表结论同步(见 CHANGELOG 的 Unreleased 记录);流程落地实现仍以本文为准。 > 后续:README 与 `track_init_readme.md`(航迹起始专项说明)已按上表结论同步(见 CHANGELOG 的 Unreleased 记录);流程落地实现仍以本文为准。
>
> 补充修订(本次):起始流程的**步骤顺序**原先在本文档(第 0、6 节)与 README(第 2、5.2、8.3、9.3 节)中都写作「航迹头 → 2 点 → ≥3 点 → 起批」,与源码每圈「点 × 临时航迹关联 → 点 × 航迹头关联 → 剩余点转航迹头 → 达标者起批」的顺序相反,现已按源码修正,并补充说明两个关联阶段互不排斥、均不做一对一分配。