创建仓库

This commit is contained in:
2025-12-11 07:24:36 +08:00
commit 0d81c1792d
128 changed files with 15104 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
---
tags:
aliases:
- 2.6 时序同步与数据一致性 (Timing Synchronization & Data Coherence)
date created: 星期一, 十一月 24日 2025, 11:50:10 晚上
date modified: 星期一, 十一月 24日 2025, 11:50:24 晚上
---
# 2.6 时序同步与数据一致性 (Timing Synchronization & Data Coherence)
- **覆盖范围**:定义系统的时间基准获取方式、数据流打点策略以及跨模块处理时的时间对齐逻辑。涵盖从硬件层面的 PTP/GPS 同步,到软件层面的 CPI相干处理间隔对齐以及航迹预测中的时间外推算法确保系统在微秒级精度下的时空一致性。
- **2.6.1 高精度统一时钟源架构 (High-Precision Unified Clock Architecture)**
- **核心指向**:定义系统时间的唯一真值来源。优先采用 **PTP (IEEE 1588v2)** 协议通过网口同步至 GPS/北斗授时服务器,实现亚微秒级的时间同步精度。涵盖在 PTP 不可用时的 **NTP 回退策略**,以及利用 CPU **TSC (Time Stamp Counter)** 寄存器作为高频计时源的校准逻辑防止系统时间跳变Time Jump导致的逻辑错误。
- **2.6.2 多级数据打点策略 (Multi-Level Timestamping Strategy)**
- **核心指向**:定义数据包时间戳的生成位置与精度分级。首选网卡硬件 **TSU (Timestamp Unit)** 生成的入站时间戳Ingress Timestamp次选内核网络栈的 `SO_TIMESTAMP` 软件时间戳。在 `DataReceiver` 封装 `RawDataPacket` 时,强制将此硬件/内核时间戳固化为数据的 **“诞生时间” (Generation Time)**,并在后续全链路中保持不变。
- **2.6.3 相干处理间隔对齐机制 (CPI Alignment Mechanism)**
- **核心指向**:针对信号处理模块的特殊时序要求。定义如何根据雷达 **PRF (脉冲重复频率)****波位编码**,将连续到达的 UDP 数据包在内存池中重组为严格对齐的 **CPI 数据块**。涵盖处理网络抖动导致的脉冲到达时间波动Jitter的缓冲策略确保 FFT 和多普勒处理时的数据在时间域上严格相干。
- **2.6.4 航迹外推与异步测量融合 (Track Extrapolation & Asynchronous Measurement Fusion)**
- **核心指向**针对数据处理模块的时空一致性逻辑。定义在进行数据关联Data Association如何将上一时刻$t_{k-1}$)的航迹状态,基于运动模型精确外推至当前测量时刻($t_k$。涵盖处理乱序到达Out-of-Order量测数据的**延迟关联**或**丢弃策略**,确保卡尔曼滤波的更新步基于单调递增的时间轴。
- **2.6.5 全链路延迟审计与抖动监控 (End-to-End Latency Auditing & Jitter Monitoring)**
- **核心指向**:定义系统实时性的度量标准。利用 `DataPacket` 头部携带的诞生时间戳,在流水线的每个关键节点(接收、信号处理完成、航迹更新完成、网关发送)计算 **驻留时间 (Residence Time)**。监控模块需实时统计各阶段的延迟分布,一旦发现处理抖动超过 CPI 周期的一定比例(如 10%),立即触发性能告警或热节流保护。

View File

@@ -0,0 +1,114 @@
---
tags: []
date created: 星期三, 十一月 26日 2025, 9:41:51 晚上
date modified: 星期三, 十一月 26日 2025, 9:54:07 晚上
---
# 2.6.1 高精度统一时钟源架构 (High-Precision Unified Clock Architecture)
**基线核心宗旨****“Hardware PTP as Truth, Software TSC for Speed (硬件 PTP 为真值,软件 TSC 为速度)”**。
我们构建一个分层的时间架构:底层依赖 **IEEE 1588v2 (PTP)** 锁定物理时钟,应用层利用 **CPU TSC** 实现零系统调用的纳秒级打点,并通过**动态校准回路**将两者对齐。
-----
## 1. 约束输入与对齐 (Constraints & Alignment)
基于分布式雷达组网TDP-2.4-DIST和实时性要求我们需对齐以下硬性约束
1. **同步精度 (P0)**:多站协同要求时间同步误差 **\< 1µs**。传统的 NTP毫秒级无法满足必须使用硬件辅助的 PTP。
2. **硬件依赖 (Hardware)**
- **网卡**:必须支持 **Hardware Timestamping** (IEEE 1588 PHY)。(需确认您的网迅网卡或采集卡是否支持,若不支持需回退到软件 PTP精度降至 10-100µs)。
- **交换机**:局域网交换机应支持 **PTP Transparent Clock (TC)****Boundary Clock (BC)** 模式,以消除排队抖动。
3. **单调性 (Monotonicity)**:系统内部逻辑(如卡尔曼滤波 `dt` 计算严禁出现“时光倒流”。即使外部时钟源发生跳变Step内部时钟也必须平滑过渡Slew
-----
## 2. 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1同步协议栈选型
| 选项 | A. NTP (Network Time Protocol) | B. Software PTP | C. Hardware PTP **(推荐)** |
| :--- | :--- | :--- | :--- |
| **机制** | 纯软件协议,通过统计学算法消除抖动。 | 使用 PTP 协议,但打点在驱动层/协议栈层。 | **PHY 芯片打点**。完全消除 OS 调度和协议栈延迟。 |
| **精度** | 1ms - 10ms。 | 10µs - 100µs。 | **\< 1µs (亚微秒级)**。 |
| **适用性** | 日志记录、非实时业务。 | 低成本组网。 | **相控阵雷达、高频交易、工业自动化**。 |
### 议题 2应用层计时源 (Application Timing Source)
| 选项 | A. `clock_gettime` (vDSO) | B. `rdtsc` 指令 (TSC) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | Linux 标准 API读取内核维护的时间结构体。 | 直接读取 CPU 内部的 Time Stamp Counter 寄存器。 |
| **开销** | **低 (\~20-50ns)**。vDSO 避免了陷入内核,但仍有内存访问开销。 | **极低 (\~5-10ns)**。纯寄存器操作,流水线几乎无停顿。 |
| **缺陷** | 仍有微小开销,且受制于内核的时钟更新策略。 | **原生不支持绝对时间**。TSC 只是一个开机后的滴答计数,且不同 CPU 核之间可能微弱不同步(现代 CPU 已通过 `constant_tsc` 解决)。 |
| **结论** | 通用场景首选。 | **高频热路径(如每秒百万次打点)首选**。需配合软件校准。 |
-----
## 3. 基线确立与实施规范
为了达成亚微秒级同步并支持高频打点,我们确立 **Hardware PTP + TSC 软时钟** 为基线。
### 3.1. 物理层同步架构 (PTP Topology)
- **Grandmaster (GM)**:总控服务器或专用的 GPS/北斗授时仪,作为 PTP 主时钟。
- **Slave Nodes**雷达处理服务器DataReceiver 所在节点)。
- **软件栈**:使用 Linux 标准工具集 **`linuxptp`**。
- **`ptp4l`**:负责将 **网卡 PHC (PTP Hardware Clock)** 同步到 GM。
- **`phc2sys`**:负责将 **系统时钟 (System Clock / CLOCK\_REALTIME)** 同步到 网卡 PHC。
**运维配置基线 (`/etc/linuxptp/ptp4l.conf`)**:
```txt
[global]
time_stamping hardware
delay_mechanism E2E
network_transport UDPv4
# 关键:在锁定前允许跳变,锁定后仅微调
step_threshold 1.0
first_step_threshold 0.00002
```
## 2. 应用层软时钟设计 (TSC-based Soft Clock)
在 C++ 应用层,我们封装一个 `HighPrecisionClock` 类,利用 TSC 提供极速时间戳,同时后台线程负责将其“锚定”到 PTP 时间上。
- **核心原理**
$$
T_{current} = T_{base} + \frac{(TSC_{current} - TSC_{base})}{Frequency}$$
* $T_{base}$ 和 $TSC_{base}$ 是最近一次校准时的基准对。
* $Frequency$ 是 CPU 主频(需通过校准测得精确值,而非标称值)。
* **校准线程 (Calibration Thread)**
- **频率**:每 1 秒运行一次。
- **逻辑**
1. 原子操作同时读取当前 `clock_gettime(REALTIME)` ($T_{new}$) 和 `rdtsc` ($TSC_{new}$)。
2. 更新全局原子变量 `BasePair` = {$T_{new}$, $TSC_{new}$}。
3. **平滑策略**:如果发现 PTP 时间发生了跳变Step不要立即更新 $T_{base}$ 导致时间倒流,而是调整 $Frequency$ 让时间“快跑”或“慢跑”以追赶Slew保证单调性。
### 3. 异常处理与回退 (Fallback)
- **PTP 失锁检测**:监控模块需监听 `ptp4l``rms` (Root Mean Square) 偏差值。
-`rms > 10µs` 持续 5 秒,标记 **"Time Sync Degraded"**。
- **NTP 回退**:如果 PTP 完全不可用(网卡不支持或链路故障),自动回退到 NTP。
- **实现**`chronyd` 配置 `noselect`,仅当 PTP 服务停止时接管。
- **标记**:此时数据包中的 `timestamp_us` 精度下降,`TrackDataBatch` 的状态字段应置位 `TIME_PRECISION_LOW`
-----
## 总结2.6.1 基线图谱
| 组件 | 核心基线 | 关键技术点 |
| :--- | :--- | :--- |
| **时间源 (Truth)** | **GPS/北斗 -\> PTP GM** | 绝对时间真值。 |
| **同步协议** | **IEEE 1588v2 (Hardware)** | `ptp4l` + `phc2sys`,亚微秒精度。 |
| **应用层 API** | **TSC Soft Clock** | `rdtsc` + 动态校准,纳秒级开销。 |
| **单调性保障** | **软件平滑 (Slewing)** | 禁止时间倒流,通过频率微调消除偏差。 |
**提问**:您是否确认 **“硬件 PTP 同步 + TSC 软时钟封装”** 的架构基线?如果确认,我们将进入 **2.6.2 多级数据打点策略**,定义这个高精度时间戳具体“打”在哪里。

View File

@@ -0,0 +1,125 @@
---
tags: []
date created: 星期三, 十一月 26日 2025, 9:49:46 晚上
date modified: 星期三, 十一月 26日 2025, 10:06:18 晚上
---
# 2.6.2 多级数据打点策略 (Multi-Level Timestamping Strategy)
**基线核心宗旨****“As Early As Possible (越早越好)”**。
时间戳必须尽可能在物理层PHY或链路层MAC生成以消除操作系统中断调度、驱动处理和协议栈拷贝带来的**不定长抖动Jitter**。
-----
## 1. 约束输入与对齐 (Constraints & Alignment)
基于 2.1.1 审计结果(网迅 WX1860AL4 网卡)和 2.6.1 确立的时钟基线,我们需要对齐以下约束:
1. **硬件能力 (Hardware Cap)**
- **网迅 NIC**:需确认驱动是否支持 `SO_TIMESTAMPING` 接口读取硬件 RX 时间戳。通常企业级网卡Intel X710/Mellanox均支持国产网卡需实测验证。若不支持需回退到内核软打点。
2. **协议支持 (Protocol)**
- UDP 数据包本身不携带“发送时间”(除非应用层协议写了)。因此我们依赖的是**接收端打点 (Ingress Timestamping)**。
3. **数据结构关联**
- 生成的纳秒级时间戳必须填入 `RawDataPacket` 的 Header并最终映射到 Protobuf 的 `timestamp_us`
-----
## 2. 权衡分析与选项呈现 (Trade-off Matrix)
我们按照“离物理线路的距离”定义三个打点层级:
| 选项 | A. 用户态打点 (User-space) | B. 内核软打点 (Kernel SW) **(基线)** | C. 硬件硬打点 (Hardware HW) **(理想)** |
| :--- | :--- | :--- | :--- |
| **生成位置** | `recvmsg()` 返回后的应用层代码。 | 网卡驱动收到中断SKB (Socket Buffer) 创建时刻。 | 网卡 PHY 芯片接收到前导码Preamble时刻。 |
| **抖动来源** | **极大**。受 CPU 调度、软中断处理、内存拷贝影响,抖动可达 10-100µs。 | **中等**。受中断响应延迟影响,抖动约 1-10µs。 | **极小**。几乎无抖动 (\< 100ns)。 |
| **PTP 依赖** | 依赖系统时钟 (`CLOCK_REALTIME`)。 | 依赖系统时钟。 | 依赖网卡 PHC (PTP Hardware Clock) 与系统时钟的同步 (`phc2sys`)。 |
| **实现复杂度**| 低。调用 `Clock::now()` 即可。 | 中。需配置 Socket 选项并解析辅助数据 (`CMSG`)。 | 高。需硬件支持,且需处理 PHC 到 UTC 的转换。 |
| **适用场景** | 功能调试、非实时业务。 | **当前国产环境最稳妥的基线**。 | 相控阵雷达、高频交易。 |
-----
## 3. 基线确立与实施规范
考虑到国产网卡驱动的成熟度风险,我们确立 **“优先硬件,兜底内核,严禁用户态”** 的分级策略。
### 3.1 策略分级定义 (Hierarchy Definition)
- **Priority 1 (HW)**: 尝试启用 **`SO_TIMESTAMPING_RX_HARDWARE`**。
- 如果网卡支持,这是绝对真值。
- *注意*:硬件时间戳通常是 PHC 时间TAI需在用户态根据 `ptp4l` 的 offset 转换为 UTC。
- **Priority 2 (SW)**: 回退到 **`SO_TIMESTAMPNS`** (内核接收时间)。
- 这是**工程基线**。它反映了数据包进入 Linux 网络栈的第一时间点,消除了应用程序调度的延迟。
- **Priority 3 (App)**: **严禁作为生产标准**。仅在上述两者皆失败时,使用 2.6.1 定义的 `HighPrecisionClock::now()` 补救,并标记数据质量为 `LOW_PRECISION`
### 3.2 实现规范:辅助数据解析 (CMSG Parsing)
`DataReceiver` 的 I/O 线程中,必须改造 `recvmmsg` 的调用方式,以提取内核附带的时间戳元数据。
- **Socket 配置**
```cpp
int flags = SO_TIMESTAMPNS; // 请求内核软件时间戳 (纳秒级)
// 如果确认网卡支持硬件打点,则改为:
// int flags = SOF_TIMESTAMPING_RX_HARDWARE | SOF_TIMESTAMPING_RAW_HARDWARE;
setsockopt(sockfd, SOL_SOCKET, SO_TIMESTAMPING, &flags, sizeof(flags));
```
- **接收逻辑 (核心代码范式)**
```cpp
void UdpReceiver::receiveLoop() {
struct mmsghdr msgs[BATCH_SIZE];
struct iovec iovecs[BATCH_SIZE];
char cmsg_buf[BATCH_SIZE][256]; // 存放辅助数据的缓冲区
// … 初始化 msgs, iovecs, cmsg …
int n = recvmmsg(sockfd, msgs, BATCH_SIZE, 0, nullptr);
for (int i = 0; i < n; ++i) {
uint64_t timestamp_ns = 0;
// 解析辅助数据 (Control Message)
struct cmsghdr *cmsg;
for (cmsg = CMSG_FIRSTHDR(&msgs[i].msg_hdr); cmsg; cmsg = CMSG_NXTHDR(&msgs[i].msg_hdr, cmsg)) {
// 优先提取硬件时间戳,若无则提取软件时间戳
if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_TIMESTAMPNS) {
struct timespec *ts = (struct timespec *)CMSG_DATA(cmsg);
timestamp_ns = ts->tv_sec * 1000000000ULL + ts->tv_nsec;
break;
}
}
// 兜底:如果内核没给时间戳,立刻用 TSC 软时钟打点
if (timestamp_ns == 0) {
timestamp_ns = HighPrecisionClock::now();
}
// 固化:写入 RawDataPacket Header
// 这一刻起,这个时间戳就是数据的"法定出生时间"
auto packet = MakePacket(std::move(payload), seq_id);
packet.header.timestamp_us = timestamp_ns / 1000;
// … 推送队列 …
}
}
```
### 3.3 全链路时间戳传递 (Propagation)
这个“出生时间戳” (`timestamp_us`) 必须在系统中神圣不可侵犯:
- **接收端 (`DataReceiver`)**:生成并写入 `RawDataPacket.header`。
- **处理端 (`SignalProcessor`)**:继承该时间戳。虽然 FFT 处理是在 5ms 后进行的,但数据的物理意义依然是“那个时刻的回波”。
- **输出端 (`DisplayController`)**:将该时间戳写入 Protobuf 的 `timestamp_us` 字段发送给显控。
- *注意*:显控端看到的延迟 = `Now() - timestamp_us`。这个延迟包含了:`内核排队 + 信号处理耗时 + 航迹关联耗时 + 序列化耗时 + 网络传输耗时`。这正是我们想要的**真实物理延迟**。
-----
## 总结2.6.2 基线图谱
| 维度 | 核心基线 | 技术细节 |
| :--- | :--- | :--- |
| **打点源** | **内核软打点 (`SO_TIMESTAMPNS`)** | 兼顾精度与兼容性,消除用户态抖动。 |
| **理想源** | **硬件打点 (HW RX)** | 若网卡支持,优先升级至此 (需实测)。 |
| **获取方式** | **`recvmmsg` + `CMSG`** | 从 Socket 辅助数据中提取,而非调用 `time()`。 |
| **语义** | **生成时间 (Generation Time)** | 代表信号到达系统的物理时刻,全链路透传,禁止修改。 |

View File

@@ -0,0 +1,146 @@
---
tags: []
date created: 星期三, 十一月 26日 2025, 10:25:55 晚上
date modified: 星期三, 十一月 26日 2025, 10:28:06 晚上
---
# 2.6.3 相干处理间隔对齐机制 (CPI Alignment Mechanism)
-----
## TL;DR
本节确立了 **“原地乱序重组 (In-Place Scatter Assembly)”** 的基线策略。
利用 **脉冲索引 (Pulse Index)** 直接计算内存偏移量将网络包数据直接写入预分配的页锁定内存Pinned Memory实现**零拷贝组装**。配合 **“空间/时间双重触发”** 的提交机制和 **“有限零填充”** 的容错策略,在抗网络抖动与保障实时性之间取得平衡。
-----
## 一、 约束输入与对齐 (Constraints & Alignment)
基于前序章节2.1, 2.2, 2.6.2),我们面临以下硬性约束:
1. **数据形态**
- **输入**UDP JUMBO Frame (9KB)。每个包可能包含 1 个或多个脉冲的回波数据,带有 `BatchID` (CPI ID) 和 `PulseIndex`
- **输出**:符合 **2.2.6** 定义的显存布局 `[Channel][Pulse][Range]` (Padding 对齐)。
2. **时序特性**
- 网络抖动Jitter客观存在数据包到达顺序不一定严格均等。
- 算法要求多普勒处理FFT要求脉冲间隔PRI是均匀的。如果数据包晚到我们不能“等”太久否则会阻塞流水线如果包丢了矩阵就“穿孔”了。
3. **内存模型**:必须在 **2.2.1** 确立的 `MemoryPool`(页锁定内存)中直接操作,避免额外的 `memcpy`
-----
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1组装策略 (Assembly Strategy)
| 选项 | A. 顺序追加 + 排序 (Append & Sort) | B. 原地乱序重组 (In-Place Scatter) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | 收到一个包就 push\_back 到 buffer凑够数量后按 `PulseIndex` 排序整理内存。 | 根据包头的 `PulseIndex` 直接计算目标地址偏移 `offset`,写入最终位置。 |
| **内存拷贝** | **2 次** (接收 -\>临时 Buf-\>排序后 Buf)。 | **1 次** (接收 -\>最终 Buf)。 |
| **乱序容忍** | 好,但整理开销大。 | **极佳**。先到的包占坑,后到的包填坑,无视顺序。 |
| **CPU 开销** | 高 (排序算法)。 | **极低** (O(1) 地址计算)。 |
### 议题 2CPI 提交触发机制 (Commit Trigger)
| 选项 | A. 严格计数触发 (Strict Count) | B. 严格时间触发 (Strict Timer) | C. 混合双触发 (Hybrid) **(推荐)** |
| :--- | :--- | :--- | :--- |
| **机制** | 必须收齐 N 个脉冲才提交。少一个就死等。 | 无论收多少,每隔 T 毫秒强制提交一次。 | **收齐 N 个** OR **首包到达后超时 T\_max**,满足其一即提交。 |
| **实时性** | **差**。一个包丢了会导致该 CPI 永远卡死,甚至阻塞后续 CPI。 | **中**。可能切断正在传输的完整 CPI。 | **高**。既保证了完整性,又防止了死锁。 |
| **丢包处理** | 无法处理。 | 截断。 | **自适应**。超时未齐则标记为“残缺 CPI”。 |
### 议题 3残缺数据处理 (Incomplete Data Policy)
当触发超时提交时CPI 矩阵中会有“空洞”(丢包导致的缺失脉冲)。
| 选项 | A. 整块丢弃 (Drop All) | B. 零填充 (Zero Padding) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | 只要不完整,整个 CPI 扔掉。 | 缺失的脉冲位置填 0强行送入 GPU。 |
| **算法影响** | **断续**。航迹会中断,但没噪音。 | **信噪比下降**。FFT 旁瓣升高,可能产生假目标。 |
| **工程价值** | 简单,适合严苛场景。 | **鲁棒**。偶尔丢 1-2 个包不影响大局,通过 CFAR 阈值抑制旁瓣。 |
-----
## 三、 基线确立与实施规范
为了在 1GbE 的物理限制下榨干性能,我们确立 **B. 原地乱序重组** + **C. 混合双触发** 为基线。
### 1\. 数据结构:智能 CPI 容器 (`AssemblyBuffer`)
我们在 `DataReceiver` 中维护一个“正在组装”的 CPI 池。
```cpp
struct AssemblyBuffer {
// 基础元数据
uint64_t batch_id = 0;
uint64_t first_packet_time_us = 0; // 首包到达时间 (用于超时判定)
uint32_t packets_received = 0; // 已收包计数
uint32_t total_packets_expected = 0; // 预期总包数 (由波位参数决定)
// 内存指针 (指向 MemoryPool 中的一块 Pinned Memory)
// 物理布局严格遵循 [Channel][Pulse][Range]
Complex* data_ptr = nullptr;
// 位图 (Bitmap):用于快速标记哪些 Pulse 已经收到了
// 相比 vector<bool> 更快,且方便检查完整性
std::bitset<MAX_PULSES_PER_CPI> reception_mask;
// 状态
enum State { IDLE, ASSEMBLING, READY, DAMAGED };
State state = IDLE;
};
```
### 2\. 原地重组逻辑 (In-Place Assembly Logic)
这是 **I/O 线程** 的核心循环逻辑:
1. **接收**`recvmmsg` 收到一个 UDP 包。
2. **解析**:读取包头,获取 `BatchID` (CPI ID) 和 `PulseIndex`
3. **寻址**
-`BatchID` 是新的,从 `MemoryPool` 申请一块新 `AssemblyBuffer`
-`BatchID` 已存在,命中缓存。
4. **计算偏移 (关键)**
- 利用 **2.2.6** 确定的 Pitch (行对齐步长)。
- `TargetAddr = BufferBase + (PulseIndex * Pitch_Bytes)`
5. **写入**:将 UDP Payload 直接 `memcpy``TargetAddr`
- *注*:这是数据进入系统的**唯一一次拷贝**。
6. **标记**`reception_mask.set(PulseIndex)``packets_received++`
### 3\. 提交与超时策略 (Commit & Timeout)
我们需要一个**低频**(如 1kHz的检查器可以在 I/O 线程的空闲间隙运行,或由定时器触发):
- **完整性检查**`if (packets_received == total_packets_expected)` -\> **立即提交** (Push to GPU Queue)。
- **超时检查**`if (Now() - first_packet_time_us > MAX_JITTER_WINDOW_US)` -\> **强制提交**
- *基线值*`MAX_JITTER_WINDOW_US` 建议设为 **CPI 时长的 10%**(例如 CPI 为 5ms则抖动容忍窗为 500us
### 4\. 容错与填充规范
**强制提交** 发生时(即丢包):
- **丢包率判定**
-`LossRate < 5%` (如 128 个脉冲丢了 \< 6 个):执行 **零填充 (Zero Padding)**。将 `reception_mask` 中为 0 的位置对应的内存置零(`memset`)。标记数据质量为 `DEGRADED`
-`LossRate >= 5%`**整块丢弃**。FFT 在缺失大量数据时结果不可信。记录 `WARNING` 日志。
-----
## 四、 自我反驳与边界修正 (Self-Rebuttal)
- **反驳 1**:原地乱序重组需要预知 `PulseIndex`,如果 UDP 包头里没有这个字段怎么办?
- *修正*协议层2.1.2)必须强制要求 FPGA/DPU 打包时带上 `PulseIndex`。如果前端不可控,则必须在 `DataReceiver` 维护一个软件计数器,但这会失去抗乱序能力,属于**降级方案**。
- **反驳 2**`memset` 清零操作在大内存(如 64MB下也是耗时的。
- *修正*:我们使用了 `MemoryPool`。在归还 Block 时,是否需要清零?不,我们在**申请 Block 时不清零**,而是依赖**全覆盖写入**。因此,对于没收到的脉冲,**必须显式清零**,否则会残留上一个 CPI 的脏数据(鬼影目标)。
- *优化*:只对 `reception_mask` 为 0 的那些行Pitch`memset`,而不是整个 Buffer。
-----
## 总结2.6.3 基线图谱
| 维度 | 核心基线 | 关键技术点 |
| :--- | :--- | :--- |
| **组装模式** | **In-Place Scatter (原地乱序)** | `Addr = Base + PulseIdx * Pitch`O(1) 复杂度。 |
| **内存操作** | **Single Copy (单次拷贝)** | 从 UDP 接收缓冲区直接到 Pinned Memory。 |
| **提交触发** | **Hybrid (满额即发 / 超时强发)** | 抖动窗口建议为 CPI 时长的 10%。 |
| **容错策略** | **Conditional Padding (条件填充)** | 丢包 \< 5% 补零;\> 5% 丢弃。需显式清除脏数据。 |
-----

View File

@@ -0,0 +1,164 @@
---
tags: []
date created: 星期三, 十一月 26日 2025, 10:30:58 晚上
date modified: 星期三, 十一月 26日 2025, 10:48:13 晚上
---
# 2.6.4 航迹外推与异步测量融合 (Track Extrapolation & Asynchronous Measurement Fusion)
-----
这是数据处理模块DataProcessor的**时空校准器**。
在 2.6.2 中,我们给每个 CPI 数据打上了高精度的“出生时间戳” ($t_{meas}$)。
在 2.6.3 中,我们完成了数据的物理拼装。
现在,算法核心面临的问题是:**当前的航迹停留在 $t_{track}$ 时刻,而新来的量测数据产生于 $t_{meas}$ 时刻。两者在时间上不对齐,如何在空间上进行关联?**
对于相控阵雷达波束调度是灵活的数据到达是异步的传统的“按扫描周期Scan-based”更新逻辑已经失效必须转向 **“按量测驱动Measurement-Driven”** 的异步滤波机制。
-----
## 一、 约束输入与对齐 (Constraints & Alignment)
基于前序基线,我们面临以下硬性约束:
1. **时间真值**:所有计算必须基于 **2.6.2** 确立的 `timestamp_us`(物理生成时间)。严禁使用 `Now()`(处理时间)进行滤波,否则会将“处理延迟”和“排队延迟”错误地耦合进运动模型,导致速度估计偏差。
2. **数据单调性**:物理世界的时间是单调递增的。但网络传输可能导致 UDP 包乱序,使得 $t_{meas}$ 偶尔出现回退。
3. **计算模型**:内部对象遵循 **2.5.1** 定义的 `TrackData` (POD, `float state[8]`)。
-----
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1时间对齐策略 (Alignment Strategy)
| 选项 | A. 统一同步到当前时刻 (Sync to Now) | B. 航迹外推到量测时刻 (Extrapolate to Meas) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | 将所有航迹和新量测都外推到 `Now()` 或固定的 `T_tick`,在同一时间切片上做关联。 | 保持量测不动(因为它是真值),将航迹状态外推 $\Delta t = t_{meas} - t_{track}$,在 $t_{meas}$ 处做关联。 |
| **精度** | **中**。引入了“处理延迟”的外推误差。 | **高**。利用了最原始的测量时间,物理意义最严谨。 |
| **适用性** | 机械扫描雷达(整圈更新)。 | **相控阵雷达/异步多传感器融合**。 |
| **副作用** | 航迹时间戳随系统时钟更新。 | 航迹时间戳随量测时间更新。 |
### 议题 2乱序量测处理 (OOSM - Out of Sequence Measurement)
当 $t_{meas} < t_{track}$ 即新收到的数据比航迹当前状态还老
| 选项 | A. 状态回溯滤波 (Retrodiction) | B. 缓冲重排 (Buffering) | C. 直接丢弃 (Drop) **(推荐)** |
| :--- | :--- | :--- | :--- |
| **机制** | 保存历史状态快照回滚到 $t_{meas}$ 更新后再推回来 | 在接收端设置缓冲窗 50ms排序后再送入算法 | **拒收**来自过去的数据 |
| **复杂度** | **极高**内存和计算开销翻倍 | **中**增加系统整体延迟 | **极低** |
| **适用性** | 数据极稀疏的场景如空管雷达)。 | 对延迟不敏感的离线系统 | **高更新率雷达**丢掉一帧数据对跟踪影响微乎其微 |
-----
## 三、 基线确立与实施规范
为了适配相控阵体制并保证微秒级一致性我们确立 **B. 航迹外推到量测时刻** + **C. 直接丢弃乱序** 为基线
### 1\. 核心算法:异步外推更新 (Asynchronous Update)
在关联阶段Association针对每一个待匹配的航迹 $Tr_i$执行以下逻辑
1. **时间差计算 (Time Difference)**
计算量测时间 ($t_{meas}$) 与当前航迹时间 ($t_{track}$) 的偏差转换为秒
$$
\Delta t = (t_{meas} - t_{track}) \times 10^{-6}
$$
- $t_{meas}$: 新到达的量测数据时间戳 (单位: 微秒)
- $t_{track}$: 航迹当前状态的时间戳 (单位: 微秒)
2. **状态外推方程 (State Extrapolation)**
基于时间差 $\Delta t$ 将航迹状态向前推演
$$
\hat{x}_{k|k-1} = F(\Delta t) \cdot \hat{x}_{k-1|k-1}
$$
- $\hat{x}_{k|k-1}$: 外推后的预测状态向量
- $F(\Delta t)$: 状态转移矩阵 ( CV/CA 模型 $\Delta t$ 变化)
- $\hat{x}_{k-1|k-1}$: 上一时刻的更新状态
3. **残差计算 (Innovation Calculation)**
计算实际量测值与预测观测值之间的差异
$$
\tilde{y} = z_{meas} - H \cdot \hat{x}_{k|k-1}
$$
- $\tilde{y}$: 修正后的残差 (Innovation)
- $z_{meas}$: 转换后的笛卡尔坐标量测值
- $H$: 观测矩阵
---
### 3\. 代码实现范式 (C++ Optimized)
利用 2.5.1 定义的定长数组和 SIMD 对齐特性
```cpp
// 2.5.1 TrackData (State[8]: x, y, z, vx, vy, vz, pad, pad)
// 2.5.1 DetectionResult (Block 0: r, azi, ele, v)
void KalmanFilter::predictAndAssociate(TrackData& track, const DetectionResult& meas) {
// 1. 时间对齐 (使用 2.6.2 定义的硬件/内核时间戳)
int64_t dt_us = (int64_t)meas.timestamp_us - (int64_t)track.timestamp_us;
// 2. OOSM 保护 (基线:丢弃乱序)
if (dt_us <= 0) return;
float dt = dt_us * 1e-6f;
// 3. 状态外推 (CV 模型示例) - SIMD 友好
// x' = x + vx * dt
// vx' = vx
float predicted_state[8];
// 手动展开循环或利用编译器自动向量化
predicted_state[0] = track.state[0] + track.state[3] * dt; // x
predicted_state[1] = track.state[1] + track.state[4] * dt; // y
predicted_state[2] = track.state[2] + track.state[5] * dt; // z
predicted_state[3] = track.state[3]; // vx
predicted_state[4] = track.state[4]; // vy
predicted_state[5] = track.state[5]; // vz
// 4. 转换量测到笛卡尔坐标 (z_meas)
float z_x, z_y, z_z;
SphericalToCartesian(meas.range, meas.azimuth, meas.elevation, z_x, z_y, z_z);
// 5. 计算马氏距离 (关联判据)
float dist = CalculateMahalanobisDist(predicted_state, track.covariance_diag, z_x, z_y, z_z);
if (dist < GATING_THRESHOLD) {
// 关联成功,准备 Update
// …
}
}
```
### 4\. 时钟跳变防护 (Sanity Check)
为了防止 2.6.1 中的 PTP/NTP 发生阶跃导致 $\Delta t$ 异常例如突然跳变 100
- **基线策略**设置物理合理性阈值
- **判据** `abs(dt) > 10.0f` (10 )视为时钟故障
- **动作**
- 不更新航迹状态
- 重置航迹的 `timestamp_us` `meas.timestamp_us`强制同步但不外推状态)。
- 触发 `SystemClockJumpEvent` 告警
-----
## 四、总结2.6.4 基线图谱
| 维度 | 核心基线 | 设计意图 |
| :--- | :--- | :--- |
| **对齐方向** | **Track Extrapolates to Measurement** | 尊重数据的物理生成时间消除处理延迟对估计的影响 |
| **时间差计算** | **$\Delta t = t_{meas} - t_{track}$** | 基于绝对时间戳而非处理间隔 |
| **乱序处理** | **Drop OOSM ($\Delta t < 0$)** | 牺牲个别数据点的利用率换取算法的低复杂度和高吞吐 |
| **异常防护** | **Sanity Check (\> 10s)** | 防止时钟源故障导致航迹出地球 |
-----

View File

@@ -0,0 +1,134 @@
---
tags: []
date created: 星期三, 十一月 26日 2025, 10:55:22 晚上
date modified: 星期三, 十一月 26日 2025, 11:02:47 晚上
---
# 2.6.5 全链路延迟审计与抖动监控 (End-to-End Latency Auditing & Jitter Monitoring)
如果说 2.6.4 是为了让算法“算得对”,那么 2.6.5 就是为了证明系统“跑得快”且“跑得稳”。这是系统性能的**心电图**,也是触发 **2.3.5 热节流****2.3.4 故障恢复** 的核心依据。
-----
## TL;DR
本节确立了 **“关键节点埋点 (Checkpointing) + 驻留时间计算 (Residence Time)”** 的基线策略。
利用数据包中携带的 **不可变时间戳 (Birth Timestamp)** 作为 T0在流水线的每个交接点Handoff Point采集当前时间计算 **阶段耗时****全系统驻留时间**。监控数据通过 **无锁直方图 (Lock-free Histogram)** 聚合,并实时计算 P99 延迟与抖动Jitter一旦超出 CPI 周期阈值,立即触发流控。
-----
## 一、 约束输入与对齐 (Constraints & Alignment)
基于前序基线,我们面临以下硬性约束:
1. **时间基准**:所有打点必须使用 **2.6.1** 确立的 `HighPrecisionClock` (TSC-based),以保证纳秒级精度和极低开销(\< 20ns
2. **零干扰**:监控逻辑严禁阻塞业务线程。严禁在热路径上进行文件 I/O写日志或复杂的锁操作。
3. **关联性**:必须能够将延迟数据关联到具体的 `StationID``TraceID`,以便定位是哪个雷达阵面或哪一帧数据导致了卡顿。
-----
## 二、 权衡分析与选项呈现 (Trade-off Matrix)
### 议题 1埋点粒度 (Granularity)
| 选项 | A. 仅首尾打点 (Black Box) | B. 细粒度逐级打点 (White Box) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | 仅在 `DataReceiver` 入口和 `DisplayController` 出口记录。计算 $T_{out} - T_{in}$。 | 在每个模块的 Input/Output 队列处均打点Rx, DSP\_Start, DSP\_End, Track, Tx… |
| **开销** | 极低。 | 低TSC 读取极快)。 |
| **诊断能力** | **差**。只能知道系统慢了,不知道是 GPU 算慢了,还是 PCIe 堵了,还是调度器卡了。 | **强**。能精确绘制“火焰图”,定位瓶颈环节。 |
| **结论** | 仅适用于最终用户 SLA。 | **研发与运维的标准解**。 |
### 议题 2数据聚合策略 (Aggregation Strategy)
| 选项 | A. 全量日志 (Raw Logs) | B. 内存直方图聚合 (In-Memory Histogram) **(推荐)** |
| :--- | :--- | :--- |
| **机制** | `LOG(INFO) << "Packet " << id << " took " << lat << "us";` | 维护一组原子计数器桶Buckets`<1ms`, `1-5ms`… 仅统计次数。 |
| **IO 压力** | **极大**。每秒 10k 包产生 10k 条日志,瞬间打爆磁盘 IOPS。 | **零**。仅涉及内存原子自增 (`fetch_add`)。 |
| **实时性** | 延迟高(需等日志落盘分析)。 | **实时**。控制面可随时读取当前 P99 值。 |
-----
## 三、 基线确立与实施规范
为了实现“显微镜级”的性能观测,我们确立 **B. 细粒度逐级打点** + **B. 内存直方图聚合** 为基线。
### 1\. 关键路径检查点定义 (Checkpoint Definition)
我们将数据包在系统中的生命周期划分为 5 个关键时刻Timestamp
- **T0 (Birth/Ingress)**: `DataReceiver` 收到 UDP 包的时刻(硬件/内核打点,见 2.6.2)。
- *阶段 1接收耗时 (Rx Latency)* = $T_1 - T_0$
- **T1 (Dispatch)**: `AssemblyBuffer` 组装完成,推入 GPU 输入队列的时刻。
- *阶段 2排队与传输 (Queue & PCIe)* = $T_2 - T_1$
- **T2 (Algo Start)**: `SignalProcessor` 从队列取出数据,开始 CUDA Kernel 的时刻。
- *阶段 3计算耗时 (Compute)* = $T_3 - T_2$
- **T3 (Algo End)**: 信号处理完成,生成点迹/航迹的时刻。
- *阶段 4后处理与关联 (Post-Proc)* = $T_4 - T_3$
- **T4 (Egress)**: `DisplayController` 完成序列化并调用 `sendto` 的时刻。
**全链路驻留时间 (Residence Time)** = $T_4 - T_0$。
### 2\. 数据结构:伴随式遥测对象 (`TelemetryContext`)
为了避免在 `DataPacket` 中增加过多字段(膨胀 Payload我们采用 **“边车模式 (Sidecar)”** 或利用 `TraceContext`(如果支持携带额外数据)。考虑到 C++ 性能,建议直接在 `DataPacket::Header` 中扩展调试字段(仅在 Debug/Profile 模式下启用)或使用 **Thread Local 统计器**
**生产环境基线(高性能方案)**
不随包携带所有中间时间戳,而是 **即时聚合**
```cpp
// 伪代码:各模块内的埋点逻辑
void SignalProcessor::onData(DataPacket& pkt) {
uint64_t now = Clock::now();
// 1. 计算上一阶段耗时 (排队延迟)
// Packet Header 中记录了上一个节点的离开时间 last_leav_time
uint64_t queue_latency = now - pkt.header.last_leave_time;
Metrics::Histogram("dsp_queue_latency")->observe(queue_latency);
// 2. 执行业务
process(pkt);
// 3. 计算本阶段耗时
uint64_t done = Clock::now();
uint64_t compute_latency = done - now;
Metrics::Histogram("dsp_compute_latency")->observe(compute_latency);
// 4. 更新 Header传给下游
pkt.header.last_leave_time = done;
}
```
### 3\. 抖动监控与告警阈值 (Jitter Monitor)
单纯看平均耗时Avg会掩盖问题我们必须监控 **P99 (99 分位)****抖动 (Jitter)**
- **抖动定义**$J_i = |(T4_i - T4_{i-1}) - (T0_i - T0_{i-1})|$
- 即:*输出间隔的变化量* 与 *输入间隔的变化量* 之差。
- 理想情况下,如果系统处理能力恒定,输入是 10ms 间隔,输出也应该是 10ms 间隔,抖动为 0。
- **阈值设定**
- **Warning**: P99 Latency \> $0.8 \times \text{CPI\_Interval}$。
- **Critical**: P99 Latency \> $1.0 \times \text{CPI\_Interval}$(系统已无法实时处理,必然积压)。
### 4\. 闭环反馈:触发热节流 (2.3.5 Linkage)
这是 2.6.5 与 2.3.5 的联动点。热节流不仅仅由**温度**触发,也应由**性能拥塞**触发。
- **逻辑**
`MonitoringModule` 每秒轮询一次延迟指标。
- 如果 `dsp_queue_latency` (排队延迟) 持续增长 -\> 说明 GPU 处理不过来。
- **动作**:发布 `SystemOverloadEvent(Type=COMPUTE_LAG)`
- **响应**:调度器下发 `SetComputeThrottleEvent(Level=1)`,通过主动降级(如减少点迹提取数量)来恢复实时性。
-----
## 四、 总结2.6 章节整体回顾
至此,**2.6 时序同步与数据一致性** 已全部完成。我们构建了一套严密的时空治理体系:
1. **2.6.1 时钟源**Hardware PTP + TSC确保尺子是准的。
2. **2.6.2 打点**Kernel/Hardware Ingress Timestamp确保起点是真实的。
3. **2.6.3 对齐**:原地乱序重组,确保数据在进入算法前是齐整的。
4. **2.6.4 融合**:异步外推,确保航迹与量测在物理时间上是对齐的。
5. **2.6.5 审计**:全链路耗时监控,确保系统没有因过载而失速。
-----