Compare commits

..

1 Commits

Author SHA1 Message Date
Yossi Elkrief
b2fdc6282d feat: Expo mobile app scaffold (118-file structure) 2026-03-02 12:42:45 +02:00
127 changed files with 16699 additions and 3286 deletions

View File

@@ -1,400 +0,0 @@
# ADR-029: Project RuvSense -- Sensing-First RF Mode for Multistatic WiFi DensePose
| Field | Value |
|-------|-------|
| **Status** | Proposed |
| **Date** | 2026-03-02 |
| **Deciders** | ruv |
| **Codename** | **RuvSense** -- RuVector-Enhanced Sensing for Multistatic Fidelity |
| **Relates to** | ADR-012 (ESP32 Mesh), ADR-014 (SOTA Signal Processing), ADR-016 (RuVector Training), ADR-017 (RuVector Signal+MAT), ADR-018 (ESP32 Implementation), ADR-024 (AETHER Embeddings), ADR-026 (Survivor Track Lifecycle), ADR-027 (MERIDIAN Generalization) |
---
## 1. Context
### 1.1 The Fidelity Gap
Current WiFi-DensePose achieves functional pose estimation from a single ESP32 AP, but three fidelity metrics prevent production deployment:
| Metric | Current (Single ESP32) | Required (Production) | Root Cause |
|--------|------------------------|----------------------|------------|
| Torso keypoint jitter | ~15cm RMS | <3cm RMS | Single viewpoint, 20 MHz bandwidth, no temporal smoothing |
| Multi-person separation | Fails >2 people, frequent ID swaps | 4+ people, zero swaps over 10 min | Underdetermined with 1 TX-RX link; no person-specific features |
| Small motion sensitivity | Gross movement only | Breathing at 3m, heartbeat at 1.5m | Insufficient phase sensitivity at 2.4 GHz; noise floor too high |
| Update rate | ~10 Hz effective | 20 Hz | Single-channel serial CSI collection |
| Temporal stability | Drifts within hours | Stable over days | No coherence gating; model absorbs environmental drift |
### 1.2 The Insight: Sensing-First RF Mode on Existing Silicon
You do not need to invent a new WiFi standard. The winning move is a **sensing-first RF mode** that rides on existing silicon (ESP32-S3), existing bands (2.4/5 GHz), and existing regulations (802.11n NDP frames). The fidelity improvement comes from three physical levers:
1. **Bandwidth**: Channel-hopping across 2.4 GHz channels 1/6/11 triples effective bandwidth from 20 MHz to 60 MHz, 3x multipath separation
2. **Carrier frequency**: Dual-band sensing (2.4 + 5 GHz) doubles phase sensitivity to small motion
3. **Viewpoints**: Multistatic ESP32 mesh (4 nodes = 12 TX-RX links) provides 360-degree geometric diversity
### 1.3 Acceptance Test
**Two people in a room, 20 Hz update rate, stable tracks for 10 minutes with no identity swaps and low jitter in the torso keypoints.**
Quantified:
- Torso keypoint jitter < 30mm RMS (hips, shoulders, spine)
- Zero identity swaps over 600 seconds (12,000 frames)
- 20 Hz output rate (50 ms cycle time)
- Breathing SNR > 10dB at 3m (validates small-motion sensitivity)
---
## 2. Decision
### 2.1 Architecture Overview
Implement RuvSense as a new bounded context within `wifi-densepose-signal`, consisting of 6 modules:
```
wifi-densepose-signal/src/ruvsense/
├── mod.rs // Module exports, RuvSense pipeline orchestrator
├── multiband.rs // Multi-band CSI frame fusion (§2.2)
├── phase_align.rs // Cross-channel phase alignment (§2.3)
├── multistatic.rs // Multi-node viewpoint fusion (§2.4)
├── coherence.rs // Coherence metric computation (§2.5)
├── coherence_gate.rs // Gated update policy (§2.6)
└── pose_tracker.rs // 17-keypoint Kalman tracker with re-ID (§2.7)
```
### 2.2 Channel-Hopping Firmware (ESP32-S3)
Modify the ESP32 firmware (`firmware/esp32-csi-node/main/csi_collector.c`) to cycle through non-overlapping channels at configurable dwell times:
```c
// Channel hop table (populated from NVS at boot)
static uint8_t s_hop_channels[6] = {1, 6, 11, 36, 40, 44};
static uint8_t s_hop_count = 3; // default: 2.4 GHz only
static uint32_t s_dwell_ms = 50; // 50ms per channel
```
At 100 Hz raw CSI rate with 50 ms dwell across 3 channels, each channel yields ~33 frames/second. The existing ADR-018 binary frame format already carries `channel_freq_mhz` at offset 8, so no wire format change is needed.
**NDP frame injection:** `esp_wifi_80211_tx()` injects deterministic Null Data Packet frames (preamble-only, no payload, ~24 us airtime) at GPIO-triggered intervals. This is sensing-first: the primary RF emission purpose is CSI measurement, not data communication.
### 2.3 Multi-Band Frame Fusion
Aggregate per-channel CSI frames into a wideband virtual snapshot:
```rust
/// Fused multi-band CSI from one node at one time slot.
pub struct MultiBandCsiFrame {
pub node_id: u8,
pub timestamp_us: u64,
/// One canonical-56 row per channel, ordered by center frequency.
pub channel_frames: Vec<CanonicalCsiFrame>,
/// Center frequencies (MHz) for each channel row.
pub frequencies_mhz: Vec<u32>,
/// Cross-channel coherence score (0.0-1.0).
pub coherence: f32,
}
```
Cross-channel phase alignment uses `ruvector-solver::NeumannSolver` to solve for the channel-dependent phase rotation introduced by the ESP32 local oscillator during channel hops. The system:
```
[Φ₁, Φ₆, Φ₁₁] = [Φ_body + δ₁, Φ_body + δ₆, Φ_body + δ₁₁]
```
NeumannSolver fits the `δ` offsets from the static subcarrier components (which should have zero body-caused phase shift), then removes them.
### 2.4 Multistatic Viewpoint Fusion
With N ESP32 nodes, collect N `MultiBandCsiFrame` per time slot and fuse with geometric diversity:
**TDMA Sensing Schedule (4 nodes):**
| Slot | TX | RX₁ | RX₂ | RX₃ | Duration |
|------|-----|-----|-----|-----|----------|
| 0 | Node A | B | C | D | 4 ms |
| 1 | Node B | A | C | D | 4 ms |
| 2 | Node C | A | B | D | 4 ms |
| 3 | Node D | A | B | C | 4 ms |
| 4 | -- | Processing + fusion | | | 30 ms |
| **Total** | | | | | **50 ms = 20 Hz** |
Synchronization: GPIO pulse from aggregator node at cycle start. Clock drift at ±10ppm over 50 ms is ~0.5 us, well within the 1 ms guard interval.
**Cross-node fusion** uses `ruvector-attn-mincut::attn_mincut` where time-frequency cells from different nodes attend to each other. Cells showing correlated motion energy across nodes (body reflection) are amplified; cells with single-node energy (local multipath artifact) are suppressed.
**Multi-person separation** via `ruvector-mincut::DynamicMinCut`:
1. Build cross-link temporal correlation graph (nodes = TX-RX links, edges = correlation coefficient)
2. `DynamicMinCut` partitions into K clusters (one per detected person)
3. Attention fusion (§5.3 of research doc) runs independently per cluster
### 2.5 Coherence Metric
Per-link coherence quantifies consistency with recent history:
```rust
pub fn coherence_score(
current: &[f32],
reference: &[f32],
variance: &[f32],
) -> f32 {
current.iter().zip(reference.iter()).zip(variance.iter())
.map(|((&c, &r), &v)| {
let z = (c - r).abs() / v.sqrt().max(1e-6);
let weight = 1.0 / (v + 1e-6);
((-0.5 * z * z).exp(), weight)
})
.fold((0.0, 0.0), |(sc, sw), (c, w)| (sc + c * w, sw + w))
.pipe(|(sc, sw)| sc / sw)
}
```
The static/dynamic decomposition uses `ruvector-solver` to separate environmental drift (slow, global) from body motion (fast, subcarrier-specific).
### 2.6 Coherence-Gated Update Policy
```rust
pub enum GateDecision {
/// Coherence > 0.85: Full Kalman measurement update
Accept(Pose),
/// 0.5 < coherence < 0.85: Kalman predict only (3x inflated noise)
PredictOnly,
/// Coherence < 0.5: Reject measurement entirely
Reject,
/// >10s continuous low coherence: Trigger SONA recalibration (ADR-005)
Recalibrate,
}
```
When `Recalibrate` fires:
1. Freeze output at last known good pose
2. Collect 200 frames (10s) of unlabeled CSI
3. Run AETHER contrastive TTT (ADR-024) to adapt encoder
4. Update SONA LoRA weights (ADR-005), <1ms per update
5. Resume sensing with adapted model
### 2.7 Pose Tracker (17-Keypoint Kalman with Re-ID)
Lift the Kalman + lifecycle + re-ID infrastructure from `wifi-densepose-mat/src/tracking/` (ADR-026) into the RuvSense bounded context, extended for 17-keypoint skeletons:
| Parameter | Value | Rationale |
|-----------|-------|-----------|
| State dimension | 6 per keypoint (x,y,z,vx,vy,vz) | Constant-velocity model |
| Process noise σ_a | 0.3 m/s² | Normal walking acceleration |
| Measurement noise σ_obs | 0.08 m | Target <8cm RMS at torso |
| Mahalanobis gate | χ²(3) = 9.0 | 3σ ellipsoid (same as ADR-026) |
| Birth hits | 2 frames (100ms at 20Hz) | Reject single-frame noise |
| Loss misses | 5 frames (250ms) | Brief occlusion tolerance |
| Re-ID feature | AETHER 128-dim embedding | Body-shape discriminative (ADR-024) |
| Re-ID window | 5 seconds | Sufficient for crossing recovery |
**Track assignment** uses `ruvector-mincut`'s `DynamicPersonMatcher` (already integrated in `metrics.rs`, ADR-016) with joint position + embedding cost:
```
cost(track_i, det_j) = 0.6 * mahalanobis(track_i, det_j.position)
+ 0.4 * (1 - cosine_sim(track_i.embedding, det_j.embedding))
```
---
## 3. GOAP Integration Plan (Goal-Oriented Action Planning)
### 3.1 Action Dependency Graph
```
Phase 1: Foundation
Action 1: Channel-Hopping Firmware ──────────────────────┐
│ │
v │
Action 2: Multi-Band Frame Fusion ──→ Action 6: Coherence │
│ Metric │
v │ │
Action 3: Multistatic Mesh v │
│ Action 7: Coherence │
v Gate │
Phase 2: Tracking │ │
Action 4: Pose Tracker ←────────────────┘ │
│ │
v │
Action 5: End-to-End Pipeline @ 20 Hz ←────────────────────┘
v
Phase 4: Hardening
Action 8: AETHER Track Re-ID
v
Action 9: ADR-029 Documentation (this document)
```
### 3.2 Cost and RuVector Mapping
| # | Action | Cost | Preconditions | RuVector Crates | Effects |
|---|--------|------|---------------|-----------------|---------|
| 1 | Channel-hopping firmware | 4/10 | ESP32 firmware exists | None (pure C) | `bandwidth_extended = true` |
| 2 | Multi-band frame fusion | 5/10 | Action 1 | `solver`, `attention` | `fused_multi_band_frame = true` |
| 3 | Multistatic mesh aggregation | 5/10 | Action 2 | `mincut`, `attn-mincut` | `multistatic_mesh = true` |
| 4 | Pose tracker | 4/10 | Action 3, 7 | `mincut` | `pose_tracker = true` |
| 5 | End-to-end pipeline | 6/10 | Actions 2-4 | `temporal-tensor`, `attention` | `20hz_update = true` |
| 6 | Coherence metric | 3/10 | Action 2 | `solver` | `coherence_metric = true` |
| 7 | Coherence gate | 3/10 | Action 6 | `attn-mincut` | `coherence_gating = true` |
| 8 | AETHER re-ID | 4/10 | Actions 4, 7 | `attention` | `identity_stable = true` |
| 9 | ADR documentation | 2/10 | All above | None | Decision documented |
**Total cost: 36 units. Minimum viable path to acceptance test: Actions 1-5 + 6-7 = 30 units.**
### 3.3 Latency Budget (50ms cycle)
| Stage | Budget | Method |
|-------|--------|--------|
| UDP receive + parse | <1 ms | ADR-018 binary, 148 bytes, zero-alloc |
| Multi-band fusion | ~2 ms | NeumannSolver on 2×2 phase alignment |
| Multistatic fusion | ~3 ms | attn_mincut on 3-6 nodes × 64 velocity bins |
| Model inference | ~30-40 ms | CsiToPoseTransformer (lightweight, no ResNet) |
| Kalman update | <1 ms | 17 independent 6D filters, stack-allocated |
| **Total** | **~37-47 ms** | **Fits in 50 ms** |
---
## 4. Hardware Bill of Materials
| Component | Qty | Unit Cost | Purpose |
|-----------|-----|-----------|---------|
| ESP32-S3-DevKitC-1 | 4 | $10 | TX/RX sensing nodes |
| ESP32-S3-DevKitC-1 | 1 | $10 | Aggregator (or x86/RPi host) |
| External 5dBi antenna | 4-8 | $3 | Improved gain, directional coverage |
| USB-C hub (4 port) | 1 | $15 | Power distribution |
| Wall mount brackets | 4 | $2 | Ceiling/wall installation |
| **Total** | | **$73-91** | Complete 4-node mesh |
---
## 5. RuVector v2.0.4 Integration Map
All five published crates are exercised:
| Crate | Actions | Integration Point | Algorithmic Advantage |
|-------|---------|-------------------|----------------------|
| `ruvector-solver` | 2, 6 | Phase alignment; coherence matrix decomposition | O(√n) Neumann convergence |
| `ruvector-attention` | 2, 5, 8 | Cross-channel weighting; ring buffer; embedding similarity | Sublinear attention for small d |
| `ruvector-mincut` | 3, 4 | Viewpoint diversity partitioning; track assignment | O(n^1.5 log n) dynamic updates |
| `ruvector-attn-mincut` | 3, 7 | Cross-node spectrogram fusion; coherence gating | Attention + mincut in one pass |
| `ruvector-temporal-tensor` | 5 | Compressed sensing window ring buffer | 50-75% memory reduction |
---
## 6. IEEE 802.11bf Alignment
RuvSense's TDMA sensing schedule is forward-compatible with IEEE 802.11bf (WLAN Sensing, published 2024):
| RuvSense Concept | 802.11bf Equivalent |
|-----------------|---------------------|
| TX slot | Sensing Initiator |
| RX slot | Sensing Responder |
| TDMA cycle | Sensing Measurement Instance |
| NDP frame | Sensing NDP |
| Aggregator | Sensing Session Owner |
When commercial APs support 802.11bf, the ESP32 mesh can interoperate by translating SSP slots into 802.11bf Sensing Trigger frames.
---
## 7. Dependency Changes
### Firmware (C)
New files:
- `firmware/esp32-csi-node/main/sensing_schedule.h`
- `firmware/esp32-csi-node/main/sensing_schedule.c`
Modified files:
- `firmware/esp32-csi-node/main/csi_collector.c` (add channel hopping, link tagging)
- `firmware/esp32-csi-node/main/main.c` (add GPIO sync, TDMA timer)
### Rust
New module: `crates/wifi-densepose-signal/src/ruvsense/` (6 files, ~1500 lines estimated)
Modified files:
- `crates/wifi-densepose-signal/src/lib.rs` (export `ruvsense` module)
- `crates/wifi-densepose-signal/Cargo.toml` (no new deps; all ruvector crates already present per ADR-017)
- `crates/wifi-densepose-sensing-server/src/main.rs` (wire RuvSense pipeline into WebSocket output)
No new workspace dependencies. All ruvector crates are already in the workspace `Cargo.toml`.
---
## 8. Implementation Priority
| Priority | Actions | Weeks | Milestone |
|----------|---------|-------|-----------|
| P0 | 1 (firmware) | 2 | Channel-hopping ESP32 prototype |
| P0 | 2 (multi-band) | 2 | Wideband virtual frames |
| P1 | 3 (multistatic) | 2 | Multi-node fusion |
| P1 | 4 (tracker) | 1 | 17-keypoint Kalman |
| P1 | 6, 7 (coherence) | 1 | Gated updates |
| P2 | 5 (end-to-end) | 2 | 20 Hz pipeline |
| P2 | 8 (AETHER re-ID) | 1 | Identity hardening |
| P3 | 9 (docs) | 0.5 | This ADR finalized |
| **Total** | | **~10 weeks** | **Acceptance test** |
---
## 9. Consequences
### 9.1 Positive
- **3x bandwidth improvement** without hardware changes (channel hopping on existing ESP32)
- **12 independent viewpoints** from 4 commodity $10 nodes (C(4,2) × 2 links)
- **20 Hz update rate** with Kalman-smoothed output for sub-30mm torso jitter
- **Days-long stability** via coherence gating + SONA recalibration
- **All five ruvector crates exercised** — consistent algorithmic foundation
- **$73-91 total BOM** — accessible for research and production
- **802.11bf forward-compatible** — investment protected as commercial sensing arrives
- **Cognitum upgrade path** — same software stack, swap ESP32 for higher-bandwidth front end
### 9.2 Negative
- **4-node deployment** requires physical installation and calibration of node positions
- **TDMA scheduling** reduces per-node CSI rate (each node only transmits 1/4 of the time)
- **Channel hopping** introduces ~1-5ms gaps during `esp_wifi_set_channel()` transitions
- **5 GHz CSI on ESP32-S3** may not be available (ESP32-C6 supports it natively)
- **Coherence gate** may reject valid measurements during fast body motion (mitigation: gate only on static-subcarrier coherence)
### 9.3 Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| ESP32 channel hop causes CSI gaps | Medium | Reduced effective rate | Measure gap duration; increase dwell if >5ms |
| 5 GHz CSI unavailable on S3 | High | Lose frequency diversity | Fallback: 3-channel 2.4 GHz still provides 3x BW; ESP32-C6 for dual-band |
| Model inference >40ms | Medium | Miss 20 Hz target | Run model at 10 Hz; Kalman predict at 20 Hz interpolates |
| Two-person separation fails at 3 nodes | Low | Identity swaps | AETHER re-ID recovers; increase to 4-6 nodes |
| Coherence gate false-triggers | Low | Missed updates | Gate on environmental coherence only, not body-motion subcarriers |
---
## 10. Related ADRs
| ADR | Relationship |
|-----|-------------|
| ADR-012 | **Extended**: RuvSense adds TDMA multistatic to single-AP mesh |
| ADR-014 | **Used**: All 6 SOTA algorithms applied per-link |
| ADR-016 | **Extended**: New ruvector integration points for multi-link fusion |
| ADR-017 | **Extended**: Coherence gating adds temporal stability layer |
| ADR-018 | **Modified**: Firmware gains channel hopping, TDMA schedule, HT40 |
| ADR-022 | **Complementary**: RuvSense is the ESP32 equivalent of Windows multi-BSSID |
| ADR-024 | **Used**: AETHER embeddings for person re-identification |
| ADR-026 | **Reused**: Kalman + lifecycle infrastructure lifted to RuvSense |
| ADR-027 | **Used**: GeometryEncoder, HardwareNormalizer, FiLM conditioning |
---
## 11. References
1. IEEE 802.11bf-2024. "WLAN Sensing." IEEE Standards Association.
2. Geng, J., Huang, D., De la Torre, F. (2023). "DensePose From WiFi." arXiv:2301.00250.
3. Yan, K. et al. (2024). "Person-in-WiFi 3D." CVPR 2024, pp. 969-978.
4. Chen, L. et al. (2026). "PerceptAlign: Geometry-Aware WiFi Sensing." arXiv:2601.12252.
5. Kotaru, M. et al. (2015). "SpotFi: Decimeter Level Localization Using WiFi." SIGCOMM.
6. Zheng, Y. et al. (2019). "Zero-Effort Cross-Domain Gesture Recognition with Wi-Fi." MobiSys.
7. Zeng, Y. et al. (2019). "FarSense: Pushing the Range Limit of WiFi-based Respiration Sensing." MobiCom.
8. AM-FM (2026). "A Foundation Model for Ambient Intelligence Through WiFi." arXiv:2602.11200.
9. Espressif ESP-CSI. https://github.com/espressif/esp-csi

View File

@@ -1,364 +0,0 @@
# ADR-030: RuvSense Persistent Field Model — Longitudinal Drift Detection and Exotic Sensing Tiers
| Field | Value |
|-------|-------|
| **Status** | Proposed |
| **Date** | 2026-03-02 |
| **Deciders** | ruv |
| **Codename** | **RuvSense Field** — Persistent Electromagnetic World Model |
| **Relates to** | ADR-029 (RuvSense Multistatic), ADR-005 (SONA Self-Learning), ADR-024 (AETHER Embeddings), ADR-016 (RuVector Integration), ADR-026 (Survivor Track Lifecycle), ADR-027 (MERIDIAN Generalization) |
---
## 1. Context
### 1.1 Beyond Pose Estimation
ADR-029 establishes RuvSense as a sensing-first multistatic mesh achieving 20 Hz DensePose with <30mm jitter. That treats WiFi as a **momentary pose estimator**. The next leap: treat the electromagnetic field as a **persistent world model** that remembers, predicts, and explains.
The most exotic capabilities come from this shift in abstraction level:
- The room is the model, not the person
- People are structured perturbations to a baseline
- Changes are deltas from a known state, not raw measurements
- Time is a first-class dimension — the system remembers days, not frames
### 1.2 The Seven Capability Tiers
| Tier | Capability | Foundation |
|------|-----------|-----------|
| 1 | **Field Normal Modes** — Room electromagnetic eigenstructure | Baseline calibration + SVD |
| 2 | **Coarse RF Tomography** — 3D occupancy volume from link attenuations | Sparse tomographic inversion |
| 3 | **Intention Lead Signals** — Pre-movement prediction (200-500ms lead) | Temporal embedding trajectory analysis |
| 4 | **Longitudinal Biomechanics Drift** — Personal baseline deviation over days | Welford statistics + HNSW memory |
| 5 | **Cross-Room Continuity** — Identity persistence across spaces without optics | Environment fingerprinting + transition graph |
| 6 | **Invisible Interaction Layer** — Multi-user gesture control through walls/darkness | Per-person CSI perturbation classification |
| 7 | **Adversarial Detection** — Physically impossible signal identification | Multi-link consistency + field model constraints |
### 1.3 Signals, Not Diagnoses
RF sensing detects **biophysical proxies**, not medical conditions:
| Detectable Signal | Not Detectable |
|-------------------|---------------|
| Breathing rate variability | COPD diagnosis |
| Gait asymmetry shift (18% over 14 days) | Parkinson's disease |
| Posture instability increase | Neurological condition |
| Micro-tremor onset | Specific tremor etiology |
| Activity level decline | Depression or pain diagnosis |
The output is: "Your movement symmetry has shifted 18 percent over 14 days." That is actionable without being diagnostic. The evidence chain (stored embeddings, drift statistics, coherence scores) is fully traceable.
### 1.4 Acceptance Tests
**Tier 0 (ADR-029):** Two people, 20 Hz, 10 min stable tracks, zero ID swaps, <30mm torso jitter.
**Tier 1-4 (this ADR):** Seven-day run, no manual tuning. System flags one real environmental change and one real human drift event, produces traceable explanation using stored embeddings plus graph constraints.
**Tier 5-7 (appliance):** Thirty-day local run, no camera. Detects meaningful drift with <5% false alarm rate.
---
## 2. Decision
### 2.1 Implement Field Normal Modes as the Foundation
Add a `field_model` module to `wifi-densepose-signal/src/ruvsense/` that learns the room's electromagnetic baseline during unoccupied periods and decomposes all subsequent observations into environmental drift + body perturbation.
```
wifi-densepose-signal/src/ruvsense/
├── mod.rs // (existing, extend)
├── field_model.rs // NEW: Field normal mode computation + perturbation extraction
├── tomography.rs // NEW: Coarse RF tomography from link attenuations
├── longitudinal.rs // NEW: Personal baseline + drift detection
├── intention.rs // NEW: Pre-movement lead signal detector
├── cross_room.rs // NEW: Cross-room identity continuity
├── gesture.rs // NEW: Gesture classification from CSI perturbations
├── adversarial.rs // NEW: Physically impossible signal detection
└── (existing files...)
```
### 2.2 Core Architecture: The Persistent Field Model
```
Time
┌────────────────────────────────┐
│ Field Normal Modes (Tier 1) │
│ Room baseline + SVD modes │
│ ruvector-solver │
└────────────┬───────────────────┘
│ Body perturbation (environmental drift removed)
┌───────┴───────┐
│ │
▼ ▼
┌──────────┐ ┌──────────────┐
│ Pose │ │ RF Tomography│
│ (ADR-029)│ │ (Tier 2) │
│ 20 Hz │ │ Occupancy vol│
└────┬─────┘ └──────────────┘
┌──────────────────────────────┐
│ AETHER Embedding (ADR-024) │
│ 128-dim contrastive vector │
└────────────┬─────────────────┘
┌───────┼───────┐
│ │ │
▼ ▼ ▼
┌────────┐ ┌─────┐ ┌──────────┐
│Intention│ │Track│ │Cross-Room│
│Lead │ │Re-ID│ │Continuity│
│(Tier 3)│ │ │ │(Tier 5) │
└────────┘ └──┬──┘ └──────────┘
┌──────────────────────────────┐
│ RuVector Longitudinal Memory │
│ HNSW + graph + Welford stats│
│ (Tier 4) │
└──────────────┬───────────────┘
┌───────┴───────┐
│ │
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Drift Reports│ │ Adversarial │
│ (Level 1-3) │ │ Detection │
│ │ │ (Tier 7) │
└──────────────┘ └──────────────┘
```
### 2.3 Field Normal Modes (Tier 1)
**What it is:** The room's electromagnetic eigenstructure — the stable propagation paths, reflection coefficients, and interference patterns when nobody is present.
**How it works:**
1. During quiet periods (empty room, overnight), collect 10 minutes of CSI across all links
2. Compute per-link baseline (mean CSI vector)
3. Compute environmental variation modes via SVD (temperature, humidity, time-of-day effects)
4. Store top-K modes (K=3-5 typically captures >95% of environmental variance)
5. At runtime: subtract baseline, project out environmental modes, keep body perturbation
```rust
pub struct FieldNormalMode {
pub baseline: Vec<Vec<Complex<f32>>>, // [n_links × n_subcarriers]
pub environmental_modes: Vec<Vec<f32>>, // [n_modes × n_subcarriers]
pub mode_energies: Vec<f32>, // eigenvalues
pub calibrated_at: u64,
pub geometry_hash: u64,
}
```
**RuVector integration:**
- `ruvector-solver` → Low-rank SVD for mode extraction
- `ruvector-temporal-tensor` → Compressed baseline history storage
- `ruvector-attn-mincut` → Identify which subcarriers belong to which mode
### 2.4 Longitudinal Drift Detection (Tier 4)
**The defensible pipeline:**
```
RF → AETHER contrastive embedding
→ RuVector longitudinal memory (HNSW + graph)
→ Coherence-gated drift detection (Welford statistics)
→ Risk flag with traceable evidence
```
**Three monitoring levels:**
| Level | Signal Type | Example Output |
|-------|------------|----------------|
| **1: Physiological** | Raw biophysical metrics | "Breathing rate: 18.3 BPM today, 7-day avg: 16.1" |
| **2: Drift** | Personal baseline deviation | "Gait symmetry shifted 18% over 14 days" |
| **3: Risk correlation** | Pattern-matched concern | "Pattern consistent with increased fall risk" |
**Storage model:**
```rust
pub struct PersonalBaseline {
pub person_id: PersonId,
pub gait_symmetry: WelfordStats,
pub stability_index: WelfordStats,
pub breathing_regularity: WelfordStats,
pub micro_tremor: WelfordStats,
pub activity_level: WelfordStats,
pub embedding_centroid: Vec<f32>, // [128]
pub observation_days: u32,
pub updated_at: u64,
}
```
**RuVector integration:**
- `ruvector-temporal-tensor` → Compressed daily summaries (50-75% memory savings)
- HNSW → Embedding similarity search across longitudinal record
- `ruvector-attention` → Per-metric drift significance weighting
- `ruvector-mincut` → Temporal segmentation (detect changepoints in metric series)
### 2.5 Regulatory Classification
| Classification | What You Claim | Regulatory Path |
|---------------|---------------|-----------------|
| **Consumer wellness** (recommended first) | Activity metrics, breathing rate, stability score | Self-certification, FCC Part 15 |
| **Clinical decision support** (future) | Fall risk alert, respiratory pattern concern | FDA Class II 510(k) or De Novo |
| **Regulated medical device** (requires clinical partner) | Diagnostic claims for specific conditions | FDA Class II/III + clinical trials |
**Decision: Start as consumer wellness.** Build 12+ months of real-world longitudinal data. The dataset itself becomes the asset for future regulatory submissions.
---
## 3. Appliance Product Categories
### 3.1 Invisible Guardian
Wall-mounted wellness monitor for elderly care and independent living. No camera, no microphone, no reconstructable data. Stores embeddings and structural deltas only.
| Spec | Value |
|------|-------|
| Nodes | 4 ESP32-S3 pucks per room |
| Processing | Central hub (RPi 5 or x86) |
| Power | PoE or USB-C |
| Output | Risk flags, drift alerts, occupancy timeline |
| BOM | $73-91 (ESP32 mesh) + $35-80 (hub) |
| Validation | 30-day autonomous run, <5% false alarm rate |
### 3.2 Spatial Digital Twin Node
Live electromagnetic room model for smart buildings and workplace analytics.
| Spec | Value |
|------|-------|
| Output | Occupancy heatmap, flow vectors, dwell time, anomaly events |
| Integration | MQTT/REST API for BMS and CAFM |
| Retention | 30-day rolling, GDPR-compliant |
| Vertical | Smart buildings, retail, workspace optimization |
### 3.3 RF Interaction Surface
Multi-user gesture interface. No cameras. Works in darkness, smoke, through clothing.
| Spec | Value |
|------|-------|
| Gestures | Wave, point, beckon, push, circle + custom |
| Users | Up to 4 simultaneous |
| Latency | <100ms gesture recognition |
| Vertical | Smart home, hospitality, accessibility |
### 3.4 Pre-Incident Drift Monitor
Longitudinal biomechanics tracker for rehabilitation and occupational health.
| Spec | Value |
|------|-------|
| Baseline | 7-day calibration per person |
| Alert | Metric drift >2sigma for >3 days |
| Evidence | Stored embedding trajectory + statistical report |
| Vertical | Elderly care, rehab, occupational health |
### 3.5 Vertical Recommendation for First Hardware SKU
**Invisible Guardian** — the elderly care wellness monitor. Rationale:
1. Largest addressable market with immediate revenue (aging population, care facility demand)
2. Lowest regulatory bar (consumer wellness, no diagnostic claims)
3. Privacy advantage over cameras is a selling point, not a limitation
4. 30-day autonomous operation validates all tiers (field model, drift detection, coherence gating)
5. $108-171 BOM allows $299-499 retail with healthy margins
---
## 4. RuVector Integration Map (Extended)
All five crates are exercised across the exotic tiers:
| Tier | Crate | API | Role |
|------|-------|-----|------|
| 1 (Field) | `ruvector-solver` | `NeumannSolver` + SVD | Environmental mode decomposition |
| 1 (Field) | `ruvector-temporal-tensor` | `TemporalTensorCompressor` | Baseline history storage |
| 1 (Field) | `ruvector-attn-mincut` | `attn_mincut` | Mode-subcarrier assignment |
| 2 (Tomo) | `ruvector-solver` | `NeumannSolver` (L1) | Sparse tomographic inversion |
| 3 (Intent) | `ruvector-attention` | `ScaledDotProductAttention` | Temporal trajectory weighting |
| 3 (Intent) | `ruvector-temporal-tensor` | `CompressedCsiBuffer` | 2-second embedding history |
| 4 (Drift) | `ruvector-temporal-tensor` | `TemporalTensorCompressor` | Daily summary compression |
| 4 (Drift) | `ruvector-attention` | `ScaledDotProductAttention` | Metric drift significance |
| 4 (Drift) | `ruvector-mincut` | `DynamicMinCut` | Temporal changepoint detection |
| 5 (Cross-Room) | `ruvector-attention` | HNSW | Room and person fingerprint matching |
| 5 (Cross-Room) | `ruvector-mincut` | `MinCutBuilder` | Transition graph partitioning |
| 6 (Gesture) | `ruvector-attention` | `ScaledDotProductAttention` | Gesture template matching |
| 7 (Adversarial) | `ruvector-solver` | `NeumannSolver` | Physical plausibility verification |
| 7 (Adversarial) | `ruvector-attn-mincut` | `attn_mincut` | Multi-link consistency check |
---
## 5. Implementation Priority
| Priority | Tier | Module | Weeks | Dependency |
|----------|------|--------|-------|------------|
| P0 | 1 | `field_model.rs` | 2 | ADR-029 multistatic mesh operational |
| P0 | 4 | `longitudinal.rs` | 2 | Tier 1 baseline + AETHER embeddings |
| P1 | 2 | `tomography.rs` | 1 | Tier 1 perturbation extraction |
| P1 | 3 | `intention.rs` | 2 | Tier 1 + temporal embedding history |
| P2 | 5 | `cross_room.rs` | 2 | Tier 4 person profiles + multi-room deployment |
| P2 | 6 | `gesture.rs` | 1 | Tier 1 perturbation + per-person separation |
| P3 | 7 | `adversarial.rs` | 1 | Tier 1 field model + multi-link consistency |
**Total exotic tier: ~11 weeks after ADR-029 acceptance test passes.**
---
## 6. Consequences
### 6.1 Positive
- **Room becomes self-sensing**: Field normal modes provide a persistent baseline that explains change as structured deltas
- **7-day autonomous operation**: Coherence gating + SONA adaptation + longitudinal memory eliminate manual tuning
- **Privacy by design**: No images, no audio, no reconstructable data — only embeddings and statistical summaries
- **Traceable evidence**: Every drift alert links to stored embeddings, timestamps, and graph constraints
- **Multiple product categories**: Same software stack, different packaging — Guardian, Twin, Interaction, Drift Monitor
- **Regulatory clarity**: Consumer wellness first, clinical decision support later with accumulated dataset
- **Security primitive**: Coherence gating detects adversarial injection, not just quality issues
### 6.2 Negative
- **7-day calibration** required for personal baselines (system is less useful during initial period)
- **Empty-room calibration** needed for field normal modes (may not always be available)
- **Storage growth**: Longitudinal memory grows ~1 KB/person/day (manageable but non-zero)
- **Statistical power**: Drift detection requires 14+ days of data for meaningful z-scores
- **Multi-room**: Cross-room continuity requires hardware in all rooms (cost scales linearly)
### 6.3 Risks
| Risk | Probability | Impact | Mitigation |
|------|-------------|--------|------------|
| Field modes drift faster than expected | Medium | False perturbation detections | Reduce mode update interval from 24h to 4h |
| Personal baselines too variable | Medium | High false alarm rate for drift | Widen sigma threshold from 2σ to 3σ; require 5+ days |
| Cross-room matching fails for similar body types | Low | Identity confusion | Require temporal proximity (<60s) plus spatial adjacency |
| Gesture recognition insufficient SNR | Medium | <80% accuracy | Restrict to near-field (<2m) initially |
| Adversarial injection via coordinated WiFi injection | Very Low | Spoofed occupancy | Multi-link consistency check makes single-link spoofing detectable |
---
## 7. Related ADRs
| ADR | Relationship |
|-----|-------------|
| ADR-029 | **Prerequisite**: Multistatic mesh is the sensing substrate for all exotic tiers |
| ADR-005 (SONA) | **Extended**: SONA recalibration triggered by coherence gate → now also by drift events |
| ADR-016 (RuVector) | **Extended**: All 5 crates exercised across 7 exotic tiers |
| ADR-024 (AETHER) | **Critical dependency**: Embeddings are the representation for all longitudinal memory |
| ADR-026 (Tracking) | **Extended**: Track lifecycle now spans days (not minutes) for drift detection |
| ADR-027 (MERIDIAN) | **Used**: Room geometry encoding for field normal mode conditioning |
---
## 8. References
1. IEEE 802.11bf-2024. "WLAN Sensing." IEEE Standards Association.
2. FDA. "General Wellness: Policy for Low Risk Devices." Guidance Document, 2019.
3. EU MDR 2017/745. "Medical Device Regulation." Official Journal of the European Union.
4. Welford, B.P. (1962). "Note on a Method for Calculating Corrected Sums of Squares." Technometrics.
5. Chen, L. et al. (2026). "PerceptAlign: Geometry-Aware WiFi Sensing." arXiv:2601.12252.
6. AM-FM (2026). "A Foundation Model for Ambient Intelligence Through WiFi." arXiv:2602.11200.
7. Geng, J. et al. (2023). "DensePose From WiFi." arXiv:2301.00250.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

1
mobile/.env.example Normal file
View File

@@ -0,0 +1 @@
EXPO_PUBLIC_DEFAULT_SERVER_URL=http://192.168.1.100:8080

26
mobile/.eslintrc.js Normal file
View File

@@ -0,0 +1,26 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
plugins: ['@typescript-eslint', 'react', 'react-hooks'],
extends: [
'eslint:recommended',
'plugin:react/recommended',
'plugin:react-hooks/recommended',
'plugin:@typescript-eslint/recommended',
],
settings: {
react: {
version: 'detect',
},
},
rules: {
'react/react-in-jsx-scope': 'off',
},
};

41
mobile/.gitignore vendored Normal file
View File

@@ -0,0 +1,41 @@
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
# dependencies
node_modules/
# Expo
.expo/
dist/
web-build/
expo-env.d.ts
# Native
.kotlin/
*.orig.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
# Metro
.metro-health-check*
# debug
npm-debug.*
yarn-debug.*
yarn-error.*
# macOS
.DS_Store
*.pem
# local env files
.env*.local
# typescript
*.tsbuildinfo
# generated native folders
/ios
/android

4
mobile/.prettierrc Normal file
View File

@@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}

25
mobile/App.tsx Normal file
View File

@@ -0,0 +1,25 @@
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { StatusBar } from 'expo-status-bar';
export default function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>WiFi-DensePose</Text>
<StatusBar style="dark" />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#fff',
},
title: {
fontSize: 24,
fontWeight: '600',
},
});

12
mobile/app.config.ts Normal file
View File

@@ -0,0 +1,12 @@
export default {
name: 'WiFi-DensePose',
slug: 'wifi-densepose',
version: '1.0.0',
ios: {
bundleIdentifier: 'com.ruvnet.wifidensepose',
},
android: {
package: 'com.ruvnet.wifidensepose',
},
// Use expo-env and app-level defaults from the project configuration when available.
};

30
mobile/app.json Normal file
View File

@@ -0,0 +1,30 @@
{
"expo": {
"name": "mobile",
"slug": "mobile",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
"userInterfaceStyle": "light",
"splash": {
"image": "./assets/splash-icon.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"backgroundColor": "#E6F4FE",
"foregroundImage": "./assets/android-icon-foreground.png",
"backgroundImage": "./assets/android-icon-background.png",
"monochromeImage": "./assets/android-icon-monochrome.png"
},
"predictiveBackGestureEnabled": false
},
"web": {
"favicon": "./assets/favicon.png"
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
mobile/assets/favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
mobile/assets/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

9
mobile/babel.config.js Normal file
View File

@@ -0,0 +1,9 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
'react-native-reanimated/plugin'
]
};
};

View File

View File

View File

View File

View File

View File

View File

17
mobile/eas.json Normal file
View File

@@ -0,0 +1,17 @@
{
"cli": {
"version": ">= 4.0.0"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
}
}

4
mobile/index.ts Normal file
View File

@@ -0,0 +1,4 @@
import { registerRootComponent } from 'expo';
import App from './App';
registerRootComponent(App);

8
mobile/jest.config.js Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
preset: 'jest-expo',
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
testPathIgnorePatterns: ['/src/__tests__/'],
transformIgnorePatterns: [
'node_modules/(?!(expo|expo-.+|react-native|@react-native|react-native-webview|react-native-reanimated|react-native-svg|react-native-safe-area-context|react-native-screens|@react-navigation|@expo|@unimodules|expo-modules-core)/)',
],
};

11
mobile/jest.setup.ts Normal file
View File

@@ -0,0 +1,11 @@
jest.mock('@react-native-async-storage/async-storage', () =>
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
);
jest.mock('react-native-wifi-reborn', () => ({
loadWifiList: jest.fn(async () => []),
}));
jest.mock('react-native-reanimated', () =>
require('react-native-reanimated/mock')
);

16327
mobile/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

49
mobile/package.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "mobile",
"version": "1.0.0",
"main": "index.ts",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest",
"lint": "eslint ."
},
"dependencies": {
"@expo/vector-icons": "^15.0.2",
"@react-native-async-storage/async-storage": "2.2.0",
"@react-navigation/bottom-tabs": "^7.15.3",
"@react-navigation/native": "^7.1.31",
"axios": "^1.13.6",
"expo": "~55.0.4",
"expo-status-bar": "~55.0.4",
"react": "19.2.0",
"react-native": "0.83.2",
"react-native-gesture-handler": "~2.30.0",
"react-native-reanimated": "4.2.1",
"react-native-safe-area-context": "~5.6.2",
"react-native-screens": "~4.23.0",
"react-native-svg": "15.15.3",
"react-native-webview": "13.16.0",
"react-native-wifi-reborn": "^4.13.6",
"victory-native": "^41.20.2",
"zustand": "^5.0.11"
},
"devDependencies": {
"@testing-library/jest-native": "^5.4.3",
"@testing-library/react-native": "^13.3.3",
"@types/jest": "^30.0.0",
"@types/react": "~19.2.2",
"@typescript-eslint/eslint-plugin": "^8.56.1",
"@typescript-eslint/parser": "^8.56.1",
"babel-preset-expo": "^55.0.10",
"eslint": "^10.0.2",
"jest": "^30.2.0",
"jest-expo": "^55.0.9",
"prettier": "^3.8.1",
"react-native-worklets": "^0.7.4",
"typescript": "~5.9.2"
},
"private": true
}

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

@@ -0,0 +1,5 @@
describe('placeholder', () => {
it('passes', () => {
expect(true).toBe(true);
});
});

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

View File

Some files were not shown because too many files have changed in this diff Show More