* refactor(train): hoist canonical PCK/OKS to un-gated metrics_core; fold test_metrics onto production (ADR-155 M1 §8)
ADR-155 §8 deferred item: test_metrics.rs reference kernels validated
production against their OWN reimplementation — a test that cannot catch a
canonical-impl bug (both could be wrong the same way).
- Extract canonical_torso_size / pck_canonical / oks_canonical / sigmas /
bounding_box_diagonal into a new NON-tch-gated `metrics_core` module, so
the single metric definition is reachable under
`cargo test --no-default-features` (the `metrics` module is tch-gated).
`metrics` re-exports every item → still exactly ONE implementation.
- Rewrite tests/test_metrics.rs to assert the PRODUCTION pck_canonical /
oks_canonical equal hand-computed fixtures (not a reimplementation):
canonical_pck_matches_hand_computed_fixture (corr=3/total=4/pck=0.75),
hip↔hip normalizer pin, zero-visible⇒0.0, OKS perfect⇒1.0, fake-Gold pin.
- Keep an INDEPENDENT raw-threshold reference kernel only as a differential
cross-check: test_kernel_agrees_with_canonical asserts it AGREES with
canonical where torso==1.0 (genuine cross-check, not duplication).
Grade: MEASURED. test_metrics 10→12 tests, 0 failed.
Co-Authored-By: claude-flow <ruv@ruv.net>
* fix(sensing-server): relabel divergent live PCK/OKS so they're never conflated with canonical (ADR-155 M1 §2.1/§8 Goal C)
Goal C named training_api.rs:804 (torso-HEIGHT PCK). Auditing it surfaced
TWO findings the ADR-155 §1 table missed:
1. training_api.rs is an ORPHAN file — not declared `mod` in lib.rs OR main.rs,
so it does NOT compile into the crate. It does not drive the live server.
2. The REAL live `best_pck`/`best_oks` (main.rs training path → RVF metadata
JSON read by model_manager.rs) come from trainer.rs:
- `pck_at_threshold` = RAW-threshold PCK, NO torso normalization (the most
divergent kind), printed/serialized as bare "PCK@0.2".
- `oks_map` calls `oks_single(area=1.0)` = the EXACT fake-Gold pattern
ADR-155 §2.1 claimed closed elsewhere — still live here, inflating best_oks.
Resolution = RELABEL (torso/raw math is load-bearing on different data; the
pub fns can't be renamed without breaking API; sensing-server has no train/
ndarray dep). Honest unify is a tracked §8 backlog item.
- training_api.rs: `compute_pck` → `compute_pck_torso_height` + divergence doc;
val_pck/best_pck/val_oks struct fields documented as torso-HEIGHT proxies;
logs say `pck_torso_h@0.2`. Test torso_pck_is_labelled_distinctly_from_canonical.
- trainer.rs (LIVE): `pck_at_threshold` documented raw-unnormalized; `oks_map`
area=1.0 flagged fake-Gold; test pck_at_threshold_is_raw_unnormalized_not_canonical.
- main.rs: live print relabelled `pck_raw@0.2` / `oks_map(area=1.0 proxy)`.
No wire-format field renames (back-compat); no pub-API rename (no silent break).
Grade: MEASURED (relabel + divergence pinned). sensing-server 450→451 lib tests, 0 failed.
Co-Authored-By: claude-flow <ruv@ruv.net>
* docs(adr-155): mark §8 metric items RESOLVED + audit map + honest §1 under-count correction (M1b Goals A/D)
- §8.1: full PCK/OKS audit map (every def: file:line, basis, canonical/
legacy/distinct), the two §8 items marked RESOLVED with resolution+why.
- Honest finding: §1's "seven divergent metrics" was an UNDER-count —
sensing-server's LIVE trainer.rs has a raw-unnormalized PCK and an
area=1.0 fake-Gold OKS the table omitted, and the file §8 named
(training_api.rs) is orphaned dead code. §9 honest-limits updated.
- Goal D: metrics.rs *_v2 variants confirmed caller-less + deprecated;
noted for future cleanup, NOT deleted (public API, tch-gated).
- CHANGELOG [Unreleased] Fixed entry.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(ruvector): RaBitQ Pass-2 randomized rotation + topk bugfix (ADR-156 §8)
Implements the deferred "Multi-bit / Extended RaBitQ Pass 2" backlog item
from ADR-156 §8: a deterministic randomized orthogonal rotation applied
before sign-quantization, the published RaBitQ construction (Gao & Long,
SIGMOD 2024).
Rotation construction: Fast Hadamard Transform + seeded ±1 sign flips
("HD" / randomized Hadamard), O(d log d) time and O(d) memory — a dense
d×d rotation is O(d²) and infeasible at the 65,535-d the wire format
provisions for. Pads to the next power of two; SplitMix64 seeds the sign
stream so index-time and query-time rotations are bit-identical.
API is additive and backward-compatible: Pass 1 (`from_embedding`) is
untouched; Pass 2 is opt-in via `Sketch::from_embedding_rotated` and
`SketchBank::with_rotation` (+ `insert_embedding` / `topk_embedding` /
`novelty_embedding` helpers that rotate consistently). Default behaviour
is unchanged.
While building the Pass-2 coverage harness, found and fixed a PRE-EXISTING
correctness bug in `SketchBank::topk`: the n>k heap path used
`BinaryHeap<Reverse<(d,id)>>` (a min-heap) but treated its peek as the
max, so it returned the k FARTHEST sketches as "nearest". The shipped unit
tests only exercised the n≤k fast path, so it went unnoticed. Fixed to a
plain max-heap; pinned by `topk_heap_path_returns_nearest` and
`tight_clusters_give_high_coverage_with_overfetch` (the latter measured
0.072 on the old code).
New tests (+17, 100→117 in the crate): rotation determinism/norm-preservation
(`rotation_is_deterministic_for_seed`, `rotation_preserves_norm`), Pass-2
shape-compatibility, `pass2_coverage_not_worse_than_pass1`, and a
deterministic coverage report.
MEASURED top-K coverage (anisotropic planted-cluster fixture, cosine ground
truth; dim=128 N=2048 K=8 64 clusters noise=0.35 128 queries):
candidate_k=K=8 : Pass1 36.13% -> Pass2 46.39% (both << 90% bar)
candidate_k=24 : Pass1 83.89% -> Pass2 91.60% (Pass2 clears 90%)
candidate_k=32 : Pass1/Pass2 100%
Honest result: rotation consistently helps (+10pp at strict K), but neither
pass clears the ADR-084 90% bar at candidate_k==K on this distribution.
Pass 2 reaches 90% only with ~3x over-fetch (the ADR-084 "candidate set"
deployment pattern). Multi-bit Pass 3 evaluated separately.
Co-Authored-By: claude-flow <ruv@ruv.net>
* feat(ruvector): multi-bit Pass-3 experiment + ADR-156/084 measured results
Adds the multi-bit half of the ADR-156 §8 "Multi-bit / Extended RaBitQ"
item as a MEASURED experiment (coverage::measure_multibit): rotate, then
b-bit uniform scalar-quantize each coord, rank by L1 over codes — the
natural multi-bit generalization of hamming. Measures the bit/coverage
tradeoff the backlog item asked for.
MEASURED at the strict bar (candidate_k=K=8, anisotropic planted-cluster
fixture, cosine ground truth):
Pass1 (1-bit, no rot) 36.13% 16 B/vec
Pass2 (1-bit, rot) 46.39% 16 B/vec
Pass3 (rot, 2-bit) 54.39% 32 B/vec
Pass3 (rot, 3-bit) 66.70% 48 B/vec
Pass3 (rot, 4-bit) 74.22% 64 B/vec
Honest: multi-bit monotonically helps but even 4-bit (4x memory) reaches
only 74% at the strict bar — neither rotation nor <=4-bit multi-bit clears
the strict-K 90% bar on this distribution. The bar is met via over-fetch
(Pass2 @ candidate_k=24). Tests: multibit_tradeoff_report,
multibit_1bit_matches_pass2_approx (+ sanity that 1-bit ~= Pass-2).
Docs:
- ADR-156 §8 item #2 marked RESOLVED-PARTIAL; §5 #2 grade CLAIMED ->
MEASURED-on-our-hardware; new §10 with full measured tables, the topk
bugfix disclosure, and graded deferred sub-items.
- ADR-084: "Pass 2" section answering the rotation open-question with
measured numbers + the topk bug note.
- CHANGELOG [Unreleased]: Added (Pass-2 milestone) + Fixed (topk heap).
Co-Authored-By: claude-flow <ruv@ruv.net>
|
||
|---|---|---|
| .. | ||
| cog-ha-matter | ||
| cog-person-count | ||
| cog-pose-estimation | ||
| homecore | ||
| homecore-api | ||
| homecore-assist | ||
| homecore-automation | ||
| homecore-hap | ||
| homecore-migrate | ||
| homecore-plugin-example | ||
| homecore-plugins | ||
| homecore-recorder | ||
| homecore-server | ||
| nvsim | ||
| nvsim-server | ||
| ruv-neural@1ece3afa33 | ||
| ruview-swarm | ||
| wifi-densepose-bfld | ||
| wifi-densepose-calibration | ||
| wifi-densepose-cli | ||
| wifi-densepose-core | ||
| wifi-densepose-desktop | ||
| wifi-densepose-engine | ||
| wifi-densepose-geo | ||
| wifi-densepose-hardware | ||
| wifi-densepose-mat | ||
| wifi-densepose-nn | ||
| wifi-densepose-occworld-candle | ||
| wifi-densepose-pointcloud | ||
| wifi-densepose-ruvector | ||
| wifi-densepose-sensing-server | ||
| wifi-densepose-signal | ||
| wifi-densepose-train | ||
| wifi-densepose-vitals | ||
| wifi-densepose-wasm | ||
| wifi-densepose-wasm-edge | ||
| wifi-densepose-wifiscan | ||
| wifi-densepose-worldgraph | ||
| wifi-densepose-worldmodel | ||
| README.md | ||
README.md
WiFi-DensePose Rust Crates
See through walls with WiFi. No cameras. No wearables. Just radio waves.
A modular Rust workspace for WiFi-based human pose estimation, vital sign monitoring, and disaster response using Channel State Information (CSI). Built on RuVector graph algorithms and the WiFi-DensePose research platform by rUv.
Performance
| Operation | Python v1 | Rust v2 | Speedup |
|---|---|---|---|
| CSI Preprocessing | ~5 ms | 5.19 us | ~1000x |
| Phase Sanitization | ~3 ms | 3.84 us | ~780x |
| Feature Extraction | ~8 ms | 9.03 us | ~890x |
| Motion Detection | ~1 ms | 186 ns | ~5400x |
| Full Pipeline | ~15 ms | 18.47 us | ~810x |
| Vital Signs | N/A | 86 us (11,665 fps) | -- |
Crate Overview
Core Foundation
| Crate | Description | crates.io |
|---|---|---|
wifi-densepose-core |
Types, traits, and utilities (CsiFrame, PoseEstimate, SignalProcessor) |
|
wifi-densepose-config |
Configuration management (env, TOML, YAML) | |
wifi-densepose-db |
Database persistence (PostgreSQL, SQLite, Redis) |
Signal Processing & Sensing
| Crate | Description | RuVector Integration | crates.io |
|---|---|---|---|
wifi-densepose-signal |
SOTA CSI signal processing (6 algorithms from SpotFi, FarSense, Widar 3.0) | ruvector-mincut, ruvector-attn-mincut, ruvector-attention, ruvector-solver |
|
wifi-densepose-vitals |
Vital sign extraction: breathing (6-30 BPM) and heart rate (40-120 BPM) | -- | |
wifi-densepose-wifiscan |
Multi-BSSID WiFi scanning for Windows-enhanced sensing | -- |
Neural Network & Training
| Crate | Description | RuVector Integration | crates.io |
|---|---|---|---|
wifi-densepose-nn |
Multi-backend inference (ONNX, PyTorch, Candle) with DensePose head (24 body parts) | -- | |
wifi-densepose-train |
Training pipeline with MM-Fi dataset, 114->56 subcarrier interpolation | All 5 crates |
Disaster Response
| Crate | Description | RuVector Integration | crates.io |
|---|---|---|---|
wifi-densepose-mat |
Mass Casualty Assessment Tool -- survivor detection, triage, multi-AP localization | ruvector-solver, ruvector-temporal-tensor |
Hardware & Deployment
| Crate | Description | crates.io |
|---|---|---|
wifi-densepose-hardware |
ESP32, Intel 5300, Atheros CSI sensor interfaces (pure Rust, no FFI) | |
wifi-densepose-wasm |
WebAssembly bindings for browser-based disaster dashboard | |
wifi-densepose-sensing-server |
Axum server: ESP32 UDP ingestion, WebSocket broadcast, sensing UI |
Applications
| Crate | Description | crates.io |
|---|---|---|
wifi-densepose-api |
REST + WebSocket API layer | |
wifi-densepose-cli |
Command-line tool for MAT disaster scanning |
Architecture
wifi-densepose-core
(types, traits, errors)
|
+-------------------+-------------------+
| | |
wifi-densepose-signal wifi-densepose-nn wifi-densepose-hardware
(CSI processing) (inference) (ESP32, Intel 5300)
+ ruvector-mincut + ONNX Runtime |
+ ruvector-attn-mincut + PyTorch (tch) wifi-densepose-vitals
+ ruvector-attention + Candle (breathing, heart rate)
+ ruvector-solver |
| | wifi-densepose-wifiscan
+--------+---------+ (BSSID scanning)
|
+------------+------------+
| |
wifi-densepose-train wifi-densepose-mat
(training pipeline) (disaster response)
+ ALL 5 ruvector + ruvector-solver
+ ruvector-temporal-tensor
|
+-----------------+-----------------+
| | |
wifi-densepose-api wifi-densepose-wasm wifi-densepose-cli
(REST/WS) (browser WASM) (CLI tool)
|
wifi-densepose-sensing-server
(Axum + WebSocket)
RuVector Integration
All RuVector crates at v2.0.4 from crates.io:
| RuVector Crate | Used In | Purpose |
|---|---|---|
ruvector-mincut |
signal, train | Dynamic min-cut for subcarrier selection & person matching |
ruvector-attn-mincut |
signal, train | Attention-weighted min-cut for antenna gating & spectrograms |
ruvector-temporal-tensor |
train, mat | Tiered temporal compression (4-10x memory reduction) |
ruvector-solver |
signal, train, mat | Sparse Neumann solver for interpolation & triangulation |
ruvector-attention |
signal, train | Scaled dot-product attention for spatial features & BVP |
Signal Processing Algorithms
Six state-of-the-art algorithms implemented in wifi-densepose-signal:
| Algorithm | Paper | Year | Module |
|---|---|---|---|
| Conjugate Multiplication | SpotFi (SIGCOMM) | 2015 | csi_ratio.rs |
| Hampel Filter | WiGest | 2015 | hampel.rs |
| Fresnel Zone Model | FarSense (MobiCom) | 2019 | fresnel.rs |
| CSI Spectrogram | Standard STFT | 2018+ | spectrogram.rs |
| Subcarrier Selection | WiDance (MobiCom) | 2017 | subcarrier_selection.rs |
| Body Velocity Profile | Widar 3.0 (MobiSys) | 2019 | bvp.rs |
Quick Start
As a Library
use wifi_densepose_core::{CsiFrame, CsiMetadata, SignalProcessor};
use wifi_densepose_signal::{CsiProcessor, CsiProcessorConfig};
// Configure the CSI processor
let config = CsiProcessorConfig::default();
let processor = CsiProcessor::new(config);
// Process a CSI frame
let frame = CsiFrame { /* ... */ };
let processed = processor.process(&frame)?;
Vital Sign Monitoring
use wifi_densepose_vitals::{
CsiVitalPreprocessor, BreathingExtractor, HeartRateExtractor,
VitalAnomalyDetector,
};
let mut preprocessor = CsiVitalPreprocessor::new(56); // 56 subcarriers
let mut breathing = BreathingExtractor::new(100.0); // 100 Hz sample rate
let mut heartrate = HeartRateExtractor::new(100.0);
// Feed CSI frames and extract vitals
for frame in csi_stream {
let residuals = preprocessor.update(&frame.amplitudes);
if let Some(bpm) = breathing.push_residuals(&residuals) {
println!("Breathing: {:.1} BPM", bpm);
}
}
Disaster Response (MAT)
use wifi_densepose_mat::{DisasterResponse, DisasterConfig, DisasterType};
let config = DisasterConfig {
disaster_type: DisasterType::Earthquake,
max_scan_zones: 16,
..Default::default()
};
let mut responder = DisasterResponse::new(config);
responder.add_scan_zone(zone)?;
responder.start_continuous_scan().await?;
Hardware (ESP32)
use wifi_densepose_hardware::{Esp32CsiParser, CsiFrame};
let parser = Esp32CsiParser::new();
let raw_bytes: &[u8] = /* UDP packet from ESP32 */;
let frame: CsiFrame = parser.parse(raw_bytes)?;
println!("RSSI: {} dBm, {} subcarriers", frame.metadata.rssi, frame.subcarriers.len());
Training
# Check training crate (no GPU needed)
cargo check -p wifi-densepose-train --no-default-features
# Run training with GPU (requires tch/libtorch)
cargo run -p wifi-densepose-train --features tch-backend --bin train -- \
--config training.toml --dataset /path/to/mmfi
# Verify deterministic training proof
cargo run -p wifi-densepose-train --features tch-backend --bin verify-training
Building
# Clone the repository
git clone https://github.com/ruvnet/wifi-densepose.git
cd wifi-densepose/v2
# Check workspace (no GPU dependencies)
cargo check --workspace --no-default-features
# Run all tests
cargo test --workspace --no-default-features
# Build release
cargo build --release --workspace
Feature Flags
| Crate | Feature | Description |
|---|---|---|
wifi-densepose-nn |
onnx (default) |
ONNX Runtime backend |
wifi-densepose-nn |
tch-backend |
PyTorch (libtorch) backend |
wifi-densepose-nn |
candle-backend |
Candle (pure Rust) backend |
wifi-densepose-nn |
cuda |
CUDA GPU acceleration |
wifi-densepose-train |
tch-backend |
Enable GPU training modules |
wifi-densepose-mat |
ruvector (default) |
RuVector graph algorithms |
wifi-densepose-mat |
api (default) |
REST + WebSocket API |
wifi-densepose-mat |
distributed |
Multi-node coordination |
wifi-densepose-mat |
drone |
Drone-mounted scanning |
wifi-densepose-hardware |
esp32 |
ESP32 protocol support |
wifi-densepose-hardware |
intel5300 |
Intel 5300 CSI Tool |
wifi-densepose-hardware |
linux-wifi |
Linux commodity WiFi |
wifi-densepose-wifiscan |
wlanapi |
Windows WLAN API async scanning |
wifi-densepose-core |
serde |
Serialization support |
wifi-densepose-core |
async |
Async trait support |
Testing
# Unit tests (all crates)
cargo test --workspace --no-default-features
# Signal processing benchmarks
cargo bench -p wifi-densepose-signal
# Training benchmarks
cargo bench -p wifi-densepose-train --no-default-features
# Detection benchmarks
cargo bench -p wifi-densepose-mat
Supported Hardware
| Hardware | Crate Feature | CSI Subcarriers | Cost |
|---|---|---|---|
| ESP32-S3 Mesh (3-6 nodes) | hardware/esp32 |
52-56 | ~$54 |
| Intel 5300 NIC | hardware/intel5300 |
30 | ~$50 |
| Atheros AR9580 | hardware/linux-wifi |
56 | ~$100 |
| Any WiFi (Windows/Linux) | wifiscan |
RSSI-only | $0 |
Architecture Decision Records
Key design decisions documented in docs/adr/:
| ADR | Title | Status |
|---|---|---|
| ADR-014 | SOTA Signal Processing | Accepted |
| ADR-015 | MM-Fi + Wi-Pose Training Datasets | Accepted |
| ADR-016 | RuVector Training Pipeline | Accepted (Complete) |
| ADR-017 | RuVector Signal + MAT Integration | Accepted |
| ADR-021 | Vital Sign Detection Pipeline | Accepted |
| ADR-022 | Windows WiFi Enhanced Sensing | Accepted |
| ADR-024 | Contrastive CSI Embedding Model | Accepted |
Related Projects
- WiFi-DensePose -- Main repository (Python v1 + Rust v2)
- RuVector -- Graph algorithms for neural networks (5 crates, v2.0.4)
- rUv -- Creator and maintainer
License
All crates are dual-licensed under MIT OR Apache-2.0.
Copyright (c) 2024 rUv