Implement the hardware and firmware portions of RuvSense (ADR-029) and RuView (ADR-031) for multistatic WiFi sensing: Rust (wifi-densepose-hardware): - TdmSchedule: uniform slot assignments with configurable cycle period, guard intervals, and processing window (default 4-node 20 Hz) - TdmCoordinator: manages sensing cycles, tracks per-slot completion, cumulative clock drift compensation (±10 ppm over 50 ms = 0.5 us) - SyncBeacon: 16-byte wire format for cycle synchronization with drift correction offsets - TdmSlotCompleted event for aggregator notification - 18 unit tests + 4 doctests, all passing Firmware (C, ESP32): - Channel-hop table in csi_collector.c (s_hop_channels, configurable via csi_collector_set_hop_table) - Timer-driven channel hopping via esp_timer at dwell intervals - NDP frame injection stub via esp_wifi_80211_tx() - Backward-compatible: hop_count=1 disables hopping entirely - NVS config extension: hop_count, chan_list, dwell_ms, tdm_slot, tdm_node_count with bounds validation and Kconfig fallback defaults Co-Authored-By: claude-flow <ruv@ruv.net>
48 lines
1.7 KiB
Rust
48 lines
1.7 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;
|
|
|
|
pub use csi_frame::{CsiFrame, CsiMetadata, SubcarrierData, Bandwidth, AntennaConfig};
|
|
pub use error::ParseError;
|
|
pub use esp32_parser::Esp32CsiParser;
|
|
pub use bridge::CsiData;
|