Files
ruvnet--RuView/v2/crates/wifi-densepose-hardware/src/lib.rs
T
ruv 4d0521ca08 fix(hardware): aggregator tolerates sibling RuView UDP packet magics (#517)
The ESP32 firmware multiplexes several wire packet types onto the same
UDP port as ADR-018 raw CSI frames (magic 0xC5110001):

  0xC5110002  ADR-039 edge vitals (32 B)
  0xC5110003  ADR-069 feature vector
  0xC5110004  ADR-063 fused vitals
  0xC5110005  ADR-039 compressed CSI
  0xC5110006  ADR-081 feature state
  0xC5110007  ADR-095/#513 temporal classification

Esp32CsiParser only knew 0xC5110001, so the standalone `aggregator`
binary printed "parse error: Invalid magic: expected 0xc5110001, got
0xc5110002" for every vitals packet. No CSI data was lost — just noise.

Add the sibling-magic constants + ruview_sibling_packet_name(), classify
recognized siblings before the CSI-frame length gate, and return a new
ParseError::NonCsiPacket { magic, kind } instead of InvalidMagic. The
`aggregator` CLI now skips them quietly (logs "[skipped ADR-039 edge
vitals packet — not a CSI frame]" only with --verbose); the library-level
CsiAggregator already dropped them silently. New regression tests cover
all seven magics.

Closes #517

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-05-11 10:48:00 -04:00

65 lines
2.5 KiB
Rust

//! WiFi-DensePose hardware interface abstractions.
//!
//! This crate provides platform-agnostic types and parsers for WiFi CSI data
//! from various hardware sources:
//!
//! - **ESP32/ESP32-S3**: Parses ADR-018 binary CSI frames streamed over UDP
//! - **UDP Aggregator**: Receives frames from multiple ESP32 nodes (ADR-018 Layer 2)
//! - **Bridge**: Converts CsiFrame → CsiData for the detection pipeline (ADR-018 Layer 3)
//!
//! # Design Principles
//!
//! 1. **No mock data**: All parsers either parse real bytes or return explicit errors
//! 2. **No hardware dependency at compile time**: Parsing is done on byte buffers,
//! not through FFI to ESP-IDF or kernel modules
//! 3. **Deterministic**: Same bytes in → same parsed output, always
//!
//! # Example
//!
//! ```rust
//! use wifi_densepose_hardware::{CsiFrame, Esp32CsiParser, ParseError};
//!
//! // Parse ESP32 CSI data from UDP bytes
//! let raw_bytes: &[u8] = &[/* ADR-018 binary frame */];
//! match Esp32CsiParser::parse_frame(raw_bytes) {
//! Ok((frame, consumed)) => {
//! println!("Parsed {} subcarriers ({} bytes)", frame.subcarrier_count(), consumed);
//! let (amplitudes, phases) = frame.to_amplitude_phase();
//! // Feed into detection pipeline...
//! }
//! Err(ParseError::InsufficientData { needed, got }) => {
//! eprintln!("Need {} bytes, got {}", needed, got);
//! }
//! Err(e) => eprintln!("Parse error: {}", e),
//! }
//! ```
mod csi_frame;
mod error;
mod esp32_parser;
pub mod aggregator;
mod bridge;
pub mod esp32;
// ADR-081: Rust mirror of the firmware radio abstraction layer (L1) and
// mesh sensing plane (L3). Lets host tests, simulators, and future
// coordinator-node Rust code drive the controller stack without
// touching any downstream signal/ruvector/train/mat crate.
pub mod radio_ops;
pub use csi_frame::{CsiFrame, CsiMetadata, SubcarrierData, Bandwidth, AntennaConfig};
pub use error::ParseError;
pub use esp32_parser::{
Esp32CsiParser, ruview_sibling_packet_name, ESP32_CSI_MAGIC, RUVIEW_VITALS_MAGIC,
RUVIEW_FEATURE_MAGIC, RUVIEW_FUSED_VITALS_MAGIC, RUVIEW_COMPRESSED_CSI_MAGIC,
RUVIEW_FEATURE_STATE_MAGIC, RUVIEW_TEMPORAL_MAGIC,
};
pub use bridge::CsiData;
pub use radio_ops::{
RadioOps, RadioMode, CaptureProfile, RadioHealth, RadioError, MockRadio,
MeshRole, MeshMsgType, AuthClass, MeshHeader, NodeStatus, AnomalyAlert,
MeshError, MESH_MAGIC, MESH_VERSION, MESH_HEADER_SIZE, MESH_MAX_PAYLOAD,
crc32_ieee, decode_mesh, decode_node_status, decode_anomaly_alert,
encode_health,
};