更新:1、增加手动设置重点目标的功能;

2、调整航迹关联时外推圈数限制,不过多外推;
3、调整航迹消批条件,重点航迹消批圈数提高;
4、更新文档;

Signed-off-by: waiwaylee <waiwaylee@foxmail.com>
This commit is contained in:
2026-09-23 09:35:13 +08:00
parent e0bf227be3
commit 50e0b5cb74
12 changed files with 247 additions and 118 deletions
+8
View File
@@ -17,6 +17,10 @@
- 新增日志文件输出功能:`parameters.h` 增加 `RDP_LOG_ENABLE` 开关(定义时启用;**当前源码中该宏为注释状态,即默认不重定向、日志输出到控制台**)与 `RDP_LOG_DIR` 目录宏(`.\\rdp_logs\\`);新增 `rdp_log.h/.cpp` 日志模块,首次 `Data_Process_Factory::GetB()` 时按 `RDP_LOG_ENABLE` 决定是否用 `rdbuf()` 将 `std::cout` 重定向到 `.\rdp_logs\` 下按日期+时间命名的日志文件(`rdp_YYYYMMDD_HHMMSS.log`,目录不存在自动创建),`Destroy()` 销毁实例时恢复 `std::cout` 并关闭文件;开关注释关闭时不重定向、日志仍输出到标准输出;日志文件在 `CMakeLists.txt` 的 SOURCES 中注册。
- 新增 `RDP_LOG` 日志输出宏(`rdp_log.h`):自动在每条日志前附加 `[时间戳][代码文件:行号]` 信息后再输出到标准输出(重定向开启时随之写入日志文件),供后续新增日志统一使用。
- 更新 README.md(604f158、d6cdbc0):目录结构补充 `archived_docs/` 归档目录;9.6 节改写 TWS 关联 ΔT≤0 的处理说明;功能特性、模块职责、已知问题与参考文档的文档引用路径同步指向 `archived_docs/`;版本历史表补充 604f158、d6cdbc0 提交记录。
- TWS 关联新增**高度门限**(e0bf227,`track_asso.cpp`):原实现中分距离段的高度门限代码块整体为注释状态、`d_h` 恒为 1(等于不生效),现改为独立判定 `thd_h = |h_track − h_point|` 与航迹距离分段比较后再参与关联(分段值见 Changed),与 TAS 通道的高度门限取值一致。
- 关联门限判定重构为三个显式布尔量(e0bf227):`track_asso.cpp` 与 `track_asso_tas.cpp` 的 `model_filter()` 原先把统计距离、高度、距离三类判据混写在一个长 `if` 条件里(高度门限在 TWS 侧整体被注释),现分别计算 `thd_d`(统计距离门限,按距离分段)、`thd_h`(高度门限)、`thd_r`(TWS 距离门限)后再以 `thd_d && thd_h && thd_r`(TAS 为 `thd_d && thd_h`)判定候选,逻辑等价性更易核对;TWS 侧同时移除了 `manual_tracking_flag==0` 的注释残留代码块与其缩进。
- 更新 README.md(e0bf227):目录结构(第 3 节)删除已移除的 `docs/data_process_flow_report.html` 并补充 `design_docs/` 留档目录(被 .gitignore 忽略);2、9.6、9.10、10.1、11.1、12、13 节按源码同步 `ASSO_THORD = 4`(关联波门等效 d < 4、TWS 分段比例门限 16/1000 与 16/100)、TWS/TAS 高度门限的分段取值、`thd_d/thd_h/thd_r` 三类门限判定结构、TAS 波束数据率门控与 e0bf227 提交记录;13 节参考文档表删除已不存在的 HTML 行。
- 同步 `docs/data_process_flow_report.md`(e0bf227):4.2 节波门由「统计距离 + 距离 + 恒通过的俯仰门限」改为「统计距离 thd_d + 高度 thd_h + 距离 thd_r」并给出 TWS 高度门限分段表,7 节 TAS 高度门限 2000~3000 m 段由 200 m 更正为 250 m;7.2 节判据与 9.1 节参数表按源码更新(`ASSO_THORD = 4`、`d² < 16`);8.2 节改为单条件数据率门控并在选中目标后按 CPI 时间计算外推步长。
- 输出航迹补充「靠近/远离」方向标志 `Flag_Direction`(f249fdc):TWS 关联输出(`track_asso.cpp`)与 TAS 关联输出(`track_asso_tas.cpp`)在写入 `vr_point` 后追加 `Flag_Direction = vr_point > 0 ? 1 : 0`,即径向速度为正判为靠近(1)、为零或负判为远离(0)。该字段此前已在 `data_process_class_dll.h` 的 `Track` 结构体中声明(注释「方向标志 1 靠近/0 远离」)但从未被赋值,宿主读到的是未初始化/旧值。
- 新增三份流程与专项文档(f249fdc):`docs/data_process_flow_report.md`(数据处理流程与关键逻辑说明:总体数据流、前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡与航迹号管理、TAS 波束控制、参数脉络与关键约定)、`docs/data_process_flow_report.html`(同内容的 HTML 渲染版)、`docs/track_init_readme.md`(航迹起始 Track Init 详细逻辑说明:分阶段流程、起始条件汇总、参数调参、输出字段映射、已知风险与代码索引)。
- 更新 README.md(7d5a56f、f249fdc):目录结构新增 `docs/` 目录;2、7.2、8.1 节满圈判据改为「方位波位号 == 0」并说明 `last_beam_num` 不再参与判断;8.3、9.3 节改写点-航迹头关联判据与起批输出历史点数;9.6 节补充方向标志输出与点迹消费说明;2、10.1 节 `RDP_LOG_ENABLE` 说明改为当前实际状态(注释停用、默认不重定向);11.4 节新增航迹起始未修复观察;版本历史表补充 7d5a56f、f249fdc,参考文档表补充 `docs/` 三份文档。
@@ -37,6 +41,9 @@
- 扫描一圈完成判据改为「方位波位号 == 0」(7d5a56f):原判据「当前方位波位号 < 上一帧波位号」在机扫模式下遇到转台反转时波位号会变小但并未扫完一圈,造成误判;现 `data_preprocess()` 仅在 `Data_Input[0].Beam_index_aiz == 0` 时返回 1(TWS 满一圈,执行航迹处理),否则返回 2(继续蓄数据,只做滑窗凝聚)。原判据代码注释保留,`last_beam_num` 仍被赋值但不再参与判断。调用方需保证点迹的方位波位号按 0~Beam_num-1 编号且每圈必然出现 0。
- 点迹-航迹头关联判据改为统一速度区间并增加多普勒-几何一致性约束(7d5a56f,`track_init.cpp`):原先按扫描体制与距离分段收紧速度门限的分支(近程 `r≥1000` 全速 / `r<1000` 半速,中远程 `r≥2000` 全速 / `r<2000` 取 1/2~1/3 速)整体注释停用,改为 `dis ≤ V_MAX·ΔT` 且 `dis ≥ V_MIN·ΔT`,同时要求 `vr_point·v_track_head > 0`,并新增 `dis ≥ |vr_point|·ΔT` 与 `dis ≥ |v_track_head|·ΔT` 两条约束——切向高速目标在 ΔT 内的实际位移远小于 `|vr|·ΔT`,据此被排除,用于限制由切向高速点迹起批出的短航迹。其中 `vmax` 的近程/中远程分支取值相同,均取 `Work_Parameter.V_MAX`。
- 同步 `docs/` 内两份专项文档(本次文档更新):`docs/data_process_flow_report.md` 附录与 `docs/track_init_readme.md` 第 12.1 节的「与 README 的差异」说明改为「差异已按结论同步到 README」并保留更新前的旧表述作为改动缘由;其中「起始输入过滤」一行更正为「上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹 + `track_init` 入口再次剔除」,「初始化用点」一行更正为「取下标 `track_start_point_num-3 ~ -1`,航迹更长时不是真正的末三点」;`docs/track_init_readme.md` 第 15 节第 2 条相应更正(`track_init.cpp` L505 的 `L` 覆盖为 `track_start_point_num`,第 L 个点之后的尾部点既不参与初始化也不输出),第 14 节补充说明 `Flag_Direction` 仅由关联输出路径赋值、起批输出不写该字段。
- 关联波门 `ASSO_THORD` 由 3 调大至 4(e0bf227,`parameters.h` 相扫/机扫两组宏同步):判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 `d` 本身已是马氏距离平方,等效门限由 `d < 3` 放宽为 `d < 4`(TWS 分距离段的比例门限随之由 9/1000、9/100 变为 16/1000、16/100),配合高度门限判定提高机动目标的关联概率;调参时仍需注意这一层「二次方」语义(BUG-20 有意保留)。
- TWS 与 TAS 关联的高度门限统一为同一组按航迹距离分段的取值(e0bf227):`r_track ≤ 1000 m` 时 `|h_track − h_point| ≤ 150 m`、`1000~2000 m` 时 ≤ 200 m、`2000~3000 m` 时 ≤ 250 m、`3000~5000 m` 时 ≤ 300 m、`> 5000 m` 不约束。其中 TWS 侧为该门限首次生效(原代码块被注释),TAS 侧 `2000~3000 m` 段由 200 m 放宽到 250 m。
- TAS 波束输出的数据率门控条件修正(e0bf227,`tas_ctrl.cpp::tas_beam_output()`):原判据同时要求「距上次波束输出超过 DATA_RATE_TAS」与「航迹时间 T_track 与最新时间戳之差超过 DATA_RATE_TAS」,后者与「选取 CPI 时间最早目标」的语义冲突,会额外抑制本可输出的目标;现仅保留 `delta_T_last_track = (latest_timestamp − last_track_time)/1000 > DATA_RATE_TAS` 一条门控,`delta_T` 改为在选中该目标后按 `(latest_timestamp − CPI_time)/1000` 计算并用于外推,使 TAS 目标输出间隔严格跟随数据率。
### Fixed
@@ -47,6 +54,7 @@
### Removed
- 删除已注释停用的无用代码残留(d6cdbc0):`data_process.cpp` 中 TWS/TAS 分支的 `std::cout` 调试输出注释块,以及 `dot_coh.cpp` 中早期「1 km 以内 SNR < 20 dB 点迹过滤」的注释代码块。仅清理注释,不改变任何运行逻辑。
- 删除 `docs/data_process_flow_report.html`(e0bf227):该 HTML 渲染版与 `docs/data_process_flow_report.md` 内容重复且已明显滞后,保留价值不足;同内容的 HTML 已在 git 忽略的 `design_docs/` 内另行留档。README 目录结构、参考文档表与版本历史表中指向该文件的条目同步移除。
## [1.5.5] - 2026-08-27
+25 -19
View File
@@ -55,7 +55,7 @@
- **两帧滑窗点迹凝聚**:TWS 点迹先与上一帧缓存合并凝聚,再进入关联,兼顾跨波位(CPI)相邻点迹的分裂问题;近程模式增加速度维约束。
- **逻辑法航迹起始**:每圈按固定顺序推进——① 未关联点迹先与 ≥2 点临时航迹做 EKF 波门 + 折角约束关联(延长临时航迹),② 再与 1 点航迹头做速度法关联(两点卡尔曼初始化出 2 点临时航迹),③ 两步都做完后删除被消费的点迹、剩余点迹转成新航迹头,④ 长度达标的临时航迹经三点卡尔曼初始化转可靠航迹;带起批屏蔽区与重复航迹去重。
- **3 模型 IMM 滤波**:三个 Singer 模型(低/中/高过程噪声),量测为「距离-方位-径向速度」三维的 EKF,多普勒残差按盲速折叠,似然用 3×3 新息协方差。
- **最近邻数据关联**:TWS 按航迹距离分段收紧模型 3 波门并附加距离门限;TAS 附加按距离分段的高度门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。
- **最近邻数据关联**:三模型统计距离波门 `ASSO_THORD = 4`(等效 d < 4,TWS 另按航迹距离分段收紧模型 3 波门),再叠加高度门限(≤1000 m 150 m / 2000 m 200 m / 3000 m 250 m / 5000 m 300 m,更远不约束)与 TWS 弦距-位移距离门限。ΔT ≤ 0(点迹时间戳不晚于航迹时间)的候选不更新航迹状态、仅消费该点迹(标记已使用),避免其继续起批而生成虚假 TWS 航迹。关联输出的航迹同时给出**靠近/远离方向标志**:`Flag_Direction = vr_point > 0 ? 1 : 0`(1 靠近 / 0 远离),TWS、TAS 两通道一致。
- **高度维独立平滑**:按距离分段选择滑动平均窗长(5/6/7/9 点),抑制俯仰测量跳变。
- **TAS 波束控制**:手动转 TAS 目标入队(队列长度与容量随扫描体制切换),按最新时间戳外推输出跟踪波束(距离/方位/俯仰/批号)。
- **航迹生命周期管理**:外推轮数计数、超时消亡、手动删除、全清空(含各模块 reset,防重连后幽灵航迹)。
@@ -77,8 +77,8 @@
│ └── requirements.md # 需求说明:日志文件输出功能修改要求(含 rdbuf() 重定向参考示例)
├── docs/ # 专项说明文档(按当前源码整理,与 README 互补)
│ ├── data_process_flow_report.md # 数据处理流程与关键逻辑说明(前处理/判圈、凝聚、关联与滤波、起始、外推消亡、TAS 波束、参数脉络)
│ ├── data_process_flow_report.html # 上文的 HTML 渲染版(浏览器直接打开)
│ └── track_init_readme.md # 航迹起始(Track Init)详细逻辑说明(阶段流程、条件汇总、输出字段映射、风险点、代码索引)
├── design_docs/ # 设计与讨论资料留档(被 .gitignore 忽略、不入库;含设计讨论、产品介绍与 README/流程报告的导出、渲染版本)
├── .vscode/ # VS Code 工程配置
│ ├── settings.json # cmake.generator=NMake、MSVC2013 x86 环境变量、compile_commands
│ ├── cmake-variants.yaml # X256_PS / X256_MS × Release / Debug 变体
@@ -100,7 +100,7 @@
│ ├── dot_coh_tas.h / .cpp # TAS 点迹凝聚
│ ├── coor_trans.h / .cpp # 极坐标 ↔ 直角坐标转换
│ ├── kalman.h / .cpp # 卡尔曼滤波:两点/三点初始化、线性 KF、EKF(含多普勒)、统计距离、盲速计算
│ ├── track_asso.h / .cpp # TWS 航迹关联:IMM 交互 → 滤波 → 输出、距离/俯仰门限、高度平滑
│ ├── track_asso.h / .cpp # TWS 航迹关联:IMM 交互 → 滤波 → 输出、统计距离/高度/距离门限、高度平滑
│ ├── track_asso_tas.h / .cpp # TAS 航迹关联(独立实现,量测门限与数据率不同)
│ ├── track_init.h / .cpp # 逻辑法航迹起始:点-临时航迹关联、点-航迹头关联、转可靠航迹、屏蔽区
│ ├── track_init_direct_tracking.h/.cpp# 引导跟踪航迹起始(当前入口为空实现,未启用)
@@ -693,18 +693,19 @@ $$
#### TWS(track_asso.cpp)
对每条航迹 × 每个点计算三模型 d1, d2, d3,波门按航迹距离分段:
对每条航迹 × 每个点计算三模型 d1, d2, d3,波门按航迹距离分段(`ASSO_THORD = 4`,判据写作 `d*d < ASSO_THORD*ASSO_THORD`,而 d 本身已是马氏距离平方,故下表的等价门限为 d < 4):
| 航迹距离 | 模型 1/2 门限 | 模型 3 门限 |
|---|---|---|
| r_track ≤ 500 m | d_i² < 9 | d_3² < 9/1000(≈关闭) |
| 500 < r_track < 1000 m | d_i² < 9 | d_3² < 9/100(收紧) |
| r_track ≥ 1000 m | d_i² < 9 | d_3² < 9 |
| r_track ≤ 500 m | d_i² < 16 | d_3² < 16/1000(≈关闭) |
| 500 < r_track < 1000 m | d_i² < 16 | d_3² < 16/100(收紧) |
| r_track ≥ 1000 m | d_i² < 16 | d_3² < 16 |
同时满足两个附加门限:
关联判据在 `model_filter()` 中拆为三个布尔量,三者同时通过才记为候选:`thd_d`(上表统计距离门限)、`thd_h`(高度门限)、`thd_r`(距离门限)。
- **俯仰门限 d_p**:当前恒为 true(分距离段 7°/5°/10° 的逻辑在源码中被注释停用);
- **距离门限 d_r**:以航迹-点迹弦距 R = √(r_t² + r_p² − 2·r_t·r_p·cos Δθ)(Δθ 为环绕方位差)与航迹速度位移 VT = v_track·ΔT 判断:
- **高度门限 thd_h**(e0bf227 起生效):以 |h_track − h_point| 与航迹距离分段比较,取值与 TAS 通道一致——≤ 1000 m 时 ≤ 150 m、1000~2000 m 时 ≤ 200 m、2000~3000 m 时 ≤ 250 m、3000~5000 m 时 ≤ 300 m、> 5000 m 时不约束。该项原为注释停用的代码块(等价于恒通过),现正式参与关联判定,用于压制俯仰测量跳变引起的错误关联;
- **俯仰门限 d_p**:分距离段 7°/5°/10° 的逻辑仍在源码中注释停用,不参与判定(其作用已由高度门限覆盖);
- **距离门限 thd_r**:以航迹-点迹弦距 R = √(r_t² + r_p² − 2·r_t·r_p·cos Δθ)(Δθ 为环绕方位差)与航迹速度位移 VT = v_track·ΔT 判断:
$$
VT<100\ \text{且}\ R>300\ \Rightarrow 拒绝;\qquad
@@ -726,17 +727,17 @@ $$
#### TAS(track_asso_tas.cpp)
只处理 tas_track_idx 指定的航迹,门限:
只处理 tas_track_idx 指定的航迹,只有统计距离与高度两个门限,两者同时通过(`thd_d && thd_h`)才记为候选:
$$
d_i^2 < ASSO\_THORD^2 = 9\ \ \text{且高度门限}\ d_h=1
thd\_d:\ d_i^2 < ASSO\_THORD^2 = 16 \qquad\text{且}\qquad thd\_h:\ \text{高度门限通过}
$$
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 200 m;3000~5000 m:≤ 300 m;>5000 m:不约束。
高度门限按航迹距离分段:≤1000 m:|h_t−h_p| ≤ 150 m;1000~2000 m:≤ 200 m;2000~3000 m:≤ 250 m;3000~5000 m:≤ 300 m;>5000 m:不约束(与 TWS 通道 9.6 的取值相同)。
输出航迹同样写入**方向标志** `Flag_Direction`(由 `vr_point` 符号决定,f249fdc),规则与 TWS 通道一致。
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 3(而非 √d < 3)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。
> ⚠ **波门语义提示**:代码中 d 本身已是马氏距离平方,但门限写作 d*d < ASSO_THORD*ASSO_THORD,等效于 d < 4(而非 √d < 4)。该行为在 BUG-20 中被识别,**有意保留未改**;ASSO_THORD/1000、/100 已改为浮点除法(BUG-21 修复)。TAS 不区分距离段,`ASSO_THORD` 原值直接作为门限。
### 9.7 高度平滑
@@ -771,14 +772,18 @@ $$
- **入队条件**(仅手动):manual_tracking_flag==1 且 Track_Mode==0 且队列未满(MAX_TAS_NUM,相扫 4 / 机扫 1)且不在 TAS 禁止区内。入队即置 Track_Mode=1、以航迹时间初始化 last_track_time,并输出一次航迹更新。
- **出队**:队列中目标在航迹表中不存在、或不再处于手动 TAS 状态时清除。(自动转 TAS / 自动退出的 tas_auto_start/tas_auto_end 逻辑全部注释停用——只支持手动。)
- **波束输出**:在 TAS 队列内遍历,选择满足**数据率门控**的目标——目标距上次波束输出(`last_track_time`)的时间、以及航迹时间 T_track 与最新时间戳 latest_timestamp 的差值,均须大于运行时参数 `Work_Parameter.DATA_RATE_TAS` 秒(单位 s,1521592 起由 Hz 改为 s,需为 >0 的有效值),保证 TAS 目标输出间隔不随波束时间(Beam_Ctrl 调用频率)变化;多个候选中取 CPI 时间最早者,用其状态外推到 latest_timestamp:
- **波束输出**:在 TAS 队列内遍历,逐个目标先按**数据率门控**筛选——仅要求目标距上次波束输出(`last_track_time`)的时间 `delta_T_last_track = (latest_timestamp − last_track_time)/1000` 大于运行时参数 `Work_Parameter.DATA_RATE_TAS` 秒(单位 s,1521592 起由 Hz 改为 s,需为 >0 的有效值),保证 TAS 目标输出间隔不随波束时间(Beam_Ctrl 调用频率)变化;对通过门控的目标再比较航迹时间 `T_track`,取 CPI 时间最早者,选中后按其 CPI 时间与最新时间戳之差外推到 latest_timestamp:
$$
\Delta T=(latest\_timestamp-CPI\_time)/1000,\qquad
x_p=x+v_x\Delta T,\quad y_p=y+v_y\Delta T,\quad
r_p=\sqrt{x_p^2+y_p^2},\quad \theta_p=\operatorname{atan2}(y_p,x_p)
$$
输出 TrackingBeam{open_flag=1, type=1, Range=rp, Azi=θp(°), Elev=asin(h/rp)(°), TAS_track_index};队空、目标不在航迹表或数据率门控未通过时 open_flag=0。输出后更新该目标队列项的 last_track_time;原每次 Beam_Ctrl 末尾的整队循环移位(队列轮转)已停用,改为按数据率门控在队列内轮换目标。
> e0bf227 修正:数据率门控原先同时要求「距上次波束输出」与「航迹时间 T_track 距最新时间戳」都超过 DATA_RATE_TAS,第二个条件与「选 CPI 最早目标」的语义冲突(航迹时间越早反倒越容易被抑制),会漏掉本应输出的波束;现已只保留前者。
- tas_ctrl_process 入口强制 *Trust_track_num_Output=0 并在写入前检查容量,防止输出数组越界(BUG-09 修复)。
---
@@ -801,7 +806,7 @@ $$
| ALPHA_START | 60 | 同左 | 起批折角(°) |
| TRACK_DIE_ROUND | 4 | 4 | TWS 航迹消亡外推轮数 |
| TRACK_DIE_ROUND_TAS | 5 | 96 | TAS 航迹消亡外推轮数 |
| ASSO_THORD | 3 | 同左 | 关联波门 |
| ASSO_THORD | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4;e0bf227 起由 3 调大至 4) |
| H_F_WIN_LEN | 3 | 同左 | 高度平滑窗长基数(实际 +2/+3/+4/+6) |
| TAS_QUEUE_LENGTH / MAX_TAS_NUM | 4 / 4 | 1 / 1 | TAS 队列长度 / 最大 TAS 目标数 |
| FREQ0 | 9.2 GHz | 同左 | 载波频点(原 FREQ0~FREQ20 一组宏已精简为单个 FREQ0,e729c5b 起由 16.8 GHz 改为 9.2 GHz) |
@@ -837,6 +842,7 @@ $$
- **越界/防御**:TAS Point_Sum 限幅 150(BUG-04);size()-1 下溢(BUG-05);起批点数校验(BUG-06);航迹号下标校验(BUG-10);Beam_Ctrl 输出计数清零+容量检查(BUG-09);消亡空向量保护(BUG-34);航迹号 500 可复用(BUG-35);局部结构体值初始化(BUG-37)。
- **时间戳/时序**:TWS 关联 ΔT≤0 的候选不参与滤波更新、不刷新航迹状态并消费该点迹(604f158 起由「剔除候选、点迹保持未使用」改为当前行为,仍属 BUG-15 处置范围);TAS 关联 ΔT≤0 拒绝(BUG-16);TAS 外推负时间差跳过(BUG-17);Bind_speed 除零保护(BUG-19)。
- **数值稳定性**:方位残差 ±π 环绕(BUG-22);IMM 概率零分母/奇异保护(BUG-23);EKF 似然 3×3 新息协方差(BUG-24);航向角 atan2(BUG-26);asin 定义域钳位(BUG-27);近程模型 3 门限浮点除法(BUG-21);track_clear_all 全模块 reset(BUG-28);未初始化变量修复(BUG-11/12/14)。
- **后修订(e0bf227,BUG_FIX_REPORT.md 成文之后)**:TWS 关联高度门限由注释状态改为实际生效(`track_asso.cpp`),统计距离/高度/距离三类判据拆为独立布尔量,`ASSO_THORD` 由 3 调大至 4;TAS 关联高度门限 2000~3000 m 段放宽至 250 m;修复 `tas_beam_output()` 数据率门控误判(详见 CHANGELOG 的 Unreleased 与 9.6、9.10)。
### 11.2 有意保留 / 未修复(标注于 archived_docs/BUG_REPORT.md)
@@ -879,7 +885,8 @@ $$
| 提交 | 内容 |
|---|---|
| f249fdc | 输出航迹新增「靠近/远离」方向标志:`track_asso.cpp`(TWS)与 `track_asso_tas.cpp`(TAS)在写入 `vr_point` 后补 `Flag_Direction = vr_point > 0 ? 1 : 0`;新增 `docs/data_process_flow_report.md`、`docs/data_process_flow_report.html`、`docs/track_init_readme.md` 三份流程与专项文档 |
| e0bf227 | 关联判定重构并加强:TWS `track_asso.cpp` 新增按航迹距离分段的高度门限(原高度门限代码块为注释状态)、统计距离/高度/距离三类判据拆为 `thd_d`/`thd_h`/`thd_r` 布尔量;`parameters.h` 的 `ASSO_THORD` 由 3 调大至 4(等效门限 d < 4,提高机动目标关联概率);TAS `track_asso_tas.cpp` 高度门限 2000~3000 m 段由 200 m 放宽至 250 m;修复 `tas_beam_output()` 的数据率门控条件(去掉与「取最早 CPI 目标」冲突的 T_track 判据);删除 `docs/data_process_flow_report.html`;`.gitignore` 增加 `design_docs/`;同步更新 README/CHANGELOG/docs |
| f249fdc | 输出航迹新增「靠近/远离」方向标志:`track_asso.cpp`(TWS)与 `track_asso_tas.cpp`(TAS)在写入 `vr_point` 后补 `Flag_Direction = vr_point > 0 ? 1 : 0`;新增 `docs/data_process_flow_report.md`、`docs/data_process_flow_report.html`、`docs/track_init_readme.md` 三份流程与专项文档(其中 HTML 版已于 e0bf227 删除) |
| 7d5a56f | 满圈判据由「波位号变小」改为「方位波位号 == 0」(避免机扫转台反转误判,`data_process.cpp`);点-航迹头关联改为统一速度区间 `V_MIN·ΔT ≤ dis ≤ V_MAX·ΔT` 并新增 `dis ≥ |vr_point|·ΔT`、`dis ≥ |v_track_head|·ΔT` 两条多普勒-几何一致性约束,限制切向高速短航迹(`track_init.cpp`);同步更新 README/CHANGELOG |
| 604f158 | TWS 关联中 ΔT≤0(点迹时间戳不晚于航迹时间)的候选不再剔除:仍参与最近邻选取但不更新 IMM/EKF、不刷新航迹时间与新鲜度,并标记该点迹已使用(`Use_Flag=1`)消费掉,避免其继续参与航迹起始生成虚假 TWS 航迹;`track_asso.cpp` 的 ΔT≤0 告警日志注释停用;过期文档(BUG_REPORT.md、BUG_FIX_REPORT.md、requirements.md)移入 archived_docs/ 留档 |
| d6cdbc0 | 删除 `data_process.cpp`、`dot_coh.cpp` 中已注释停用的调试输出与早期 1 km SNR 过滤代码残留(不改变运行逻辑);同步更新 README/CHANGELOG |
@@ -912,8 +919,7 @@ $$
| 文档 | 内容 |
|---|---|
| CHANGELOG.md | 变更日志(Keep a Changelog 格式,按 git 提交记录维护) |
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示) |
| docs/data_process_flow_report.html | 上文同内容的 HTML 渲染版;⚠ 该文件的航迹起始步骤顺序仍是修正前的旧表述(未随 md 同步,本次未改动),以 md 与源码为准 |
| docs/data_process_flow_report.md | 数据处理流程与关键逻辑说明(按当前源码整理:前处理与判圈、凝聚、TWS/TAS 关联与滤波、航迹起始、外推消亡、TAS 波束控制、参数脉络;含与本文档的历史差异提示)——原同内容 HTML 渲染版(`data_process_flow_report.html`)已于 e0bf227 删除,HTML 版本在 git 忽略的 `design_docs/` 内留档 |
| docs/track_init_readme.md | 航迹起始(Track Init)专项说明:分阶段流程、起始条件汇总、调参直觉、输出字段映射(含未赋值字段)、已知风险与代码索引 |
| archived_docs/BUG_REPORT.md | 全量逻辑 BUG 分析(P0~P3 共 37 项,含位置、说明、建议、处置结论)——过期留档,仅作历史参考 |
| archived_docs/BUG_FIX_REPORT.md | 修复明细、修复状态汇总表与 MSVC 2013 x86 编译验证记录——过期留档(对应 01d28e0 时期) |
+27
View File
@@ -264,6 +264,33 @@ int Data_Process:: track_delete(int delete_track_num, //手动
return 0;
}
//手动添加重点目标
int Data_Process::primary_target_add(int track_index)
{
for ( size_t i=0; i<trust_track.size();i++)
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].primary_target_flag = 1;
}
return 0;
}
//手动取消重点目标
int Data_Process::primary_target_cancel(int track_index)
{
for ( size_t i=0; i<trust_track.size();i++){
if(trust_track[i].Track_Index==track_index)
{
trust_track[i].primary_target_flag = 0;
}
}
return 0;
}
//手动转TAS跟踪函数
int Data_Process:: tracking_start(int track_index) //需要手动转入TAS跟踪的航迹号
{
+6
View File
@@ -59,6 +59,12 @@ public:
//手动取消TAS跟踪函数
int tracking_stop(int track_index); //需要取消TAS跟踪的航迹号
//手动添加重点目标
int primary_target_add(int track_index); //将指定目标转为重点目标
//手动取消重点目标
int primary_target_cancel(int track_index); //将指定重点目标取消
//手动打跟踪波束
int tracking_point(float Azimuth); //方位角
@@ -236,6 +236,18 @@ public:
return 0;
}
//手动添加重点目标
virtual int primary_target_add(int track_index) //将指定目标转为重点目标
{
return 0;
}
//手动取消重点目标
virtual int primary_target_cancel(int track_index) //将指定重点目标取消
{
return 0;
}
//手动打跟踪波束
virtual int tracking_point(float Azimuth) //方位角(当前接口未启用,保留空实现)
{
+12 -4
View File
@@ -49,8 +49,12 @@
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
#define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 96 //
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
#define MAX_EXTRAPOLATE_NUM_TAS 48 //TAS最大外推点数
#define TRACK_DIE_ROUND 6 // 航迹消亡圈数
#define TRACK_DIE_ROUND_PRIMARY 10 // 重点航迹消亡圈数
#define TRACK_DIE_ROUND_TAS 96 //TAS航迹消亡圈数
#define ASSO_THORD 4
@@ -98,8 +102,12 @@
#define TRACK_START_THRESHOLD 8 //起航波门 3 越大越容易起批
#define ALPHA_START 60 //起航夹角 120 越大越容易起批
#define TRACK_DIE_ROUND 4 // 航迹消亡时间
#define TRACK_DIE_ROUND_TAS 5
#define MAX_EXTRAPOLATE_NUM 3 //最大外推点数
#define MAX_EXTRAPOLATE_NUM_TAS 5 //TAS最大外推点数
#define TRACK_DIE_ROUND 6 // 航迹消亡圈数
#define TRACK_DIE_ROUND_PRIMARY 10 // 重点航迹消亡圈数
#define TRACK_DIE_ROUND_TAS 10 //TAS航迹消亡圈数
#define ASSO_THORD 4
+2
View File
@@ -57,6 +57,8 @@ struct Trust_Track
int manual_tracking_flag; //手动TAS
int associate_point_number; //关联的点数
int primary_target_flag; //重点目标标记
int Target_Type; //目标类型 搜索目标 低空目标 地面目标
int Track_section_idx; //航迹区号
+60 -49
View File
@@ -67,7 +67,8 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
{ // 输出更新航迹条件:
// if((*trust_track)[i].manual_tracking_flag==0)
// {
if((*trust_track)[i].Track_Update_Flag==1){
(*trust_track)[i].Track_Update_Flag=0;
*Trust_track_num_Output= *Trust_track_num_Output+1;
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
@@ -129,7 +130,7 @@ int Track_Asso:: track_asso_process(std::vector<PointRecv> *point_
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim, (*trust_track)[i].speed_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].speed_dim));
std::memcpy(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim, (*trust_track)[i].range_dim, sizeof(Trust_Track_Output[*Trust_track_num_Output-1][0].range_dim));
// }
}
}
return 0;
@@ -651,6 +652,7 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
(*trust_track)[track_index-1].Extrapolate_round = 0; //连续未用实点更新时间
(*trust_track)[track_index-1].point_flag=1; //实点
(*trust_track)[track_index-1].associate_point_number = (*trust_track)[track_index-1].associate_point_number+1; //关联点数+1
(*trust_track)[track_index-1].Track_Update_Flag = 1; //航迹更新标志
}
//删除associated_info中关联上的航迹 点迹信息
@@ -685,57 +687,66 @@ void Track_Asso:: model_filter(std::vector <Trust_Track> *trust_track,struct Ra
//未关联上的航迹 进行外推 tws
for (size_t i =0; i<(*trust_track).size();i++ )
{
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0)
if((*trust_track)[i].point_flag == 0 && (*trust_track)[i].manual_tracking_flag == 0) //未关联上点迹的航迹进行外推
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
double delta_T;
if(Work_Parameter.work_mode == 0)
{
delta_T = Work_Parameter.DATA_RATE_SHORT;
}
else if(Work_Parameter.work_mode == 1)
{
delta_T = Work_Parameter.DATA_RATE_MIDDLE;
}
else if(Work_Parameter.work_mode == 2)
{
delta_T = Work_Parameter.DATA_RATE_FAR;
}
else
//外推点数未到上限,则继续外推,否则不外推
if((*trust_track)[i].Extrapolate_round <= MAX_EXTRAPOLATE_NUM)
{
double v_track = sqrt(pow((*trust_track)[i].X[1],2)+pow((*trust_track)[i].X[4],2));
double delta_T;
if(Work_Parameter.work_mode == 0)
{
delta_T = Work_Parameter.DATA_RATE_SHORT;
}
else if(Work_Parameter.work_mode == 1)
{
delta_T = Work_Parameter.DATA_RATE_MIDDLE;
}
else if(Work_Parameter.work_mode == 2)
{
delta_T = Work_Parameter.DATA_RATE_FAR;
}
else
{
delta_T = Work_Parameter.DATA_RATE_SHORT;
}
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[i].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[i].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[i].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[i].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[i].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[i].P3,6*6*sizeof(double));
//外推
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
}else{
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
}
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
//航迹X P
double X1[6],X2[6],X3[6],P1[6][6],P2[6][6],P3[6][6];
memcpy(X1,(*trust_track)[i].X1,6*sizeof(double));
memcpy(X2,(*trust_track)[i].X2,6*sizeof(double));
memcpy(X3,(*trust_track)[i].X3,6*sizeof(double));
memcpy(P1,(*trust_track)[i].P1,6*6*sizeof(double));
memcpy(P2,(*trust_track)[i].P2,6*6*sizeof(double));
memcpy(P3,(*trust_track)[i].P3,6*6*sizeof(double));
//外推
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1; //连续未用实点更新时间
(*trust_track)[i].point_flag=0; //虚点
}
}
+33 -23
View File
@@ -39,8 +39,9 @@ long long latest_timestamp //最新时间戳
//输出航迹
for (size_t i=0;i<trust_track->size();i++)
{ // 输出更新航迹条件:
if((*trust_track)[i].Track_Index == tas_track_idx)
if((*trust_track)[i].Track_Index == tas_track_idx && (*trust_track)[i].Track_Update_Flag == 1)
{
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
*Trust_track_num_Output= *Trust_track_num_Output+1;
Trust_Track_Output[*Trust_track_num_Output-1][0].Point_Sum=1;
@@ -444,6 +445,7 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
(*trust_track)[i].Amplitude= point_process[point_index-1].Amplitude; //幅度
(*trust_track)[i].associate_point_number = (*trust_track)[i].associate_point_number+1; //关联点数+1
(*trust_track)[i].RCS = point_process[point_index-1].RCS;
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
(*trust_track)[i].pitch_num = point_process[point_index-1].pitch_num;
@@ -459,33 +461,41 @@ void Track_Asso_Tas::model_filter( std::vector<Trust_Track> *t
else
{
//RDP_LOG << "unassociated point for track index " << tas_track_idx << std::endl;
for(size_t i=0;i<trust_track->size();i++)
for(size_t i=0;i<trust_track->size();i++){
if((*trust_track)[i].Track_Index == tas_track_idx)
{
double delta_T = (latest_timestamp - T_track)/1000.0;
if (delta_T <= 0)
continue;
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
if((*trust_track)[i].Extrapolate_round >= MAX_EXTRAPOLATE_NUM_TAS)
{
double delta_T = (latest_timestamp - T_track)/1000.0;
if (delta_T <= 0)
continue;
double F[6][6], Q1[6][6], Q2[6][6], Q3[6][6];
IMM_F_Q_gen( v_track, delta_T, F, Q1, Q2, Q3,Work_Parameter);
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
double X1_pred[6],X2_pred[6],X3_pred[6],P1_pred[6][6],P2_pred[6][6],P3_pred[6][6];
kalman Kalman;
Kalman.kalman_pred(F, Q1, X1, P1, X1_pred, P1_pred);
Kalman.kalman_pred(F, Q2, X2, P2, X2_pred, P2_pred);
Kalman.kalman_pred(F, Q3, X3, P3, X3_pred, P3_pred);
memcpy((*trust_track)[i].X1,X1_pred,6*sizeof(double));
memcpy((*trust_track)[i].X2,X2_pred,6*sizeof(double));
memcpy((*trust_track)[i].X3,X3_pred,6*sizeof(double));
memcpy((*trust_track)[i].P1,P1_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P2,P2_pred,6*6*sizeof(double));
memcpy((*trust_track)[i].P3,P3_pred,6*6*sizeof(double));
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].T_track = static_cast<long long>((*trust_track)[i].T_track+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].GNSS_time = static_cast<long long>((*trust_track)[i].GNSS_time+delta_T*1000.0); //更新航迹时间
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].Track_Update_Flag = 1; //航迹更新标志
}else{
(*trust_track)[i].point_flag=0; //虚点
(*trust_track)[i].Extrapolate_round =(*trust_track)[i].Extrapolate_round+1;
(*trust_track)[i].Track_Update_Flag = 0; //航迹更新标志
}
}
}
}
}
+17 -3
View File
@@ -14,10 +14,24 @@ void Track_Die::track_die_process( std::vector <Trust_Track> *trust_track,
std::vector <Trust_Track>::iterator Iter;
for (Iter=trust_track->begin(); Iter!=trust_track->end();)
{
if( ((*Iter).Track_Mode == 0 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND)
// ||((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND_TAS)
||(*Iter).manual_delete_flag == 1)
bool die_flag = false;
if((*Iter).manual_delete_flag == 1)
{
die_flag = true;
}
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 0 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND)
{
die_flag = true;
}
if((*Iter).Track_Mode == 0 && (*Iter).primary_target_flag == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_PRIMARY)
{
die_flag = true;
}
if(die_flag)
{
//输出消亡信息
*Track_die_num_Output=*Track_die_num_Output+1;
+12 -2
View File
@@ -17,9 +17,19 @@ void Track_Die_Tas::track_die_process_tas( std::vector <Trust_Track> *trust_trac
std::vector <Trust_Track>::iterator Iter;
for (Iter=trust_track->begin(); Iter!=trust_track->end();)
{
if( ((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round >= TRACK_DIE_ROUND_TAS)
||(*Iter).manual_delete_flag == 1)
bool die_flag = false;
if((*Iter).manual_delete_flag == 1)
{
die_flag = true;
}
if((*Iter).Track_Mode == 1 && (*Iter).Extrapolate_round > TRACK_DIE_ROUND_TAS)
{
die_flag = true;
}
if(die_flag)
{
// RDP_LOG << "remove tas target :" << (*Iter).Track_Index << std::endl;
//输出消亡信息
+33 -18
View File
@@ -165,7 +165,7 @@ flowchart TD
A["全部可靠航迹"] --> A1["IMM 交互(mix)"]
A1 --> B["滤波前先统一置 point_flag=0(本圈默认未关联实点)"]
B --> C["逐航迹 × 逐点:算 ΔT、三个模型马氏距离 d1/d2/d3"]
C --> D{"波门判定<br/>距离分段 + 距离门限 d_r + 俯仰门限 d_p"}
C --> D{"波门判定<br/>统计距离波门 thd_d + 高度门限 thd_h + 距离门限 thd_r"}
D -- 通过 --> E["记入候选表(含 d_min = min(d1,d2,d3))"]
D -- 不通过 --> F["丢弃该候选对"]
E --> G["最近邻:反复取全表 d_min 最小者"]
@@ -180,19 +180,31 @@ flowchart TD
M --> N["输出全部航迹(更新行 + 补点行)"]
```
### 4.2 波门(三步都通过才算候选)
### 4.2 波门(统计距离、高度、距离三类都通过才算候选)
**(1)统计距离波门**——按航迹距离分段收紧高机动模型:
**(1)统计距离波门 thd_d**——按航迹距离分段收紧高机动模型(`ASSO_THORD = 4`):
| 航迹距离 r_track | 模型 1 / 2 | 模型 3 |
|---|---|---|
| ≤ 500 m | d² < 9 | d² < 9/1000(≈ 关闭,抑制近程高机动模型的乱关联) |
| 500 ~ 1000 m | d² < 9 | d² < 9/100(收紧) |
| ≥ 1000 m | d² < 9 | d² < 9 |
| ≤ 500 m | d² < 16 | d² < 16/1000(≈ 关闭,抑制近程高机动模型的乱关联) |
| 500 ~ 1000 m | d² < 16 | d² < 16/100(收紧) |
| ≥ 1000 m | d² < 16 | d² < 16 |
> ⚠ **门限语义**:代码里 d 已经是马氏距离平方,判据又写作 `d*d < 9`,等效于 **d < 3**(约对应 3 自由度卡方分布 95% 分位的量级)。调整 ASSO_THORD 时务必记住这一层"二次方"。
> ⚠ **门限语义**:代码里 d 已经是马氏距离平方,判据又写作 `d*d < ASSO_THORD*ASSO_THORD`,`ASSO_THORD = 4` 时等效于 **d < 4**(而非 √d < 4)。调整 ASSO_THORD 时务必记住这一层"二次方"(该行为在 BUG-20 中被识别,有意保留)。
**(2)距离门限 d_r**——抑制远距离突跳。用航迹-点迹弦距 R 与航迹位移 VT = v_track·ΔT 判断:
**(2)高度门限 thd_h**(e0bf227 起在 TWS 侧生效)——|h_track − h_point| 按航迹距离分段:
| 航迹距离 r_track | 高度差门限 |
|---|---|
| ≤ 1000 m | 150 m |
| 1000 ~ 2000 m | 200 m |
| 2000 ~ 3000 m | 250 m |
| 3000 ~ 5000 m | 300 m |
| > 5000 m | 不约束 |
> 该门限此前在 `track_asso.cpp` 中整块被注释、`d_h` 恒为 1(等于不生效),现改为独立布尔量 `thd_h` 参与判定,取值与 TAS 通道一致,用于压制俯仰测量跳变引起的错误关联。
**(3)距离门限 thd_r**——抑制远距离突跳。用航迹-点迹弦距 R 与航迹位移 VT = v_track·ΔT 判断:
```
VT < 100 且 R > 300 → 拒绝(慢速目标不允许 300 m 以上的跳变)
@@ -200,7 +212,9 @@ VT < 100 且 R > 300 → 拒绝(慢速目标不允许 300 m 以上的跳
VT > 300 且 R > 1200 → 拒绝(高速目标给固定 1200 m 上限)
```
**(3)俯仰门限 d_p**:分距离段 7°/5°/10° 的逻辑当前被注释停用,**恒为通过**。
**(4)俯仰门限 d_p**:分距离段 7°/5°/10° 的逻辑仍被注释停用,不参与判定(其作用已由高度门限覆盖)。
> 实现要点:三类判据分别算成 `thd_d` / `thd_h` / `thd_r` 三个布尔量,最后以 `thd_d && thd_h && thd_r` 决定是否记入候选表(e0bf227 起;此前三者混写在一个长 `if` 条件里)。
### 4.3 最近邻(NN)选取规则
@@ -395,7 +409,7 @@ flowchart TD
|---|---|---|
| 凝聚 | 相邻两帧滑窗,近程多速度维约束 | 单帧,恒有速度维约束 |
| 处理对象 | 全部可靠航迹 × 全部点 | **仅指定批号**的这一条航迹 × 全部点 |
| 俯仰/高度门限 | 俯仰门限当前恒通过 | **高度门限生效**(按距离分段) |
| 俯仰/高度门限 | 俯仰门限已注释停用;**高度门限生效**(按距离分段,e0bf227 起) | **高度门限生效**(按距离分段,取值与 TWS 相同) |
| ΔT ≤ 0 | 参与选取但只消费点迹、不更新 | **直接跳过该点**(并记告警日志) |
| 外推时间 | 按 work_mode 数据率固定步长 | 按 `最新时间戳 − 航迹时间` |
| 输出 | 全部航迹 | 仅该批号航迹 |
@@ -409,7 +423,7 @@ flowchart TD
B -- 是 --> C["IMM 交互;航迹 point_flag 置 0"]
C --> D["逐点:ΔT ≤ 0 → 记告警并跳过"]
D --> E["逐点:三模型统计距离 d1/d2/d3 + 高度门限"]
E --> F{"d² < 9(任一模型)且高度门限通过?"}
E --> F{"d² < 16(任一模型)且高度门限通过?"}
F -- 是 --> G["记入候选表"]
F -- 否 --> H["丢弃"]
G --> I["取 d_min 最小的一点"]
@@ -426,7 +440,7 @@ flowchart TD
|---|---|
| ≤ 1000 m | 150 m |
| 1000 ~ 2000 m | 200 m |
| 2000 ~ 3000 m | 200 m |
| 2000 ~ 3000 m | 250 m |
| 3000 ~ 5000 m | 300 m |
| > 5000 m | 不约束 |
@@ -465,10 +479,10 @@ flowchart TD
```mermaid
flowchart TD
A["遍历 TAS 队列"] --> B["按批号在航迹表中找到该航迹"]
B --> C{"距上次波束输出 > DATA_RATE_TAS 秒<br/>且 最新时间戳 − 航迹时间 > DATA_RATE_TAS 秒?"}
B --> C{"距上次波束输出 > DATA_RATE_TAS 秒<br/>且 CPI 时间早于已选中的最早 CPI?"}
C -- 否 --> A
C -- 是 --> D["在合格目标中选 CPI 时间最早者"]
D --> E["位置外推:x + vx·ΔT, y + vy·ΔT"]
C -- 是 --> D["选中该目标(CPI 时间最早者)"]
D --> E["位置外推:ΔT = (最新时间戳 − CPI 时间)/1000<br/>x + vx·ΔT, y + vy·ΔT"]
E --> F["Range = √(xp²+yp²),Azi = atan2(yp,xp)<br/>Elev = asin(Height / Range)"]
F --> G["open_flag=1, type=1, 批号=真实航迹号<br/>更新该目标 last_track_time = 最新时间戳"]
C -.->|"无任何合格目标"| H["open_flag = 0(其余字段保持旧值)"]
@@ -476,8 +490,9 @@ flowchart TD
**要点**
- 门控用**两个条件**:距上次输出的时间、航迹时间与最新时间戳之差都要大于 `DATA_RATE_TAS` 秒(单位是秒,由宿主经参数初始化传入,须为 > 0 的有效值)→ 保证 TAS 目标的数据率**不随 Beam_Ctrl 的调用频率变化**;
- 多目标时选**队列中 CPI 时间最早**的目标,等价于"按最久未更新者优先"的轮转;
- 门控只用一个条件(e0bf227 修正):目标「距上次波束输出的时间」(`latest_timestamp − last_track_time`)要大于 `DATA_RATE_TAS` 秒(单位是秒,由宿主经参数初始化传入,须为 > 0 的有效值)→ 保证 TAS 目标的数据率**不随 Beam_Ctrl 的调用频率变化**;修正前还要求「航迹时间与最新时间戳之差」也大于该门限,与"取最早 CPI 目标"的语义冲突,会漏掉本应输出的波束;
- 多目标时选**队列中 CPI 时间最早**的目标(同时要求其 CPI 时间早于当前已选者),等价于"按最久未更新者优先"的轮转;
- 选中后才按该目标的 CPI 时间算外推步长 `ΔT = (最新时间戳 − CPI 时间)/1000`;
- 原来的"每次调用整队循环移位"已停用;
- 俯仰用航迹高度直接算(叠加雷达平台高度的处理只影响对外输出的 z,不参与波束俯仰)。
@@ -494,7 +509,7 @@ flowchart TD
| `R_MIN / R_MAX` | 100 / 100000 | 同左 | 有效量程(凝聚后过滤) |
| `TRACK_START_THRESHOLD` | 8 | 同左 | 起始统计距离波门(d ≤ 8) |
| `ALPHA_START` | 60 | 同左 | 起始折角上限(°) |
| `ASSO_THORD` | 3 | 同左 | 关联波门(d² < 9,等效 d < 3) |
| `ASSO_THORD` | 4 | 同左 | 关联波门(判据 d*d < ASSO_THORD²,d 已是马氏距离平方,等效 d < 4) |
| `TRACK_DIE_ROUND` | 4 | 4 | TWS 消亡外推轮数 |
| `TRACK_DIE_ROUND_TAS` | 5 | 96 | TAS 消亡外推轮数 |
| `MAX_TRACK_NUM / MAX_TRACK_INDEX` | 500 / 500 | 同左 | 航迹数上限 / 航迹号上限 |