RuView 为 RTL8720F FMCW 雷达定义版本化线协议:RtlRadarFrameV1 信封设计、传输与 Rust 解析器解析
导读
RuView 通过 ADR-263 引入 Realtek RTL8720F 2.4 GHz FMCW 雷达作为可选的感知平台,而 ADR-264-rtl8720f-radar-wire-protocol.md(本文的骨架文档)则决定在"反腐化边界"上为 CFR(Channel Frequency Report)与近/远 Range-FFT 上报定义一套 独立的、带版本、小端序、带 CRC 的 RtlRadarFrameV1 信封。读完本文你将掌握:RTL8720F 雷达报文从字节布局、载荷语义到 UDP / 串口 / 回放三种传输方式的完整契约;宿主解析器的安全校验与信任规则;以及在真实硬件与厂商 SDK 尚未到达时,如何用仓库内已落地的 Rust 编解码器、确定性模拟器、模糊测试与回放 CLI 先行打通整条数据链路。
背景:为什么不能直接复刻 Realtek 的二进制布局
ADR-263 所引用的 Realtek RTL8720F-2.4G-Radar-Advantages_EN.pptx 只描述了能力(CFR、近/远 Range-FFT、干扰上报、20/40/70 MHz 扫频、8/16/32/64 µs chirp 等),并未给出任何二进制 ABI:没有头文件名、函数签名、回调内存布局或报文结构。同时上游 Ameba RTOS 公开仓库(v1.2.1)只包含 CSI 修复,PR #1336 才暴露 wifi_radar_config 与 AT+RAD 命令,而报表接收仍依赖 wifi_hal_radar_recv_data(...) 这类非公开/占位 HAL 符号。
因此 ADR-264 需要回答两个问题:
- RuView 需要一个稳定、可测试、可在厂商 SDK 到达前实现的契约;
- 该契约不能在网络上暴露厂商结构体、指针布局、内存填充或回调生命周期规则。
另一个关键约束来自 ADR-018(ESP32 CSI 帧格式):虽然同属 2.4 GHz 频段,但 RTL8720F 是有源单站 FMCW 雷达,ESP32 路径观察的是 Wi-Fi 数据包 CSI。若直接复用 ADR-018 的 magic 或把 Realtek 雷达伪装成 ESP32 数据包,会导致"数据源识别歧义"并丢失雷达专属的标定元数据。ADR-264 明确拒绝了这条捷径。
决策总览:RuView 自有协议而非厂商内存视图
ADR-264 的核心决策是一句话:定义一个新的小端序 RtlRadarFrameV1 信封,使用独立 magic 与显式载荷类型。这是 RuView 自己的协议,不是对 Realtek 原生内存布局的断言。
实现上的铁律是:
- 所有整数字段均为小端序(little-endian);
- 浮点载荷使用 IEEE-754 binary32;
- 任何 C struct 都不允许通过
memcpy直接发送;固件必须逐字段显式序列化。
该原则在仓库源码中落地为 v2/crates/wifi-densepose-hardware/src/rtl8720f.rs 模块的文件头注释:"This is a RuView-owned wire contract around the public Ameba API boundary, not a representation of Realtek's private structs"。该模块被 v2/crates/wifi-densepose-hardware/src/lib.rs 以 pub mod rtl8720f 导出,并在 lib.rs 中通过 pub use rtl8720f::{...} 公开 Rtl8720fRadarFrame、Rtl8720fReportType、Rtl8720fRadarFlags 等类型别名,可供同 crate 与下游直接使用。
V1 信封:56 字节定长头 + 载荷 + CRC32
ADR-264 文档给出了完整的字节布局表,这是整个协议最核心的契约,必须原样保留:
| Offset | Size | Field | Meaning |
|---|---|---|---|
| 0 | 4 | magic | ASCII RTR1 (0x31525452) |
| 4 | 1 | version | 1 |
| 5 | 1 | report_type | 1 CFR, 2 range-near, 3 range-far, 4 interference, 5 capabilities |
| 6 | 2 | header_len | complete header size, initially 56 |
| 8 | 4 | frame_len | header + payload + CRC |
| 12 | 4 | sequence | wraps modulo 2^32 |
| 16 | 8 | timestamp_us | monotonic device time at acquisition |
| 24 | 8 | device_id | stable pseudonymous identifier, not a MAC address |
| 32 | 4 | center_freq_khz | RF centre frequency |
| 36 | 2 | bandwidth_mhz | 20, 40, or 70 |
| 38 | 2 | flags | calibration/interference/saturation/time-sync flags |
| 40 | 2 | element_count | complex samples or range bins |
| 42 | 1 | element_format | 0 bytes/TLV, 1 complex-i16, 2 complex-f32, 3 power-u16, 4 power-f32 |
| 43 | 1 | antenna_count | expected to be 1 for the deck's 1T1R configuration |
| 44 | 4 | scale | quantized-to-physical multiplier; 1.0 for float payloads |
| 48 | 4 | bin_spacing | Hz for CFR, metres for Range-FFT |
| 52 | 4 | calibration_id | device calibration revision/hash prefix |
| 56 | variable | payload | determined by type, count, and format |
| final-4 | 4 | crc32 | IEEE CRC-32 over header and payload |
ADR 明确补充了设计取舍:若厂商证据表明 56 字节头代价过高,后续协议版本可引入紧凑头;V1 宁愿优先保证可审计的溯源,也不做过早的字节节省。
源码层面对应着 rtl8720f.rs 的一组常量:
pub const RTL8720F_RADAR_MAGIC: u32 = 0x3152_5452; // "RTR1" in little endian
pub const RTL8720F_RADAR_VERSION: u8 = 1;
pub const RTL8720F_RADAR_HEADER_LEN: usize = 56;
pub const RTL8720F_RADAR_CRC_LEN: usize = 4;
/// Largest payload that can be carried in one IPv4 UDP datagram.
pub const RTL8720F_RADAR_MAX_FRAME_LEN: usize = 65_507;
pub const RTL8720F_RADAR_MAX_ELEMENTS: usize = 16_384;
注意两个安全边界常量:单帧上限 65 507 字节(一个 IPv4 UDP 数据报能容纳的最大载荷,即 65 535 - 20 IP 头 - 8 UDP 头)与最大元素数 16 384。这两者共同约束了长度算术的安全上界。CRC 实现位于 v2/crates/wifi-densepose-hardware/src/radio_ops.rs,文档注释说明它与 rv_feature_state.c 中的逐位实现一致:多项式 0xEDB88320、初值 0xFFFFFFFF、结果取反(即标准 IEEE CRC-32),且覆盖范围是 header + payload(不含 CRC 自身字段)。
枚举与类型/格式组合是强校验对象
header 中 report_type 与 element_format 两个枚举在源码中被建模为 Rust 枚举,非法值一律拒绝解析:
ReportType:Cfr = 1、RangeNear = 2、RangeFar = 3、Interference = 4、Capabilities = 5(见 rtl8720f.rs),其余值返回UnknownReportType;ElementFormat:Bytes = 0(TLV/opaque)、ComplexI16 = 1、ComplexF32 = 2、PowerU16 = 3、PowerF32 = 4,并据此推导每个元素的字节宽度:Bytes=1、ComplexI16/PowerF32=4、ComplexF32=8、PowerU16=2(见 rtl8720f.rs)。
更重要的是,ADR-264 要求做语义级类型/格式匹配,源码中的 validate_type_format(rtl8720f.rs)明确规定:
- CFR 只允许
ComplexI16 | ComplexF32(复数信道频率样本); - RangeNear / RangeFar 只允许
PowerU16 | PowerF32(功率谱距离 bin); - Interference / Capabilities 只允许
Bytes(版本化 TLV)。
这样一来,任何一个把 CFR 声明为 power 类型、或把 range 声明为 complex 类型的畸形帧都会在解码前被 InvalidTypeFormat 拒绝——这是把"轴(axis)语义"直接写进协议层。
载荷语义:五类上报各自的物理含义
ADR-264 规定了每类 payload 的语义:
- CFR 含有序的复数信道-频率样本。适配器必须知道频率起点/顺序,不得伪造缺失的相位;未标定帧会带 uncalibrated 标志,且不得进入任何相位敏感处理。源码中
RadarFlags::CALIBRATED为 bit 0,未置位即视为不可信相位。 - Range-near / range-far 含有序距离 bin。near 与 far 是独立的上报类型,因此滤波与泄漏行为永远不会对消费者隐藏。源码里模拟器对 RangeNear 在前 2 个 bin 叠加静态泄漏分量、对 RangeFar 则不叠加,正是这种区分的直观体现。
- Interference 含一套版本化 TLV:channel-busy、detected-during-chirp、估计干扰功率、数据包抖动。未知 TLV 必须按长度跳过(长度字段前置于每个 TLV,保证向后兼容)。模拟器产出的干扰 TLV 为:type 1 = channel-busy 百分比、type 2 = 是否在 chirp 期间检测到干扰、type 3 = 有符号 dBm 干扰功率。
- Capabilities 在启动时广播一次、也可按请求发出。它声明:支持的上报类型、带宽、chirp 长度、单帧最大元素数、最大帧率、固件版本与 SDK 标识。模拟器把能力集编码为紧凑 TLV:type 1 = 带宽位图(0b0000_0111 表示 20/40/70 MHz)、type 2 = CFR bin 数、type 3 = range bin 数、type 4 = 最小帧周期(µs)。启动能力帧让 SDK/API 漂移变成可观测事件:一旦真实 SDK 暴露的参数与启动帧声明不符,宿主可以立刻察觉。
flags 字段在源码 rtl8720f.rs 中有精确位义:bit 0 = CALIBRATED、bit 1 = INTERFERENCE_DETECTED、bit 2 = SATURATED、bit 3 = TIME_SYNCHRONIZED,且 bit 15 被 RuView 保留为 SYNTHETIC:Rust 模拟器生成的每一帧都必须置位它,真实固件则永远不得置位——这是"模拟数据绝不冒充硬件测量"的协议级兜底。
载荷元素的内存表示
源码用 RadarPayload 枚举表达五种载荷载体(rtl8720f.rs):Bytes(Vec<u8>)、ComplexI16(Vec<[i16;2]>)、ComplexF32(Vec<[f32;2]>)、PowerU16(Vec<u16>)、PowerF32(Vec<f32>)。序列化阶段全部走 to_le_bytes() 显式小端写出,没有任何一次按结构体 memcpy;解析阶段则对复数样本按 [I, Q] 交错的字节流切块重建。对 ComplexF32 与 PowerF32,编码/解码都会执行 validate_finite,任何 NaN/Infinity 直接报 NonFiniteValue。
传输层:同一信封走三种通道
ADR-264 规定同一份信封支持三种传输:
- UDP 数据报:用于常规 RuView 摄取。一条信封必须恰好装进一个 UDP 数据报,V1 不提供分片;固件会拒绝"最大上报超过配置 MTU"的 profile,并通过 capabilities 上报所需大小。
- USB CDC 或 UART:使用 COBS 成帧 + 零字节分隔符,解决串口通道的字节边界问题。
- 文件回放:长度前缀的连续信封序列(每条信封前跟一个长度字段)。
仓库中 v2/crates/wifi-densepose-hardware/src/bin/rtl8720f-sim.rs 是这三条传输路径的现成实现:它同时支持 --udp 127.0.0.1:PORT(一帧一个数据报)与 --output file(先写 LE u32 长度、再写帧字节,即"长度前缀序列")。每个测试会先发一条 capabilities_frame(),随后循环发出 CFR / RangeNear / RangeFar / Interference(每 16 帧中第 16 帧为干扰帧),并可加 --realtime 让每帧间隔 interval_ms 毫秒以模拟真实流速。--frames 与 --seed 控制帧数与确定性随机种子。
其 CLI 参数全貌:
--frames N # 发送的帧数(默认 100;另有 1 条启动 capabilities 帧)
--seed 0x... # 确定性种子(默认 0x8720f123456789ab)
--bandwidth N # 20 / 40 / 70 MHz(默认 40)
--interval_ms N # 帧间隔毫秒(默认 15,转成 frame_period_us)
--udp IP:PORT # UDP 目的地,每帧一个数据报(与 --output 至少选一)
--output PATH # 回放文件,LE u32 长度 + ADR-264 帧字节
--realtime # 按 interval_ms 实际睡眠节奏发送
硬件 crate 的 README 给出了 PowerShell 下的启动示例:
cargo run -p wifi-densepose-hardware --bin rtl8720f-sim -- `
--frames 100 --seed 0x8720f123456789ab `
--output rtl8720f-synthetic.rtr
加 --udp 127.0.0.1:5005 --realtime 即可按每 UDP 数据报一条 ADR-264 帧的方式实时推流。由于解析器是纯字节缓冲处理、无 C FFI 亦无硬件依赖,因此编译期可移植、运行结果确定——同样的字节进、同样的解析出。
在 v2/crates/wifi-densepose-sensing-server/src/main.rs 中,传感服务器用 RTL8720F_RADAR_MAX_FRAME_LEN 大小的缓冲区接收 UDP,先比对前 4 字节 RTL8720F_RADAR_MAGIC 完成协议分拣(与 Qualcomm QUALCOMM_CSI_MAGIC、MediaTek MEDIATEK_CSI_MAGIC 在同一接收循环并列,且服从 ADR-296 的源 IP 白名单过滤),随后调用 RadarFrame::from_bytes 解析,把结果转成有界摘要 RealtekRadarSnapshot 后:既在 /ws/sensing WebSocket 上推送 JSON,又通过 /api/v1/radar/latest 暴露最近一条上报(route 注册见 main.rs)。
v2/crates/wifi-densepose-sensing-server/src/realtek_radar.rs 实现了这份"有界、隐私友好的雷达摘要":RealtekRadarSnapshot 只暴露聚合量(峰值距离 peak_range_m、峰值功率、CFR 平均幅度),而不是把整帧复数谱上抛;source 字段在模拟帧上固定为 realtek:simulated,保证溯源标签端到端保留。其单元测试断言:Range 摘要必带峰且 source == "realtek:simulated"、CFR 摘要只暴露聚合幅度而峰值为 None。
宿主解析器与信任规则:解码前的每一道闸门
ADR-264 为宿主解析器列了 7 条硬性信任规则,逐条都能在源码中对应:
- 在任何分配/解码载荷之前校验 magic、version、长度、枚举值、element_count × format 的乘法与 CRC。源码
from_bytes(rtl8720f.rs)严格按序:先InsufficientData检查最小头长 → magic → version → report_type 枚举 → header_len(不得小于 56)→ frame_len(不得超 65 507,且必须 ≥ header+CRC)→ 缓冲区是否含完整 frame_len 字节 → element_count ≤ 16 384 → format 枚举 → 类型/格式语义匹配 → 用 checked_mul/checked_add 的溢出保护算术推导 payload_len 与 expected_len → 核对expected_len == frame_len→ CRC 比对。任何一步失败都返回带结构化上下文的RadarParseError,绝不 panic。 - 帧上限 64 KiB、元素数上限为配置的硬件最大值:对应常量
RTL8720F_RADAR_MAX_FRAME_LEN = 65_507与RTL8720F_RADAR_MAX_ELEMENTS = 16_384。 - 拒绝非有限浮点:
scale、bin_spacing以及浮点载荷都过is_finite()检查。 - 按设备追踪 sequence 缺口与时间戳回退:V1 设计上 sequence 按 2^32 环绕、
timestamp_us为单调设备时钟,供上层检测丢帧与时钟异常(对应验收标准中的 gap / regression 检测)。 - 保留未知 flags 但永不当作可信解释:
RadarFlags是对u16的位容器,解析器不抹除未知位,但只有已定义的位(标定、干扰、饱和、时间同步、synthetic)会进入下游语义。 - 把传输源、固件/SDK 版本、calibration ID、干扰状态附到溯源上:
RealtekRadarSnapshot保留 device_id、center_freq、bandwidth、calibrated/synthetic/interference/saturated/time_synchronized、calibration_id、bin_spacing 等完整溯源字段。 - 把 fixture/生成帧标记为 synthetic:模拟器每帧强制
CALIBRATED | SYNTHETIC,服务器侧 source 恒为realtek:simulated。
ADR 收尾还写了一条不可让步的原则:任何厂商提供的存在性概率都不能绕过 RuView 的隐私、溯源与质量门禁——厂商 AI 输出最多只能作为带 model/version 溯源、明确标注来源的派生观测,绝不充当 ground truth。这与 ADR-263 中"vendor AI presence probability → derived observation, advisory input, never ground truth"的用法表一致。
决策后果与取舍
正向收益(ADR-264 Consequences 原文):
- 固件、传输、解析器、回放与融合可以各自独立演进;
- 模糊测试与 golden fixture 的建立不依赖 Realtek SDK 或开发板;
- CFR 与 Range-FFT 能保住正确的轴(axis)与标定溯源;
- 启动时能力帧让 SDK/API 漂移可观测。
负面代价:
- 相比直接倾倒厂商缓冲区,序列化增加 CPU 与带宽开销;
- V1 字段可能在真实 API 与上报上限披露后需要修订;
- UDP 只提供完整性/检错,不提供真实性或机密性。
中性项:
- 认证可以后续叠加 ADR-032 设备身份或带签名的 RuField receipt,且不改变上报语义;
- 已有的 ESP32 ADR-018 帧格式保持不变(两条链路并存互不干扰)。
实现计划的当前状态:代码先行,硬件待闸
ADR-264 的实现计划共 6 步,其中宿主侧 1–3 步已在仓库落地,其余步骤被厂商 SDK 访问权限阻塞。逐条对照:
- 在
wifi-densepose-hardware中新增无厂商依赖的rtl8720f类型/解析模块——已完成,即 v2/crates/wifi-densepose-hardware/src/rtl8720f.rs,由 lib.rs 导出,注释明确"no dependency on the vendor SDK"。 - 新增 golden CFR、near/far Range-FFT、interference、capabilities fixtures——对应 ADR-264 收尾段所称"typed report and element enums, semantic type/format validation, bounded length arithmetic, CRC verification, finite-float checks, encode/decode round trips, corruption/truncation tests, deterministic arbitrary-input panic checks"。跨语言 golden vector 仍受厂商 SDK 回调 ABI 阻塞。
- 属性/模糊测试(长度算术、枚举处理、CRC、浮点校验)——同文件
#[cfg(test)] mod tests内有:cfr_round_trip_and_stream_consumption(校验"已消费字节数",可正确处理数据报后跟随的下一条数据)、every_report_family_round_trips、single_bit_corruption_is_detected(翻转载荷 1 bit 触发CrcMismatch)、truncation_is_reported_without_panicking(从 0 到整帧逐字节截断全部is_err()且不 panic)、count_length_mismatch_fails_before_payload_decode(伪造 element_count 后即使重算 CRC 也报PayloadLengthMismatch)、invalid_semantic_combinations_are_rejected、non_finite_values_are_rejected、arbitrary_short_inputs_never_panic(0–255 字节伪随机输入全部catch_unwind验证)、simulator_is_deterministic_and_uses_real_wire_boundary(同种子输出逐字节相等且走真实编码器)、simulator_range_peak_tracks_ground_truth(Range-FFT 峰值 bin × bin_spacing 与模拟目标距离误差 ≤ 一个 bin)、simulator_capabilities_are_explicitly_synthetic。 - 回放 CLI——即 v2/crates/wifi-densepose-hardware/src/bin/rtl8720f-sim.rs,"打印归一化元数据而不跑推理"的宿主侧消费样例见服务器摘要。
- SDK 就绪后实现嵌入式序列化器——待厂商闸门(ADR-263 P0 的
VENDOR_BLOCKED状态)。 - 以实测元素数、帧率与 API 名修订本 ADR——待验收。
ADR-264 收尾还强调:模拟器按种子确定性且走生产级编码/解析器而非平行的 mock 表示——即模拟数据与真实硬件共享同一套 to_bytes/from_bytes 代码路径,因此测试的正是将来硬件会用的协议实现。v0.9.0-realtek-beta.1.md 亦记录:该预发布版已覆盖 Rust codec 往返、损坏拒绝、尺寸边界、确定性模拟器测试,并通过 loopback UDP 端到端验证了服务器摄取、REST 上报与来源溯源;但尚未有物理 RTL8720F 板卡被烧录或测量,雷达到姿态/生命体征推断、RF 标定与精度声明均未启用。
验收标准(何时才算完成)
ADR-264 给出了五项明确验收标准,可作为任何实现此协议的工作的自检清单:
- 每种上报类型的 Rust encode/decode 往返均通过;
- RTL8720F 固件产出跨语言 golden vector(即真实固件字节必须通过宿主 golden 解码器验证);
- 在模糊语料库与任意字节输入上零解析器 panic;
- 能检测:单 bit 翻转损坏、截断、count 溢出、时间戳回退、sequence 缺口;
- 捕获 CFR 的频率顺序与 Range-FFT 的 bin 间距须对照厂商文档 + 实测目标双重验证。
协议调试与接入速查
把本文串成可执行的最小闭环,共三个环节:
① 造数据(无硬件):运行模拟器把确定性帧写入回放文件或直接 UDP 推流:
# 生成回放文件(LE u32 长度前缀 + ADR-264 帧)
cargo run -p wifi-densepose-hardware --bin rtl8720f-sim -- \
--frames 200 --bandwidth 40 --output radar.rtr
# 或实时推到本机传感服务器(注意默认 15ms 间隔配合 --realtime)
cargo run -p wifi-densepose-hardware --bin rtl8720f-sim -- \
--frames 200 --udp 127.0.0.1:5005 --realtime
② 看解析:让传感服务器在其 UDP 摄取循环里按 magic 分拣并解析(main.rs),然后查询最近一条上报摘要:
curl http://127.0.0.1:PORT/api/v1/radar/latest
③ 验正确性:跑 crate 内建测试,观察每类错误路径(损坏、截断、溢出、非有限浮点、类型/格式错配、非法带宽/天线数)都返回结构化 RadarParseError:
cargo test -p wifi-densepose-hardware rtl8720f
三个环节全部验证通过,即代表你已具备不依赖任何 Realtek 私密 ABI 就能开发、测试与回放 RTL8720F 雷达数据的能力;而当厂商 SDK 闸门打开后,唯一剩余的工作是把同一套 RtlRadarFrameV1 信封塞进固件的真实回调。
关联文档与源码导航
- 决策原文:ADR-264-rtl8720f-radar-wire-protocol.md
- 上游平台决策与交付门禁:ADR-263-rtl8720f-2-4ghz-fmcw-radar-platform.md
- 宿主实现:rtl8720f.rs(信封常量、枚举、编解码、模拟器、单元测试全部在此)
- 回放/推流 CLI:rtl8720f-sim.rs
- 模块导出:wifi-densepose-hardware/src/lib.rs
- CRC32 参考实现:radio_ops.rs
- 服务器摄取与摘要:main.rs、realtek_radar.rs
- 相关平台协议对照:ADR-266/267(MediaTek Filogic MIMO CSI)、ADR-269(Qualcomm Atheros CSI),均遵循"vendor-neutral framing + simulator + SYNTHETIC"同一范式
- 预发布状态:v0.9.0-realtek-beta.1.md
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00