mirror of
https://github.com/ruvnet/RuView
synced 2026-08-06 19:51:43 +00:00
fix(host): 256-bin HE-LTF ingest end-to-end + latent offset bugs (#1005)
Audit of every ADR-018 consumer against live C6 HE20 frames (532B/256-bin): - sensing-server + CLI calibrate parsers read n_subcarriers from one byte (256 decoded as 0) with stale seq/rssi offsets (rssi always 0 — latent, pre-existing, confirmed vs firmware csi_collector.c). Fixed to the real ADR-018 layout; n_subcarriers u8->u16; byte 18 surfaced as typed PpduType. - sensing-server probe buffer 256B -> 2048B (532B datagram errored on Windows) - per-node grid gate: lock densest (n_subcarriers, ppdu_type) grid, re-warm on upgrade, skip sparser minority frames — HT-64 never mixes into an HE-256 baseline window - hardware parser: HE-aware bandwidth classification (256-FFT HE20 = 20MHz, was Bw160); PpduType/Adr018Flags re-exported - verbatim live frames (532B HE-SU, 148B HT) embedded as regression fixtures - archive python parser: bandwidth heuristic mirror fix Live-validated: calibrate --tier he20 consumed 600x 256-bin frames into an ADR-135 He20 baseline (242 tones) skipping 94 HT frames; sensing-server shows node 12 active with real RSSI (-40dBm). 765 tests green across the three crates; workspace check clean; Python proof PASS. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -13,19 +13,19 @@ use std::time::Duration;
|
||||
|
||||
/// Build a minimal valid ESP32 CSI frame (magic 0xC511_0001).
|
||||
///
|
||||
/// Format (ADR-018):
|
||||
/// [0..3] magic: 0xC511_0001 (LE)
|
||||
/// [4] node_id
|
||||
/// [5] n_antennas (1)
|
||||
/// [6] n_subcarriers (e.g., 32)
|
||||
/// [7] reserved
|
||||
/// [8..9] freq_mhz (2437 = channel 6)
|
||||
/// [10..13] sequence (LE u32)
|
||||
/// [14] rssi (signed)
|
||||
/// [15] noise_floor
|
||||
/// [16..19] reserved
|
||||
/// [20..] I/Q pairs (n_antennas * n_subcarriers * 2 bytes)
|
||||
fn build_csi_frame(node_id: u8, seq: u32, rssi: i8, n_sub: u8) -> Vec<u8> {
|
||||
/// Format (ADR-018, authoritative: firmware `csi_collector.c`):
|
||||
/// [0..3] magic: 0xC511_0001 (LE)
|
||||
/// [4] node_id
|
||||
/// [5] n_antennas (1)
|
||||
/// [6..7] n_subcarriers (LE u16 — 256 for ESP32-C6 HE-SU, issue #1005)
|
||||
/// [8..11] freq_mhz (LE u32, 2437 = channel 6)
|
||||
/// [12..15] sequence (LE u32)
|
||||
/// [16] rssi (signed)
|
||||
/// [17] noise_floor
|
||||
/// [18] PPDU type (ADR-110: 0=HT/legacy, 1=HE-SU)
|
||||
/// [19] flags (ADR-110)
|
||||
/// [20..] I/Q pairs (n_antennas * n_subcarriers * 2 bytes)
|
||||
fn build_csi_frame(node_id: u8, seq: u32, rssi: i8, n_sub: u16) -> Vec<u8> {
|
||||
let n_pairs = n_sub as usize;
|
||||
let mut buf = vec![0u8; 20 + n_pairs * 2];
|
||||
|
||||
@@ -35,18 +35,19 @@ fn build_csi_frame(node_id: u8, seq: u32, rssi: i8, n_sub: u8) -> Vec<u8> {
|
||||
|
||||
buf[4] = node_id;
|
||||
buf[5] = 1; // n_antennas
|
||||
buf[6] = n_sub;
|
||||
buf[7] = 0;
|
||||
buf[6..8].copy_from_slice(&n_sub.to_le_bytes());
|
||||
|
||||
// freq = 2437 MHz (channel 6)
|
||||
let freq: u16 = 2437;
|
||||
buf[8..10].copy_from_slice(&freq.to_le_bytes());
|
||||
let freq: u32 = 2437;
|
||||
buf[8..12].copy_from_slice(&freq.to_le_bytes());
|
||||
|
||||
// sequence
|
||||
buf[10..14].copy_from_slice(&seq.to_le_bytes());
|
||||
buf[12..16].copy_from_slice(&seq.to_le_bytes());
|
||||
|
||||
buf[14] = rssi as u8;
|
||||
buf[15] = (-90i8) as u8; // noise floor
|
||||
buf[16] = rssi as u8;
|
||||
buf[17] = (-90i8) as u8; // noise floor
|
||||
buf[18] = u8::from(n_sub >= 256); // ADR-110 PPDU type: HE-SU for 256-bin
|
||||
buf[19] = 0; // ADR-110 flags
|
||||
|
||||
// Generate I/Q pairs with node-specific patterns.
|
||||
// Different nodes produce different amplitude patterns so the server
|
||||
@@ -136,7 +137,7 @@ fn test_multi_node_udp_send() {
|
||||
sock.set_write_timeout(Some(Duration::from_millis(100)))
|
||||
.ok();
|
||||
|
||||
let n_sub = 32u8;
|
||||
let n_sub = 32u16;
|
||||
let node_ids = [1u8, 2, 3, 5, 7];
|
||||
|
||||
for &nid in &node_ids {
|
||||
@@ -161,11 +162,13 @@ fn test_multi_node_udp_send() {
|
||||
/// size for various subcarrier counts (boundary testing).
|
||||
#[test]
|
||||
fn test_frame_sizes() {
|
||||
for n_sub in [1u8, 16, 32, 52, 56, 64, 128] {
|
||||
// 256 = ESP32-C6 HE-SU grid (issue #1005) → 532-byte frame as on the wire.
|
||||
for n_sub in [1u16, 16, 32, 52, 56, 64, 128, 256] {
|
||||
let frame = build_csi_frame(1, 0, -50, n_sub);
|
||||
let expected = 20 + (n_sub as usize) * 2;
|
||||
assert_eq!(frame.len(), expected, "wrong size for n_sub={n_sub}");
|
||||
}
|
||||
assert_eq!(build_csi_frame(1, 0, -50, 256).len(), 532);
|
||||
}
|
||||
|
||||
/// Simulate a mesh of N nodes sending frames at different rates.
|
||||
|
||||
Reference in New Issue
Block a user