Files
radar_data_process/docs/track_init_readme.md
T

792 lines
49 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 航迹起始(Track Init)详细逻辑说明
> 适用范围:`data_process_class_dll/track_init.cpp` / `track_init.h`(类 `Track_Init`),TWS 通道逻辑法航迹起始。
> 关联文件:`kalman.cpp`(滤波与统计距离)、`coor_trans.cpp`(坐标转换)、`track_index_mangement.cpp`(航迹号分配)、`data_process.cpp`(调用与参数)。
> 本文按**当前源码实际行为**梳理,行号均指当前版本文件行号;与 `README.md` 第 8.3 / 9.3 节存在差异之处,见第 12 节。
---
## 目录
1. [模块定位与调用时机](#1-模块定位与调用时机)
2. [数据结构与状态容器](#2-数据结构与状态容器)
3. [处理流程总览](#3-处理流程总览)
4. [阶段一:输入装填与圈数计数](#4-阶段一输入装填与圈数计数)
5. [阶段二:点迹与临时航迹关联](#5-阶段二点迹与临时航迹关联)
6. [阶段三:点迹与航迹头关联](#6-阶段三点迹与航迹头关联)
7. [阶段四:点迹消费与航迹头生成](#7-阶段四点迹消费与航迹头生成)
8. [阶段五:临时航迹转可靠航迹(起批)](#8-阶段五临时航迹转可靠航迹起批)
9. [阶段六:临时航迹消亡](#9-阶段六临时航迹消亡)
10. [起始条件汇总表](#10-起始条件汇总表)
11. [关键算法细节](#11-关键算法细节)
12. [与其他文档/实现的差异](#12-与其他文档实现的差异)
13. [参数与调参](#13-参数与调参)
14. [输出字段映射](#14-输出字段映射)
15. [已知问题与风险点](#15-已知问题与风险点)
16. [代码索引](#16-代码索引)
---
## 1. 模块定位与调用时机
### 1.1 在整体流程中的位置
航迹起始处于 TWS 处理链的**末端**:只有「当圈未被任何可靠航迹关联上」的剩余点迹才会进入起始流程。
```mermaid
flowchart LR
A["data_preprocess<br/>点迹入 Data_buffer"] --> B["dot_coh(model=2)<br/>两帧滑窗凝聚"]
B --> C["track_asso(model=1)<br/>IMM + EKF 点航关联<br/>已用点从 point_recv 删除"]
C --> D["track_die<br/>可靠航迹消亡"]
D --> E["<b>track_init</b><br/>逻辑法航迹起始"]
E --> F["Trust_Track_Output<br/>(起批航迹 + 历史点)"]
```
调用点(`data_process.cpp`):
```cpp
// track_process()(data_process.cpp L167 起),仅 model==1(TWS 满一圈)时执行
track_asso.track_asso_process(&point_recv, &trust_track, ...); // 入口删除 Use_Flag==1 的点
track_die.track_die_process(&trust_track, ...);
track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
Trust_Track_Output, Trust_track_num_Output);
```
> 工作参数为**全局对象 `rdp_parameter`**(`struct.h` 中 `extern` 声明、`data_process.cpp` 定义并零初始化),由公开接口 `parameters_reload(配置文件路径)` 从 TOML 配置文件加载;各模块直接以 `rdp_parameter.<field>` 访问,不再作为函数形参逐层传递(参数来源见 README 第 10 节;改造前为宿主下发 `RadarPara` + `parameters.h` 宏,更早则由 `const RDP_parameter&` 形参传入)。
### 1.2 调用频率 = 扫描圈
`data_preprocess` 判为一圈完成时返回 1(`data_process.cpp`),宿主据此调用 `track_process(model=1)`。判据由配置项 `[radar_params] scan_mode` **运行时**选择(0 相扫 / 1 机扫;替代原编译期 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 分支):**scan_mode=0** 比较前后两帧方位波位号(当前 ≤ 上一帧即满圈,`last_beam_num` 参与判断,清空后/首帧即满圈);**scan_mode=1** 判 `Data_Input[0].Beam_index_aiz == 0`(详见 README 7.2 与 `docs/data_process_flow_report.md` 2.2 节)。
因此 **`track_init_process_logic()` 每次调用对应 TWS 一圈**,`Temp_track::buff_round`(已缓存圈数)正是以「调用次数」为单位累加的。
### 1.3 类接口
| 成员 | 可见性 | 作用 |
|---|---|---|
| `track_init_process_logic(...)` | public | 起始主流程(唯一入口) |
| `reset()` | public | 清空内部点迹缓存 + 复位航迹号分配器 |
| `point_temp_track_asso(...)` | private | 点迹 ↔ ≥2 点临时航迹关联 |
| `point_track_head_asso(...)` | private | 点迹 ↔ 1 点航迹头关联 |
| `tmp_track_to_trust_track(...)` | private | 收集 → 去重 → 起批 → 输出 |
| `tmp_track_die(...)` | private | 临时航迹消亡 |
| `alpha_cal_track_init(...)` | private | 三点折角(度) |
| `track_init_prohibit(...)` | private | 起批屏蔽区判断 |
| `track_index_mangement` | private | 航迹号(1~`track_index_max`,默认 500)分配器 |
> `temp_track`(`std::vector<std::vector<Temp_track>>`)**不是** `Track_Init` 的成员,而是 `Data_Process::temp_track`,由调用方传入并跨圈保持;`track_clear_all()` 中清空(`data_process.cpp` L233),`Track_Init::reset()` 只清自己的 `point_process`。
---
## 2. 数据结构与状态容器
### 2.1 Temp_track(临时航迹点,`struct.h`)
| 字段 | 含义 | 起始流程中的作用 |
|---|---|---|
| `X[4]` | 4 维状态 **[x, vx, y, vy]** | EKF 一步预测的状态;由两点卡尔曼初始化填充 |
| `P[4][4]` | 状态协方差 | 同上的协方差 |
| `r / azi / vr / height / SNR / RCS / amplitude` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
| `CPI_time` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
| `d` | 该点关联时的马氏统计距离 | 去重阶段用于「保留更优者」 |
| `buff_round` | **已缓存圈数** | 生命周期与关联时机控制(见 4.2) |
| `asso_flag` | 0 未关联 / 1 已关联(待删除)/ 2 去重淘汰 | 临时状态标记 |
| `pitch_num`、`speed_dim[129]`、`range_dim[8]` | 俯仰波位、十字星数据 | 纯透传 |
| `temp_track_id` / `point_id` / `circle_id` | 临时航迹 ID / 末关联点迹 ID / 处理圈 ID(c3c7e66 新增) | 数据记录链路标识;起批时透传给 `Trust_Track` |
| `quality` | **航迹质量(0~1,V1.5.8 新增)** | 每延长一点即重算(见 8.6);起批时取末点值作为初始质量,并参与起批质量门限判断 |
| `temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
> 字段更名提示(c3c7e66):`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`。阅读早期提交或旧文档时注意对应关系。
>
> ⚠ **下标陷阱**:`Temp_track::X` 是 4 维 `[x, vx, y, vy]`(y 在 index 2),而 `Trust_Track::X` 是 6 维 `[x, vx, ax, y, vy, ay]`(y 在 index 3)。两套结构混用下标必错。
### 2.2 容器语义
| 容器 | 语义 |
|---|---|
| `temp_track`(外层 vector) | 「临时航迹表」:外层每一项 = 一条临时航迹,**下标是不稳定的位置号**(擦除后会重排);内层 vector = 该航迹按时间正序的点序列 |
| `point_process`(`Track_Init` 成员) | 本次调用待处理的点迹工作副本 |
| `point_recv`(`Data_Process` 成员) | TWS 点迹文件:凝聚输出 → 被 `track_asso` 消费 → 剩余点进入 `track_init` → 被清空 |
### 2.3 输入与输出
| 参数 | 方向 | 说明 |
|---|---|---|
| `point_recv` | 输入/输出 | 输入:当圈未被可靠航迹关联的点迹;**输出:被清空**(`track_init.cpp` L118) |
| `trust_track` | 输入/输出 | 可靠航迹表;起批成功的临时航迹 `push_back` 到此 |
| `temp_track` | 输入/输出 | 临时航迹表(跨圈状态) |
| `Trust_Track_Output[MAX_TRACK_NUM][10]` | 输出 | 与 `track_asso` **共用同一数组与同一计数**;起始流程在 `track_asso` 已写入的行之后追加新航迹的历史点 |
| `Trust_track_num_Output` | 输出 | 累计行数(track_asso 输出 N 行后,track_init 在其后继续追加) |
| 全局 `rdp_parameter` | 读取 | 工作参数(TOML 配置加载的全局对象,`struct.h` 声明、`data_process.cpp` 定义);**不再经函数形参传入**,模块直接访问 |
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < track_index_max`(配置项,默认 500),故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 `track_index_max` 时不再允许新起批。
---
## 3. 处理流程总览
```mermaid
flowchart TD
S["track_init_process_logic 入口"] --> A1["① 取出 point_recv → point_process"]
A1 --> A2["② temp_track 全部点的 buff_round += 1"]
A2 --> Q{"point_process 空?"}
Q -- 是 --> DIE["⑥ tmp_track_die"]
Q -- 否 --> B["③ point_temp_track_asso<br/>点 ×(≥2 点临时航迹)"]
B --> C["④ point_track_head_asso<br/>点 ×(1 点航迹头)"]
C --> E["⑤ 删除 Use_Flag==1 的点<br/>剩余点 → 新建航迹头(buff_round=1)"]
E --> F["⑦ tmp_track_to_trust_track<br/>收集 → 去重 → 起批 → 输出"]
F --> G["清空 point_process / point_recv"]
G --> DIE
DIE --> R["return 0"]
```
要点顺序(不可颠倒):
1. **buff_round 先自增,再做关联** —— 这正是「同一圈内不重复关联同一条临时航迹」的实现方式(见 4.2)。
2. **先关联 ≥2 点临时航迹,再关联 1 点航迹头** —— 前者优先,但两者**不互斥**:同一圈可同时被两边关联(点迹可以既延续长航迹,又延续航迹头)。
3. **关联结果统一用 `Use_Flag` 标记,最后一次性物理删除** —— 只有 `Use_Flag==0` 的剩余点才会变成新航迹头。
4. **转可靠航迹在航迹头生成之后** —— 因此当圈新建的航迹头(1 点)不参与当圈起批。
5. **消亡在最后执行** —— 且即使当圈无剩余点迹(跳过 ③~⑦)也照常执行。
---
## 4. 阶段一:输入装填与圈数计数
### 4.1 装填(`track_init.cpp` L36-38)
```cpp
for (size_t i=0;i<(*point_recv).size();i++)
point_process.push_back((*point_recv)[i]); // 全量拷贝,不检查 Use_Flag
```
- 上游 `track_asso` 已把关联上的点从 `point_recv` 中删除,所以这里进来的都是「未关联点」;函数内部**不再**用 `Use_Flag` 过滤输入(`Use_Flag` 只用于本次调用内部两阶段关联的标记)。
- 点迹的 `Range` 已在凝聚阶段做过量程过滤(下限/上限为配置项 `point_cohesion_range_min` / `point_cohesion_range_max`,默认 100 m / 100000 m)。
### 4.2 buff_round 自增(L40-47)
```cpp
for (每一条临时航迹 j) (*temp_track)[i][j].buff_round += 1;
```
含义与后果(关键机制):
| 动作 | 效果 |
|---|---|
| 新建点(航迹头 / 新关联点) | `buff_round = 1` |
| 每经过一次 `track_init` 调用(一圈) | 该航迹内**所有**点的 `buff_round` +1;关联判据只看**末点** |
| 关联判据要求 `末点.buff_round >= 2` | 等价于:**上一圈加入的点才能被本圈延续** ⇒ 同一条临时航迹每圈最多吸收一个点(除分裂,见 5.5) |
| 消亡判据 `末点.buff_round > 2` | 等价于:**连续 2 圈没有新点**就消亡(允许中间空 1 圈,最大关联间隔 2 圈) |
> 时间戳只在关联时用 `ΔT = (T_point − T_末点)/1000` 秒表示,**不参与生命周期判断**;生命周期完全由「圈数」驱动。这也意味着临时航迹的存活时间与扫描周期成正比,与航迹的绝对时间无关。
**生命周期时间轴(以 scan k 为起点)**
```text
scan k : 新点加入 → 末点 buff_round = 1 (本圈不能再关联)
scan k+1 : 自增 → 2 → 允许关联;若关联成功 → 新点 1;若失败 → 2 > 2 不成立,存活
scan k+2 : 自增 → 3 → 仍然允许关联;若成功 → 新点 1;若失败 → 3 > 2 成立,消亡
```
---
## 5. 阶段二:点迹与临时航迹关联
函数:`point_temp_track_asso()`(L145-304)。双重循环:外层点迹、内层临时航迹,**无一对一竞争约束**。
### 5.1 参与资格(L164)
```cpp
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2 )
```
- 只处理 **≥2 点** 的临时航迹(1 点的是航迹头,留给阶段三);
- 末点必须来自历史圈次。
### 5.2 关联判据(L167-221)
设点迹量测(`Z[3]`):
$$
Z=\begin{bmatrix} r_p \\ \theta_p \\ v_{r,p}\end{bmatrix},\qquad
\mathbf{X}_{k-1}=\text{末点状态}[x,v_x,y,v_y],\qquad
\Delta T=\frac{T_p-T_{\text{末点}}}{1000}\ \text{(s)}
$$
**前置硬门限(L191)**:`|r_p − r_末点| >= 5` —— 距离差小于 5 m 的点直接不参与(连 d 都不算)。
**统计距离(L194-195)**:调用 `kalman::d_cal_track_init_EKF(Z, X, P, ΔT, prt, freq_ind)` 得到 3 维量测的马氏距离平方 `d`(详见 11.2)。
**折角(L199-205)**:取末点前一点 → 末点 → 新点,计算折角 `α`(度,详见 11.3)。
**三条件同时成立(L221)**:
$$
\boxed{\;d^2 \le \text{TRACK\_START\_THRESHOLD}^2=64
\quad\wedge\quad
\alpha < \text{ALPHA\_START}=60^\circ
\quad\wedge\quad
v_{r,p}\cdot v_{r,\text{末点}} > 0\;}
$$
> ⚠ 由于 `d_cal_track_init_EKF` 返回的 `d` **本身就是马氏距离平方**,而判据又写作 `d*d <= 64`(即 `d <= 8`),实际生效的门限是「马氏距离平方 ≤ 8」。3 自由度卡方分布 95% 分位为 7.815,因此该写法恰好给出约 95.4% 的置信门限(详见 11.2 与 15.6)。
### 5.3 记录关联(L223-231)
命中后向 `asso_info` 记录 `(point_idx=i+1, track_idx=j+1, d)`,同时:
- `末点.asso_flag = 1` → 标记**原航迹**待删除;
- `point_process[i].Use_Flag = 1` → 标记该点已被消费。
索引在整个扫描循环中保持稳定(循环期间不修改两个容器),因此后面的 1-based 索引回查是安全的。
### 5.4 生成新临时航迹(L237-281)
对每一条关联记录:**复制原航迹的全部 L 个点**(并把各点 `asso_flag` 清 0)、再**追加关联上的新点**,作为一条**新的临时航迹** push 到 `temp_track` 末尾。
新点字段:极坐标测量、幅度、SNR、RCS、时间、俯仰波位、十字星数据全套透传;`asso_flag=0`、`buff_round=1`、`d = asso_info[i].d`;
状态与协方差由 **两点卡尔曼初始化** 得到(L268-279):
```cpp
Z0 = (末点.r·cos(末点.azi), 末点.r·sin(末点.azi)); // 原航迹末点
Z1 = (点.Range·cos(点.Azimuth), 点.Range·sin(点.Azimuth));
T = (点.CPI_Time − 末点.T)/1000.0;
Kalman.kalman_filter_init_2dots(Z0, Z1, T, X, P); // 4 维 CV 初始化
```
### 5.5 航迹分裂(重要特性)
关联循环**不做一对一分配**:
- 一个点若满足多条临时航迹的判据,会为**每一条**各自复制出一条新航迹 —— 一条母航迹可以派生多条子航迹;
- 一条临时航迹若被多个点同时命中,也会**各自复制**成多条子航迹(每个子航迹只带一个点);
- 因此同一圈内可能产生大量同源临时航迹,靠第 8.3 节的**去重**收敛。
### 5.6 删除被关联的原航迹(L283-303)
遍历 `temp_track`,凡 `size()>=2` 且 **末点 `asso_flag==1`** 者整体 `erase`(每次擦除后 `Iter = begin()` 重扫,O(n²) 但结果正确;1 点航迹不在此处理)。新生成的子航迹末点 `asso_flag==0`,不会被误删。
---
## 6. 阶段三:点迹与航迹头关联
函数:`point_track_head_asso()`(L306-445)。这是**两点起始**:航迹头 + 1 个点 → 2 点临时航迹。
### 6.1 参与资格(L325)
```cpp
if((*temp_track)[j].size()==1 && (*temp_track)[j][0].buff_round >= 2)
```
只处理 **1 点**且该点来自历史圈次的航迹头。
### 6.2 关联判据(L328-372)
点迹与航迹头都转直角坐标后取**平面距离**:
$$
x_p = r_p\cos\theta_p,\quad y_p=r_p\sin\theta_p;\qquad
x_h=\text{头.X}[0],\quad y_h=\text{头.X}[2]
$$
$$
dis=\sqrt{(x_p-x_h)^2+(y_p-y_h)^2},\qquad \Delta T=\frac{T_p-T_h}{1000}\ \text{(s)}
$$
**当前生效的判据(L368-372)**:
$$
\boxed{\;
dis \le V_{max}\Delta T
\;\wedge\;
dis \ge V_{min}\Delta T
\;\wedge\;
v_{r,p}\cdot v_{r,h} > 0
\;\wedge\;
dis \ge |v_{r,p}|\,\Delta T
\;\wedge\;
dis \ge |v_{r,h}|\,\Delta T\;}
$$
各条件的物理含义:
| 条件 | 含义 |
|---|---|
| `dis ≤ V_max·ΔT` | 平均速度不超过最大速度(`V_max = rdp_parameter.track_init_velocity_max`,配置项) |
| `dis ≥ V_min·ΔT` | 平均速度不低于最小速度(滤除静止/慢速点) |
| `v_r,p · v_r,h > 0` | 两点多普勒径向速度**同号**(同接近/同远离) |
| `dis ≥ abs(v_r,p)·ΔT` | 位置变化不小于**点迹自身多普勒**在这段时间内的径向位移(多普勒-几何一致性) |
| `dis ≥ abs(v_r,h)·ΔT` | 同上,用航迹头的多普勒约束 |
**近/远程分支(L348-356)当前是死分支**:`work_mode==0` 与 `else` 都取 `vmax = rdp_parameter.track_init_velocity_max`(注释"近程模式最大速度减小一点"未生效;原按 `V_MAX/5` 收紧的写法仅保留在未启用的 `track_init_direct_tracking.cpp` 注释里)。
**ΔT 语义**:判据隐含要求 `ΔT > 0`(若 `ΔT < 0`,则 `dis ≤ 负数` 必假)。`ΔT == 0` 时仅在 `dis == 0`(同时间同位置的点)才可能成立,属退化情形。
**与文档差异**:源码中被注释停用的旧判据按距离分段收紧门限(`r<1000` 时半速、中远程 `r<2000` 时 1/2~1/3 速),见第 12 节。
### 6.3 命中后的处理(L374-427)
- 记录 `(point_idx, track_idx)`,标记 `头.asso_flag=1`、`point_process[i].Use_Flag=1`;
- 逐条生成 2 点临时航迹:**第 1 点 = 原航迹头**(`asso_flag` 清 0),**第 2 点 = 新点**(全套测量透传,`buff_round=1`,`asso_flag=0`,**`d` 未赋值,保持 0**);
- 同样调用 `kalman_filter_init_2dots` 得到 4 维状态/协方差(`Z0` = 头点、`Z1` = 新点,`T = (点.CPI_Time − 头.T)/1000`)。
### 6.4 删除被关联的航迹头(L429-443)
凡 `[0].asso_flag==1` 的 1 点航迹头整条 `erase`(同样每次重扫)。
注意:本阶段遍历的是**全部** `temp_track`,其中由阶段二新建的 ≥2 点航迹因 `size()!=1` 不会被误处理。
---
## 7. 阶段四:点迹消费与航迹头生成
### 7.1 删除已消费点迹(L57-70)
```cpp
if((*Iter).Use_Flag==1) { point_process.erase(Iter); Iter = point_process.begin(); }
```
物理删除所有被阶段二或阶段三消费的点(两阶段共用同一 `Use_Flag`)。
> 结果:一个点迹**要么**参与延续已有临时航迹/航迹头(可同时延续两者),**要么**成为新航迹头,二者互斥。这保证不会「用同一个点既延续又起新头」。
### 7.2 剩余点回写(L72-75,等效空操作)
```cpp
std::vector<PointRecv>().swap((*point_recv));
for (...) (*point_recv).push_back(point_process[i]); // 回写未被使用的点
```
紧接着 L117-118 又把 `point_process` 与 `point_recv` 双双清空,因此这段回写**不产生任何对外效果**(`point_recv` 最终必然为空),属历史残留代码。
### 7.3 剩余点转航迹头(L77-114)
对每个剩余点构造 1 点 `Temp_track`:
| 字段 | 取值 |
|---|---|
| `r / azi / height / amplitude / vr / SNR / RCS` | 点迹对应量(`azi` 为弧度) |
| `pitch_num / speed_dim / range_dim` | 透传 |
| `X[0] = Range·cos(Azimuth)`、`X[1]=0`、`X[2] = Range·sin(Azimuth)`、`X[3]=0` | 位置赋值,速度置 0(**注意 4 维布局**) |
| `CPI_time`、`GNSS_time` | 点迹时间 |
| `temp_track_id = temp_track_id++`、`point_id`、`circle_id` | 取自该点迹(记录链路标识,c3c7e66 新增) |
| `asso_flag = 0`、`buff_round = 1` | 生命周期起点 |
| `d` | 未赋值 → 0 |
---
## 8. 阶段五:临时航迹转可靠航迹(起批)
函数:`tmp_track_to_trust_track()`(L458-671),四步。
### 8.1 步骤 1:收集候选(L467-499)
```cpp
int L = (*Iter).size();
double range = (*Iter)[L-1].r;
double azi = (*Iter)[L-1].azi/PI*180; // 转度,供屏蔽区比较
double quality = (*Iter)[L-1].quality; // V1.5.8:末点航迹质量
if( L <= 10 && L >= rdp_parameter.track_init_point_num_min
&& track_init_prohibit(range, azi) == 0
&& quality >= 按长度取的质量门限 ) // V1.5.8 新增
{ 复制到 Track_to_start; 从 temp_track 中 erase; }
else
Iter++;
```
**入选条件**:
1. **长度**:`track_init_point_num_min ≤ L ≤ 10`(配置项 `[track_init] track_init_point_num_min`,由 TOML 配置文件提供,加载器校验范围 **3~9**;改造前由宿主经 `track_process_parameters_initial` 传入);
2. **不在起批屏蔽区**内(用**末点**的距离/方位判断,见 8.5);
3. **航迹质量达标(V1.5.8,d9a7bd0)**:按长度取门限 `track_init_quality_L3~L7`(配置项)——L=3 →0.7、L=4 →0.6、L=5 →0.5、L=6 →0.4、L≥7 →0.3;比较对象是**末点** `quality`,不达标则本圈不起批、留在 `temp_track` 继续积累。
命中即从 `temp_track` 摘出(先复制后删除,`Track_to_start` 成为候选表)。
> 结果:**一旦长度达到阈值、不落在屏蔽区且末点质量达标,当圈即起批**(没有"连续 M 圈满足才起批"的确认期)。若因屏蔽区被拦住,航迹会继续在 `temp_track` 中增长,直到离开屏蔽区(此时 `L > track_init_point_num_min`,见 8.3 的副作用)或达到 `L=10` 被判消亡;若因质量门限被拦住,则继续积累直到质量回升或临时航迹消亡。
### 8.2 步骤 2:同源去重(L504-550)
```cpp
int L = rdp_parameter.track_init_point_num_min; // 注意:此 L 是"起批点数",不是航迹长度
for (i: 0 .. size-2) for (j: i+1 .. size-1) {
if (两者都未被标记 asso_flag==2) {
if (某条长度 < 3) continue; // 防御性判断(实际不会触发)
if (前三点中存在一点 "T 与 r 完全相同") { // 硬编码比较点 0/1/2
if (Track_to_start[i][L-1].d < Track_to_start[j][L-1].d) j 标记 asso_flag=2;
else i 标记 asso_flag=2;
}
}
}
// 然后统一删除 [0].asso_flag==2 的航迹
```
- **同源判定**:两条候选航迹的前三个点中,只要有一个点的 `T` 与 `r` 完全相等(同一点迹被复制而来),就视为同源重复;
- **择优准则**:比较 `[L-1].d`(第 `track_init_point_num_min` 个点的关联统计距离),**保留 d 更小者**;
- **标记位置**:只写 `[0].asso_flag = 2`,最后统一擦除;
- 去重仅看**前三点**,且无论航迹实际多长都用固定下标 0/1/2。
### 8.3 步骤 3:起批与构造可靠航迹(L552-667)
```cpp
if (Track_to_start[i].size() < 3) continue; // 防御
if (trust_track->size() < rdp_parameter.track_index_max) { // 默认 500 条上限
int index = track_index_mangement.track_ind_get(trust_track);
if (index > 0) { ... }
}
```
**三点卡尔曼初始化**(L563-571,注意使用的是 `L-3, L-2, L-1`,即**最后三个点**):
```cpp
Z0 = 极坐标→直角(点[L-3]); Z1 = (点[L-2]); Z2 = (点[L-1]);
T1 = (点[L-2].T − 点[L-3].T)/1000.0; T2 = (点[L-1].T − 点[L-2].T)/1000.0;
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y,vy,ay]
```
> ⚠ **不一致点**:初始化用「最后三点」,而输出历史点(8.4)只写「前 `L` 个点」。当航迹长度 = `track_init_point_num_min` 时两者一致;当因屏蔽区滞留使长度 > 阈值时,输出的历史点里**不包含**用于初始化的那三个点。
**Trust_Track 字段构造(L573-608)**:
| 字段 | 赋值 |
|---|---|
| `X[6] / P[6][6]` | 三点初始化结果 |
| `X1/X2/X3 = X`、`P1/P2/P3 = P` | 三个 IMM 子模型同状态、同协方差初始化 |
| `u[0..2] = 0.3333` | 模型概率均分 |
| `Track_Index` | `track_ind_get()` 分配的航迹号 |
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值(`CPI_time` 字段) |
| `Amplitude / snr_point / RCS / pitch_num / speed_dim / range_dim` | 末点透传 |
| `track_id / point_id / circle_id` | 取末点的 `temp_track_id / point_id / circle_id`(记录链路标识,b227ae1 新增) |
| `point_flag = 1` | 实点 |
| `Track_Mode = 0` | TWS |
| `Target_Type = 0` | 未确认目标(`UNCONF_TARGET` 宏已删除,直接写 0) |
| `Extrapolate_round = 0` | 外推计数清零 |
| `approach_flag = -1` | 接近标志复位 |
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type / primary_target_flag = 0` | 各类标志复位(`Trust_Track trust_track_tmp = {}` 零初始化,故新航迹的重点目标标记为 0) |
> L606-608 有一处**死代码**:`coor_trans::cart2polar(X[0], X[3], &r, &azi)`(把 `X[3]=vy` 当 y 用),计算结果 `r/azi` 从未被使用。
**航迹号分配**(`track_index_mangement.cpp`):空表返回 1;否则从 `lastest_index+1` 向上找第一个未被占用的号(含 `track_index_max`,默认 500),找不到再从 1 回绕;全满返回 0(**不起批**,该候选被静默丢弃)。
### 8.4 输出更新航迹(L612-664)
- `*Trust_track_num_Output += 1`,在 `track_asso` 已写的行之后追加一行;
- 对 `j = 0 .. L-1`(`L = rdp_parameter.track_init_point_num_min`)写入该航迹的**历史点**:`Track_Index`、`Point_Sum = L`,以及每个点的距离/方位/俯仰、速度、幅度/SNR/RCS、俯仰波位、十字星数据、航向角、时间、直角坐标、原始点信息等(完整映射见第 14 节);
- 之后该航迹的后续更新由 `track_asso`(每次 `Point_Sum=1`)负责。
### 8.5 起批屏蔽区(L699-712)
```cpp
for (i = 0; i < rdp_parameter.track_init_prohibite_area_num; i++)
if (r < rdp_parameter.track_init_prohibite_area_range_max[i] && r > rdp_parameter.track_init_prohibite_area_range_min[i] &&
azi < rdp_parameter.track_init_prohibite_area_azimuth_max[i] && azi > rdp_parameter.track_init_prohibite_area_azimuth_min[i])
return 1; // 位于屏蔽区,禁止起批
return 0;
```
- 判据是**距离-方位矩形区域**(`azi` 已转成度),最多 30 个(`RDP_parameter` 定长数组长度;配置入口由加载器校验 `*_num ∈ [0,30]` 且 4 个数组长度等于 `*_num`,非法配置在 `parameters_reload` 阶段即失败);
- 采用**严格不等号**:恰好落在边界上的点不算屏蔽;
- 判断用**末点**极坐标,不使用高度。
### 8.6 临时航迹质量评估与起批门限(V1.5.8,1ae0cf2 / d9a7bd0)
在阶段二(点-临时航迹关联)每成功延长一个点后,`point_temp_track_asso()` 立即重算该临时航迹末点的质量:
```cpp
L = temp_track_ref.size();
d_tmp = exp(-temp_track_ref[L-1].d); // 末点统计距离项(d 已是马氏距离平方)
// L == 3:取三个点 SNR 的平均;L > 3:只取末点 SNR
snr_tmp = 1 / (1 + exp(-(SNR - 16) / 1.2)); // 以 16 dB 为界的 Sigmoid
// 权重为配置项 track_quality_d_weight(0.5) / track_quality_snr_weight(0.3)
quality_tmp = (w_d * d_tmp + w_snr * snr_tmp) / (w_d + w_snr); // 只用距离 + SNR 两项(不含长度项)
if (L == 3) 三点全部写 quality_tmp; // 质量起点
else 末点 quality = 前一点 quality * c + quality_tmp * (1 - c); // 指数平滑,c = track_quality_iter_coeff(0.8)
```
- `Temp_track::quality` 是 **0~1** 的归一化值;起批时 `Trust_Track.Quality = 末点 quality`,`Point_sum = Valid_point_sum = L`;
- 起批门限按**临时航迹长度 L** 取(比较对象是**末点**质量):L=3 → 0.7、L=4 → 0.6、L=5 → 0.5、L=6 → 0.4、L≥7 → 0.3;
- 质量不达标时该临时航迹**不进入 `Track_to_start`**,继续留在 `temp_track` 中参与下一圈关联(受 `buff_round`/长度 10 的消亡约束)。
> 注意:质量只在「点-临时航迹关联」路径更新,阶段三(点 × 1 点航迹头 → 2 点临时航迹)不写 `quality`;2 点航迹要到下一圈被延长成 3 点时才会首次获得质量值,这与其本来也不能起批(长度不足)是一致的。
---
## 9. 阶段六:临时航迹消亡
函数:`tmp_track_die()`(L673-697):
```cpp
if (n <= 0) erase; // 空航迹
if ((*Iter)[n-1].buff_round > 2 || n >= 10) erase;
```
| 消亡条件 | 含义 |
|---|---|
| 序列为空 | 防御性清理 |
| 末点 `buff_round > 2` | 连续 2 圈没有新点(允许空 1 圈) |
| 长度 `n >= 10` | 达到上限仍未完成起批(例如长期滞留屏蔽区) |
消亡**不输出任何信息**(与可靠航迹消亡不同,起批失败的临时航迹对宿主不可见)。
---
## 10. 起始条件汇总表
### 10.1 条件链(一条 3 点起批航迹的完整判据)
| 阶段 | 判据(源码位置) |
|---|---|
| **0. 输入资格** | 该点未被 `track_asso` 关联(已从 `point_recv` 删除);凝聚后 `100 m ≤ r ≤ 100000 m` |
| **1. 生成航迹头** | 未被阶段二/三消费(`Use_Flag==0`);创建时 `buff_round=1`(L77-114) |
| **2. 航迹头 → 2 点**(L325, L368-372) | 航迹头 `size==1` 且 `buff_round ≥ 2`;`dis ≤ track_init_velocity_max·ΔT`;`dis ≥ track_init_velocity_min·ΔT`;`dis ≥ abs(vr_p)·ΔT`;`dis ≥ abs(vr_h)·ΔT`;`vr_p·vr_h > 0`(隐含 `ΔT > 0`) |
| **3. 2 点 → 3 点及以上**(L164, L191, L221) | 临时航迹 `size > 1` 且末点 `buff_round ≥ 2`;`abs(r_p − r_末点) ≥ 5 m`;`d ≤ 8`(代码写 `d² ≤ 64`,`track_init_threshold=8`);`α < 60°`(`track_init_angle`);`vr_p·vr_末点 > 0` |
| **4. 转可靠航迹**(L484, L556) | `track_init_point_num_min ≤ L ≤ 10`;末点不在起批屏蔽区;**末点质量 ≥ 按 L 取的门限(V1.5.8)**;同源去重后保留;`trust_track.size() < track_index_max` 且成功分配航迹号 |
| **5. 消亡**(L686) | `末点.buff_round > 2`(连续 2 圈无新点)或 `长度 ≥ 10` |
### 10.2 门限汇总
| 判据 | 数值 | 来源 | 可调 |
|---|---|---|---|
| 最大速度 `vmax` | 50 m/s(样例) | 配置项 `track_init.track_init_velocity_max` | ✅(改配置文件) |
| 最小速度 `vmin` | 1 m/s(样例) | 配置项 `track_init.track_init_velocity_min` | ✅(改配置文件) |
| 距离差下限 `abs(Δr)` | **5 m(硬编码)** | `track_init.cpp` L191 | ❌ |
| 起批波门 `d ≤ 8` | `track_init_threshold=8` | 配置项 `track_init.track_init_threshold` | ✅(改配置文件) |
| 起批折角 | `track_init_angle=60°` | 配置项 `track_init.track_init_angle` | ✅(改配置文件) |
| 起批点数 | 3~9(典型 3~4) | 配置项 `track_init.track_init_point_num_min` | ✅(加载器校验 3~9) |
| 临时航迹长度上限 | 10(硬编码) | L484 / L686 | ❌ |
| 允许丢圈数 | 1 圈(`buff_round > 2` 消亡) | L686 | ❌ |
| 航迹头上限等待 | 2 圈 | L686(1 点航迹同判据) | ❌ |
| 起批屏蔽区 | ≤30 个矩形 | 配置段 `[track_init_prohibite_area]`(数组长度 = `*_num`) | ✅(加载器校验 0~30) |
| 起批最低质量(V1.5.8) | L3→0.7 / L4→0.6 / L5→0.5 / L6→0.4 / L≥7→0.3 | 配置项 `track_init.track_init_quality_L3~L7` | ✅(改配置文件) |
| 质量权重(V1.5.8) | 距离 0.5 / SNR 0.3 / 长度 0.2 | 配置段 `[track_quality]` | ✅(改配置文件) |
| 航迹数上限 | 500(默认) | 配置项 `track_index.track_index_max` | ✅(加载器校验 1~500) |
---
## 11. 关键算法细节
### 11.1 两点卡尔曼初始化(`kalman.cpp` L18-58)
输入两个直角坐标量测 `Z0, Z1` 与时间差 `T`(秒):
$$
X=\begin{bmatrix}x_1\\ (x_1-x_0)/T\\ y_1\\ (y_1-y_0)/T\end{bmatrix},
\qquad
P_{axis}=\begin{bmatrix}R & R/T\\ R/T & 2R/T^2\end{bmatrix}
$$
其中 `R` 取 `Z1` 处的**转换量测协方差**(极坐标→直角坐标,见 `README.md` 9.1 节),x/y 两轴独立填充 `P[4][4]`。
> 风险:`T → 0` 时速度与协方差同时发散,函数无除零保护(对应 BUG-18);临时航迹的两点初始化 `T` 来自相邻两圈,实际由时间戳决定,若宿主给出重复时间戳会异常。
### 11.2 EKF 统计距离(`kalman.cpp` L615-693)
对 4 维状态做 CV 一步预测(`F = [[1,T,0,0],[0,1,0,0],[0,0,1,T],[0,0,0,1]]`,过程噪声 `Q = 0.03²·I`,`G=[T²/2, T]ᵀ`):
$$
\hat X_{k|k-1}=F X,\qquad P_{k|k-1}=FPF^{T}+GQG^{T}
$$
非线性量测函数(距离-方位-径向速度):
$$
h(\hat X)=\begin{bmatrix}\sqrt{x^2+y^2}\\ \operatorname{atan2}(y,x)\bmod 2\pi\\ -\dfrac{x v_x+y v_y}{\sqrt{x^2+y^2}}\end{bmatrix},
\qquad H=\left.\frac{\partial h}{\partial X}\right|_{\hat X_{k|k-1}}
$$
量测噪声 `R = diag(σ_r², σ_a², σ_v²) = diag(10², 0.02², 2²)`,新息协方差 `S = H P Hᵀ + R`,残差做两项修正:
- **方位环绕**:`ν_θ = wrapToPi(θ_p − θ_pred)`,归一化到 `[−π, π)`;
- **多普勒盲速折叠**:`ν_v ← ν_v − round(ν_v / v_bind)·v_bind`,其中
$$
v_{bind}=\frac{150000}{f_{GHz}\cdot PRI_{\mu s}}\ \text{(m/s)},
\qquad f_{GHz}=9.2+0.02\cdot f_{ind}
$$
(`PRI ≤ 0` 或 `f ≤ 0` 时返回安全值 1.0;调用方约定 PRI 单位 µs,不允许为 0。)
返回马氏距离平方:
$$
d=\nu^{T}S^{-1}\nu,\qquad \nu=Z-h(\hat X_{k|k-1})
$$
**调用参数来源**:`prt = point.PRF_index`(实际存的是 `DataRev.PRI`,单位 µs);`freq_ind = point.Freq_index`。
### 11.3 折角计算(L448-456)
$$
\alpha=\arccos\!\left(\frac{\vec D_{12}\cdot\vec D_{23}}{\|\vec D_{12}\|\,\|\vec D_{23}\|}\right)\cdot\frac{180}{\pi}
$$
其中 `D12 = (末点 − 前一点)`、`D23 = (新点 − 末点)`,三点均取 **Temp_track 的直角坐标**(`X[0]` 为 x、`X[2]` 为 y)。`α` 越小表示越接近直线;`track_init_angle`(配置项,样例 60°)表示允许的航向偏折。
> 无 `acos` 定义域钳位:当两点重合(分母为 0)或浮点误差使比值略超 ±1 时结果为 `NaN`,`NaN < 60` 恒为 false → 表现为「该点无法关联」,不崩溃。
---
## 12. 与其他文档/实现的差异
### 12.1 与 `README.md` 第 8.3 / 9.3 节的差异(README 已同步本节结论)
> 说明:下表为本文档成文时发现的差异,当时 README 尚未同步 7d5a56f 的改动;随后已按本节结论更新 README 第 2、7.2、8.1、8.3、9.3、9.6、10.2 等节,「README 描述」列对应的是更新前的旧表述,保留以说明改动缘由。
| 项 | README 描述(更新前) | 当前源码 |
|---|---|---|
| 点-航迹头判据 | 按距离分段:近程 `r≥1000` 全速 / `r<1000` 半速;中远程 `r≥2000` 全速 / `r<2000` 1/2~1/3 速 | 该分支已**整体注释停用**,改为统一 `track_init_velocity_min·ΔT ≤ dis ≤ track_init_velocity_max·ΔT` **外加两条多普勒一致性条件** `dis ≥ abs(vr_p)·ΔT`、`dis ≥ abs(vr_h)·ΔT`(L361-372) |
| 起批触发长度 | 长度满足 `track_init_point_num_min ≤ L ≤ 10` | 一致 |
| 初始化用点 | 最近三点 | 不一致:取下标 `track_init_point_num_min-3 ~ -1` 的三个点,航迹长度大于起批点数时**不是**航迹真正的末三点(详见第 15 节第 2 条) |
| 输出历史点 | "将临时航迹的全部 L 个点写入" | 实际只写**前 `track_init_point_num_min` 个点**(L614 的 `L` 是起批点数,不是航迹长度) |
| use_flag 过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹,`track_init` 入口(L59-70)也会再剔除一次,两道处理叠加,描述成立 |
### 12.2 与 `track_init_direct_tracking.cpp`(引导跟踪版,未启用)的差异
| 项 | `track_init.cpp`(TWS,启用) | `track_init_direct_tracking.cpp`(未启用) |
|---|---|---|
| 入口 | 完整实现 | `track_init_process_logic` 空实现(L11-23) |
| 点-航迹头判据 | 统一门限 + 两条多普勒一致性条件 | 仍为分距离段门限(原 `V_MAX/5` 亦在其注释中) |
| 起批长度 | `L ≥ track_init_point_num_min`(长度可大于阈值) | `L == track_init_point_num_min`(严格相等) |
| 去重 | 有同源去重 | 无 |
| 屏蔽区 | 有 | 无 |
| 航迹号 | `track_ind_get()` 分配 | 外部传入 `track_ID` |
| 消亡阈值 | `buff_round > 2` | `buff_round > 1`(更严格) |
| 输出模式 | `Track_Mode = 0`(TWS) | `Track_Mode = 1`(TAS) |
---
## 13. 参数与调参
> **参数配置方式改造后**:与起始流程相关的全部可调参数都在 TOML 配置文件里(样例 `config/config_tmp.toml`),由公开接口 `parameters_reload(配置文件路径)` 加载到 `RDP_parameter`;`parameters.h` 不再有这些宏,宿主也不再下发 `RadarPara`。改参数 = 改配置文件后重新调用 `parameters_reload()`(加载失败时参数保持不变)。完整参数表与严格校验规则见 README 第 10 节。
### 13.1 配置项(样例值,均为「配置段.键」)
| 配置项 | 样例值 | 含义 | 调大效果 |
|---|---|---|---|
| `track_init.track_init_threshold` | 8 | 起批波门,实际生效为 `d ≤ 8` | **更容易起批**(虚警增多) |
| `track_init.track_init_angle` | 60 | 起批折角(度) | 更容易起批(允许更急的转向) |
| `track_associate.sigma_range/azimuth/pitch/velocity` | 10 / 0.02 / 0.2 / 2.0 | EKF 量测噪声 | 影响 `S` 与 `d`,进而影响关联难度 |
| `track_index.track_index_max` | 500 | 航迹号/航迹数上限(1~500) | — |
| `cohesion.point_cohesion_range_min/max` | 100 / 100000 | 有效量程(凝聚阶段过滤) | — |
| `track_init.track_init_point_num_min` | 3 | 起批点数(加载器校验 3~9):调大 → 起批更慢、更抗虚警,但机动目标捕获更迟 | |
| `track_init.track_init_velocity_max/min` | 50 / 1 | 头-点关联的速度区间;`min` 调大可滤除静止/慢速点 | |
| `radar_params.radar_work_mode` | 0 | 当前对起始流程**无实际影响**(近/远程分支取值相同) | |
| `track_init_prohibite_area.*_num` + 4 个数组 | 0 + 空数组 | 起批屏蔽区(距离 m / 方位 度;≤30 组,数组长度 = `*_num`) | |
| `track_init.track_init_quality_L3~L7` | 0.7 / 0.6 / 0.5 / 0.4 / 0.3 | 按临时航迹长度的起批最低质量(V1.5.8) | 降低 → 更容易起批 |
| `track_quality.*` | 0.5 / 0.3 / 0.2 / 0.8 | 质量权重、平滑系数与衰减系数(V1.5.8) | 影响质量评估与消亡速度 |
> 旧 `RadarPara` 中的 `track_start_threshold` 字段原本就未被实现读取;新结构体只保留实现真正使用/保留的参数(见 README 10.3 对照表)。
### 13.2 调参直觉
- **虚警多、起批太容易**:先降 `track_init_threshold`(8 → 5 左右),再降 `track_init_angle`;必要时提高 `track_init_point_num_min`。
- **真实目标起批慢**:提高 `track_init_threshold` / `track_init_angle`,或降低 `track_init_point_num_min`(最小 3)。
- **静止/低速杂波起批**:提高 `track_init_velocity_min`。
- **某方位扇区/距离段禁止起批**(如地物、干扰区):配置起批屏蔽区,比调门限更精准。
- **注意**:`config/config_tmp.toml` 是相扫取值(`scan_mode = 0`);机扫部署时需配 `scan_mode = 1`,且 `extrapolate_max_tas`、`height_smooth_win`、`track_die_round_tas`、`tas_track_num_max` 按机扫取值配置(见 README 4.2、10.1)。
---
## 14. 输出字段映射
对每条新起批航迹的每个历史点 `j = 0 .. track_init_point_num_min-1`(L614-664):
| 输出字段 | 来源 | 备注 |
|---|---|---|
| `Track_Index` | 新分配的航迹号 | 同一行内所有点相同 |
| `Point_Sum` | `= track_init_point_num_min` | 与 `track_asso` 输出的 `Point_Sum=1` 可作区分 |
| `Range` / `Azimuth` | 点的 `r` / `azi`(→度) | 极坐标 |
| `Elevation` | `asin(h/r)`(度,钳位 ±1) | BUG-27 修复后的定义域钳位 |
| `z` | 点的 `height` | |
| `Range_V` | `sqrt(X[1]² + X[3]²)` = `sqrt(vx²+vy²)` | 速度**模值**(非径向速度) |
| `Direction_Angle` | `atan2(vy, vx)`(→度, `[0,360)`) | 航向角 |
| `x / v_x / y / v_y` | 点的 `X[0..3]` | 注意 4 维布局 |
| `range_point / azi_point / elev_point / vr_point` | 点的原始测量 | `point_type=0` |
| `Amplitude / track_snr / track_rcs` | 点的 `amplitude / SNR / RCS` | |
| `pitch_num / speed_dim / range_dim` | 点透传 | |
| `track_time` | `CPI_time/1000`(秒) | |
| `GNSS_time` | 点透传 | |
| `Flag_Point` | 恒为 **1**(实点) | |
| `Track_Mode` | 恒为 **0**(TWS) | |
| `Quality` | **该历史点的临时航迹质量 × 100**(V1.5.8) | 内部 `quality` 为 0~1,输出折算为 0~100;同一条航迹的各个历史点可能取值不同(L=3 时三点相同,之后只有末点更新) |
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经配置项 `radar_system_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
---
## 15. 已知问题与风险点
> 以下为阅读源码得到的观察,**不代表已确认的缺陷**,部分为有意保留的历史行为;修改前请结合实测数据评估。
1. **硬编码 5 m 距离差门限**(L191):`|r_p − r_末点| < 5` 的点永不参与关联。对切向飞行、远距离慢速目标可能造成漏关联(起批失败)。
2. **初始化用点不是航迹真正的末三点**(L505、L563-565 vs L614):起批流程中 `L = track_init_point_num_min`(L505 覆盖了前面循环里的航迹长度变量),Z0/Z1/Z2 取下标 `L-3 / L-2 / L-1`,输出循环也只写到 `j = L-1`。因此当航迹长度 N 大于起批点数时,参与初始化的只是**前 L 个点中的最后三点**,而不是航迹真正的末三点;第 L 个点之后的尾部点既未参与初始化也不出现在输出中(这些点通常来自滞留在起批屏蔽区期间的积累)。
3. **去重只比较前三个点**(L517-519 硬编码 0/1/2),且优度指标取 `[track_init_point_num_min-1].d`(长度大于阈值时非末点);若 `track_init_point_num_min > 3`,共享前三点但后续分叉的两条航迹会被误判为同源。
4. **无一对一关联约束**:一个点可同时为多条临时航迹续点(阶段二/三),产生航迹分裂,虚警场景下临时航迹表可能快速膨胀(靠去重与消亡收敛)。
5. **ΔT ≤ 0 无显式校验**(BUG-33 遗留):头-点关联靠 `dis ≤ track_init_velocity_max·ΔT` 隐式排除负 ΔT;EKF 关联路径 `d_cal_track_init_EKF(ΔT<0)` 会得到无物理意义的结果,只是通常会被 `d ≤ 8` 挡住。
6. **判据对马氏距离平方二次平方**(L221):`track_init_threshold=8` 时实际门限为 `d ≤ 8`(3 自由度约 95.4%)。若按字面 `d² ≤ 64` 理解会误判为 `d ≤ 64`(极宽松)。修改 `track_init_threshold` 时务必记得这一层。
7. **两点/三点初始化无 `T > 0` 保护**(BUG-18):`T` 极小(重复/乱序时间戳)时速度、加速度与协方差会爆炸性放大。
8. **死代码**:L72-75 的 `point_recv` 回写随即被 L117-118 清空;L606-608 的 `cart2polar` 结果未使用;L348-356 的近/远程分支取值相同。
9. **`acos` 无定义域钳位**(L452):浮点越界产生 `NaN`,表现为条件恒假(静默丢弃该关联),不报错也不崩溃。
10. **航迹数饱和静默丢弃**:`trust_track.size() ≥ track_index_max`(配置项,默认 500)或 `track_ind_get()` 返回 0 时,候选被直接跳过且该临时航迹已从 `temp_track` 摘除 → 起批机会永久丢失。
11. **未赋值输出字段**(第 14 节):宿主若直接使用 `x_Predict`、`Flag_Direction`、`Target_Type` 等字段,会读到非本次写入的数据。
12. **`PI` 为 float 常量**(3.1415926f)参与双精度运算,角度换算存在约 1e-7 量级偏差,属既有实现风格。
13. **精度/量纲提醒**:`Temp_track::vr` 直接取点的 `Velocity`(多普勒)作为径向速度,EKF 关联时用于残差折叠(盲速 `v_bind`)与符号判据;若宿主的 `Velocity` 存在模糊,符号判据 `vr_p·vr_末点 > 0` 可能失效。
14. **输出数组行号可能越界(极端场景)**:容量判据用 `trust_track->size() < track_index_max`,而 `Trust_Track_Output` 的行计数已在 `track_asso` 阶段按「消亡前」的航迹数推进,中间的 `track_die` 删除 D 条后腾出的配额会让起始流程多写 D 行;当 D ≥ 2 且当圈起批数占满配额时,写入行号可超过 `MAX_TRACK_NUM=500`(详见 2.3)。建议把判据改为同时校验 `*Trust_track_num_Output < MAX_TRACK_NUM` 与 `trust_track->size() < track_index_max`。
15. **起批质量门限比较的是「末点」质量而非整条航迹质量(V1.5.8)**:`tmp_track_to_trust_track()` 取 `(*Iter)[L-1].quality` 与按长度取的门限比较,而该质量在 L>3 时只是末点的指数平滑值(L=3 时三点同值),并不代表整条临时航迹的平均质量;长度越大门限越低,实际是「用更宽松的门限校验最新一点」。
16. **起批质量来源依赖阶段二**:`quality` 只在「点-临时航迹关联」成功后更新,阶段三产生的 2 点临时航迹不带质量值;长度 3 的临时航迹在第三次延长时写入三点同值,因此其起批质量有定义。若将来把起批点数放宽到 3 以下(当前加载器校验 3~9),则会出现质量恒为 0、永远无法通过质量门限的情况。
---
## 16. 代码索引
### 16.1 `track_init.cpp`
| 行号 | 内容 |
|---|---|
| L11-143 | `track_init_process_logic` 主流程 |
| L36-38 | 装填点迹 |
| L40-47 | `buff_round` 自增 |
| L49 | `if(point_process.size()>0)` 主分支 |
| L52 / L55 | 两阶段关联调用 |
| L57-70 | 删除已消费点迹 |
| L72-75 | 点迹回写(等效空操作) |
| L77-114 | 剩余点 → 航迹头 |
| L117-118 | 清空 `point_process` / `point_recv` |
| L121 | 转可靠航迹调用 |
| L126 | 临时航迹消亡调用 |
| L145-304 | `point_temp_track_asso` |
| L164 | ≥2 点 + `buff_round≥2` 资格 |
| L191 | `abs(Δr) ≥ 5 m` 门限 |
| L194-195 | EKF 统计距离 `d` |
| L199-205 | 折角 `α` |
| L221 | 三条件判据 |
| L237-281 | 生成新临时航迹(两点初始化) |
| L283-303 | 删除被关联的原航迹 |
| L306-445 | `point_track_head_asso` |
| L325 | 1 点 + `buff_round≥2` 资格 |
| L361-372 | 门限判据(含停用的旧分支注释) |
| L388-427 | 生成 2 点临时航迹 |
| L429-443 | 删除被关联的航迹头 |
| L448-456 | `alpha_cal_track_init` |
| L458-671 | `tmp_track_to_trust_track` |
| L484 | 起批长度 + 屏蔽区条件 |
| L504-550 | 同源去重 |
| L552-667 | 起批与输出 |
| L563-571 | 三点卡尔曼初始化 |
| L573-604 | `Trust_Track` 构造 |
| L612-664 | 输出历史点 |
| L673-697 | `tmp_track_die` |
| L699-712 | `track_init_prohibit` |
| L714-723 | 构造 / `reset()` |
### 16.2 依赖
| 文件 | 函数 | 用途 |
|---|---|---|
| `kalman.cpp` L18-58 | `kalman_filter_init_2dots` | 两点(4 维)初始化 |
| `kalman.cpp` L125-194 | `kalman_filter_init_3dots` | 三点(6 维)初始化 |
| `kalman.cpp` L615-693 | `d_cal_track_init_EKF` | 起始关联统计距离 |
| `kalman.cpp` L780-789 | `Bind_speed` | 盲速计算 |
| `coor_trans.cpp` L8-20 | `polar2cart` / `cart2polar` | 坐标转换 |
| `track_index_mangement.cpp` L14-77 | `track_ind_get` | 航迹号分配 |
| `data_process.cpp` L22 | `data_preprocess` | 一圈完成 → `model=1` |
| `data_process.cpp` L167 起 | `track_process` | 起始流程调用点(L187-201 调用 `track_asso`/`track_die`/`track_init`) |
| `data_process.cpp` L246-258 | 参数加载 | `parameters_reload()`:TOML 配置 → 全局对象 `rdp_parameter`(原「参数初始化/修改」已删除) |