7.8 KiB
7.8 KiB
tags, date created, date modified
| tags | date created | date modified |
|---|---|---|
| 星期三, 十一月 26日 2025, 10:25:55 晚上 | 星期三, 十一月 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),我们面临以下硬性约束:
- 数据形态:
- 输入:UDP JUMBO Frame (9KB)。每个包可能包含 1 个或多个脉冲的回波数据,带有
BatchID(CPI ID) 和PulseIndex。 - 输出:符合 2.2.6 定义的显存布局
[Channel][Pulse][Range](Padding 对齐)。
- 输入:UDP JUMBO Frame (9KB)。每个包可能包含 1 个或多个脉冲的回波数据,带有
- 时序特性:
- 网络抖动(Jitter)客观存在,数据包到达顺序不一定严格均等。
- 算法要求:多普勒处理(FFT)要求脉冲间隔(PRI)是均匀的。如果数据包晚到,我们不能“等”太久,否则会阻塞流水线;如果包丢了,矩阵就“穿孔”了。
- 内存模型:必须在 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) 地址计算)。 |
议题 2:CPI 提交触发机制 (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 池。
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 线程 的核心循环逻辑:
- 接收:
recvmmsg收到一个 UDP 包。 - 解析:读取包头,获取
BatchID(CPI ID) 和PulseIndex。 - 寻址:
- 若
BatchID是新的,从MemoryPool申请一块新AssemblyBuffer。 - 若
BatchID已存在,命中缓存。
- 若
- 计算偏移 (关键):
- 利用 2.2.6 确定的 Pitch (行对齐步长)。
TargetAddr = BufferBase + (PulseIndex * Pitch_Bytes)。
- 写入:将 UDP Payload 直接
memcpy到TargetAddr。- 注:这是数据进入系统的唯一一次拷贝。
- 标记:
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.1.2)必须强制要求 FPGA/DPU 打包时带上
- 反驳 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% 丢弃。需显式清除脏数据。 |