Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8658cc3

Browse files
committed
docs: improve RuvSense domain model and add DDD index
- Add intro explaining DDD purpose and bounded context overview table - Add Edge Intelligence bounded context (ruvnet#7) for on-device sensing - Add ubiquitous language terms: Edge Tier, WASM Module - Fix frame rate 20 Hz -> 28 Hz (measured on hardware) - Link each context to its source files and ADRs - Add NVS configuration table and invariants for edge processing - Create docs/ddd/README.md introducing all 3 domain models - Update main README docs table to link to DDD index Co-Authored-By: claude-flow <[email protected]>
1 parent 2e9b34e commit 8658cc3

3 files changed

Lines changed: 208 additions & 5 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ docker run -p 3000:3000 ruvnet/wifi-densepose:latest
5050
| [User Guide](docs/user-guide.md) | Step-by-step guide: installation, first run, API usage, hardware setup, training |
5151
| [Build Guide](docs/build-guide.md) | Building from source (Rust and Python) |
5252
| [Architecture Decisions](docs/adr/README.md) | 44 ADRs — why each technical choice was made, organized by domain (hardware, signal processing, ML, platform, infrastructure) |
53-
| [DDD Domain Model](docs/ddd/ruvsense-domain-model.md) | RuvSense bounded contexts, aggregates, domain events, and ubiquitous language |
53+
| [Domain Models](docs/ddd/README.md) | 3 DDD models (RuvSense, WiFi-Mat, CHCI) — bounded contexts, aggregates, domain events, and ubiquitous language |
5454

5555
---
5656

docs/ddd/README.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Domain Models
2+
3+
This folder contains Domain-Driven Design (DDD) specifications for each major subsystem in RuView.
4+
5+
DDD organizes the codebase around the problem being solved — not around technical layers. Each *bounded context* owns its own data, rules, and language. Contexts communicate through domain events, not by sharing mutable state. This makes the system easier to reason about, test, and extend — whether you're a person or an AI agent.
6+
7+
## Models
8+
9+
| Model | What it covers | Bounded Contexts |
10+
|-------|---------------|------------------|
11+
| [RuvSense](ruvsense-domain-model.md) | Multistatic WiFi sensing, pose tracking, vital signs, edge intelligence | 7 contexts: Sensing, Coherence, Tracking, Field Model, Longitudinal, Spatial Identity, Edge Intelligence |
12+
| [WiFi-Mat](wifi-mat-domain-model.md) | Disaster response: survivor detection, START triage, mass casualty assessment | Scan Zone, Survivor Tracking, Triage |
13+
| [CHCI](chci-domain-model.md) | Coherent Human Channel Imaging: sub-millimeter body surface reconstruction | Sounding, Channel Estimation, Imaging |
14+
15+
## How to read these
16+
17+
Each model defines:
18+
19+
- **Ubiquitous Language** — Terms with precise meanings used in both code and conversation
20+
- **Bounded Contexts** — Independent subsystems with clear responsibilities and boundaries
21+
- **Aggregates** — Clusters of objects that enforce business rules (e.g., a PoseTrack owns its keypoints)
22+
- **Value Objects** — Immutable data with meaning (e.g., a CoherenceScore is not just a float)
23+
- **Domain Events** — Things that happened that other contexts may care about
24+
- **Invariants** — Rules that must always be true (e.g., "drift alert requires >2sigma for >3 days")
25+
- **Anti-Corruption Layers** — Adapters that translate between contexts without leaking internals
26+
27+
## Related
28+
29+
- [Architecture Decision Records](../adr/README.md) — Why each technical choice was made
30+
- [User Guide](../user-guide.md) — Setup and API reference

docs/ddd/ruvsense-domain-model.md

Lines changed: 177 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,32 @@
11
# RuvSense Domain Model
22

3+
RuvSense is the multistatic WiFi sensing subsystem of RuView. It turns raw radio signals from multiple ESP32 sensors into tracked human poses, vital signs, and spatial awareness — all without cameras.
4+
5+
This document defines the system using [Domain-Driven Design](https://martinfowler.com/bliki/DomainDrivenDesign.html) (DDD): bounded contexts that own their data and rules, aggregate roots that enforce invariants, value objects that carry meaning, and domain events that connect everything. The goal is to make the system's structure match the physics it models — so that anyone reading the code (or an AI agent modifying it) understands *why* each piece exists, not just *what* it does.
6+
7+
**Bounded Contexts:**
8+
9+
| # | Context | Responsibility | Key ADRs | Code |
10+
|---|---------|----------------|----------|------|
11+
| 1 | [Multistatic Sensing](#1-multistatic-sensing-context) | Collect and fuse CSI from multiple nodes and channels | [ADR-029](../adr/ADR-029-ruvsense-multistatic-sensing-mode.md) | `signal/src/ruvsense/{multiband,phase_align,multistatic}.rs` |
12+
| 2 | [Coherence](#2-coherence-context) | Monitor signal quality, gate bad data | [ADR-029](../adr/ADR-029-ruvsense-multistatic-sensing-mode.md) | `signal/src/ruvsense/{coherence,coherence_gate}.rs` |
13+
| 3 | [Pose Tracking](#3-pose-tracking-context) | Track people as persistent skeletons with re-ID | [ADR-024](../adr/ADR-024-contrastive-csi-embedding-model.md), [ADR-037](../adr/ADR-037-multi-person-pose-detection.md) | `signal/src/ruvsense/pose_tracker.rs` |
14+
| 4 | [Field Model](#4-field-model-context) | Learn room baselines, extract body perturbations | [ADR-030](../adr/ADR-030-ruvsense-persistent-field-model.md) | `signal/src/ruvsense/{field_model,tomography}.rs` |
15+
| 5 | [Longitudinal Monitoring](#5-longitudinal-monitoring-context) | Track health trends over days/weeks | [ADR-030](../adr/ADR-030-ruvsense-persistent-field-model.md) | `signal/src/ruvsense/longitudinal.rs` |
16+
| 6 | [Spatial Identity](#6-spatial-identity-context) | Cross-room tracking via environment fingerprints | [ADR-030](../adr/ADR-030-ruvsense-persistent-field-model.md) | `signal/src/ruvsense/cross_room.rs` |
17+
| 7 | [Edge Intelligence](#7-edge-intelligence-context) | On-device sensing (no server needed) | [ADR-039](../adr/ADR-039-esp32-edge-intelligence.md), [ADR-040](../adr/ADR-040-wasm-programmable-sensing.md) | `firmware/esp32-csi-node/main/edge_processing.c` |
18+
19+
All code paths shown are relative to `rust-port/wifi-densepose-rs/crates/wifi-densepose-` unless otherwise noted.
20+
21+
---
22+
323
## Domain-Driven Design Specification
424

525
### Ubiquitous Language
626

727
| Term | Definition |
828
|------|------------|
9-
| **Sensing Cycle** | One complete TDMA round (all nodes TX once): 50ms at 20 Hz |
29+
| **Sensing Cycle** | One complete TDMA round (all nodes TX once): ~35ms at 28.5 Hz (measured) |
1030
| **Link** | A single TX-RX pair; with N nodes there are N×(N-1) directed links |
1131
| **Multi-Band Frame** | Fused CSI from one node hopping across multiple channels in one dwell cycle |
1232
| **Fused Sensing Frame** | Aggregated observation from all nodes at one sensing cycle, ready for inference |
@@ -15,6 +35,8 @@
1535
| **Pose Track** | A temporally persistent per-person 17-keypoint trajectory with Kalman state |
1636
| **Track Lifecycle** | State machine: Tentative → Active → Lost → Terminated |
1737
| **Re-ID Embedding** | 128-dim AETHER contrastive vector encoding body identity |
38+
| **Edge Tier** | Processing level on the ESP32: 0 = raw passthrough, 1 = signal cleanup, 2 = vitals, 3 = WASM modules |
39+
| **WASM Module** | A small program compiled to WebAssembly that runs on the ESP32 for custom on-device sensing |
1840
| **Node** | An ESP32-S3 device acting as both TX and RX in the multistatic mesh |
1941
| **Aggregator** | Central device (ESP32/RPi/x86) that collects CSI from all nodes and runs fusion |
2042
| **Sensing Schedule** | TDMA slot assignment: which node transmits when |
@@ -194,7 +216,7 @@
194216
**Domain Services:**
195217
- `PersonSeparationService` — Min-cut partitioning of cross-link correlation graph
196218
- `TrackAssignmentService` — Bipartite matching of detections to existing tracks
197-
- `KalmanPredictionService` — Predict step at 20 Hz (decoupled from measurement rate)
219+
- `KalmanPredictionService` — Predict step at 28 Hz (decoupled from measurement rate)
198220
- `KalmanUpdateService` — Gated measurement update (subject to coherence gate)
199221
- `EmbeddingIdentifierService` — AETHER cosine similarity for re-ID
200222

@@ -575,7 +597,7 @@ pub trait MeshRepository {
575597
### Multistatic Sensing
576598
- At least 2 nodes must be active for multistatic fusion (fallback to single-node mode otherwise)
577599
- Channel hop sequence must contain at least 1 non-overlapping channel
578-
- TDMA cycle period must be ≤50ms for 20 Hz output
600+
- TDMA cycle period must be ≤50ms for 28 Hz output
579601
- Guard interval must be ≥2× clock drift budget (≥1ms for 50ms cycle)
580602

581603
### Coherence
@@ -1005,7 +1027,7 @@ pub trait SpatialIdentityRepository {
10051027
### Extended Invariants
10061028

10071029
#### Field Model
1008-
- Baseline calibration requires ≥10 minutes of empty-room CSI (≥12,000 frames at 20 Hz)
1030+
- Baseline calibration requires ≥10 minutes of empty-room CSI (≥12,000 frames at 28 Hz)
10091031
- Environmental modes capped at K=5 (more modes overfit to noise)
10101032
- Tomographic inversion only valid with ≥8 links (4 nodes minimum)
10111033
- Baseline expires after 24 hours if not refreshed during quiet period
@@ -1025,3 +1047,154 @@ pub trait SpatialIdentityRepository {
10251047
- Transition graph is append-only (immutable audit trail)
10261048
- No image data stored — only 128-dim embeddings and structural events
10271049
- Maximum 100 rooms indexed per deployment (HNSW scaling constraint)
1050+
1051+
---
1052+
1053+
## Part III: Edge Intelligence Bounded Context (ADR-039, ADR-040, ADR-041)
1054+
1055+
### 7. Edge Intelligence Context
1056+
1057+
**Responsibility:** Run signal processing and sensing algorithms directly on the ESP32-S3, without requiring a server. The node detects presence, measures breathing and heart rate, alerts on falls, and runs custom WASM modules — all locally with instant response.
1058+
1059+
This is the only bounded context that runs on the microcontroller rather than the aggregator. It operates independently: the server is optional for visualization, but the ESP32 handles real-time sensing on its own.
1060+
1061+
```
1062+
┌──────────────────────────────────────────────────────────┐
1063+
│ Edge Intelligence Context │
1064+
│ (runs on ESP32-S3, Core 1) │
1065+
├──────────────────────────────────────────────────────────┤
1066+
│ │
1067+
│ ┌───────────────┐ ┌───────────────┐ │
1068+
│ │ Phase │ │ Welford │ │
1069+
│ │ Extractor │ │ Variance │ │
1070+
│ │ (I/Q → φ, │ │ Tracker │ │
1071+
│ │ unwrap) │ │ (per-subk) │ │
1072+
│ └───────┬───────┘ └───────┬───────┘ │
1073+
│ │ │ │
1074+
│ └────────┬───────────┘ │
1075+
│ ▼ │
1076+
│ ┌────────────────┐ │
1077+
│ │ Top-K Select │ │
1078+
│ │ + Bandpass │ │
1079+
│ │ (breathing: │ │
1080+
│ │ 0.1-0.5 Hz, │ │
1081+
│ │ HR: 0.8-2 Hz) │ │
1082+
│ └────────┬───────┘ │
1083+
│ ▼ │
1084+
│ ┌─────────────┼─────────────┐ │
1085+
│ ▼ ▼ ▼ │
1086+
│ ┌────────┐ ┌──────────┐ ┌──────────┐ │
1087+
│ │Presence│ │ Vitals │ │ Fall │ │
1088+
│ │Detector│ │ (BPM via │ │ Detector │ │
1089+
│ │(motion │ │ zero- │ │ (phase │ │
1090+
│ │ energy)│ │ crossing)│ │ accel) │ │
1091+
│ └────┬───┘ └────┬─────┘ └────┬─────┘ │
1092+
│ └───────────┼──────────────┘ │
1093+
│ ▼ │
1094+
│ ┌────────────────┐ │
1095+
│ │ Vitals Packet │──▶ UDP 32-byte (0xC5110002) │
1096+
│ │ Assembler │ at 1 Hz to aggregator │
1097+
│ └────────┬───────┘ │
1098+
│ │ │
1099+
│ ┌────────▼───────┐ │
1100+
│ │ WASM3 Runtime │ │
1101+
│ │ (Tier 3: hot- │──▶ Custom module outputs │
1102+
│ │ loadable │ │
1103+
│ │ modules) │ │
1104+
│ └────────────────┘ │
1105+
│ │
1106+
└──────────────────────────────────────────────────────────┘
1107+
```
1108+
1109+
**Aggregates:**
1110+
- `EdgeProcessingState` (Aggregate Root) — Holds all per-subcarrier state, filter history, and detection flags
1111+
1112+
**Value Objects:**
1113+
- `VitalsPacket` — 32-byte UDP packet: presence, motion, breathing BPM, heart rate BPM, confidence, fall flag, occupancy
1114+
- `EdgeTier` — Off (0) / BasicSignal (1) / FullVitals (2) / WasmExtended (3)
1115+
- `PresenceState` — Empty / Present / Moving
1116+
- `BandpassOutput` — Filtered signal in breathing or heart rate band
1117+
- `FallAlert` — Phase acceleration exceeding configurable threshold
1118+
1119+
**Entities:**
1120+
- `WasmModule` — A loaded WASM binary with its own memory arena (160 KB), frame budget (10 ms), and timer interval
1121+
1122+
**Domain Services:**
1123+
- `PhaseExtractionService` — Converts raw I/Q to unwrapped phase per subcarrier
1124+
- `VarianceTrackingService` — Welford running stats for subcarrier selection
1125+
- `TopKSelectionService` — Picks highest-variance subcarriers for downstream analysis
1126+
- `BandpassFilterService` — Biquad IIR filters for breathing (0.1-0.5 Hz) and heart rate (0.8-2.0 Hz)
1127+
- `PresenceDetectionService` — Adaptive threshold calibration (3-sigma over 1200-frame window)
1128+
- `VitalSignService` — Zero-crossing BPM estimation from filtered phase signals
1129+
- `FallDetectionService` — Phase acceleration exceeding threshold triggers alert
1130+
- `WasmRuntimeService` — WASM3 interpreter: load, execute, and sandbox custom modules
1131+
1132+
**NVS Configuration (runtime, no reflash needed):**
1133+
1134+
| Key | Type | Default | Purpose |
1135+
|-----|------|---------|---------|
1136+
| `edge_tier` | u8 | 0 | Processing tier (0/1/2/3) |
1137+
| `pres_thresh` | u16 | 0 | Presence threshold (0 = auto-calibrate) |
1138+
| `fall_thresh` | u16 | 2000 | Fall detection threshold (rad/s^2 x 1000) |
1139+
| `vital_win` | u16 | 256 | Phase history window (frames) |
1140+
| `vital_int` | u16 | 1000 | Vitals packet interval (ms) |
1141+
| `subk_count` | u8 | 8 | Top-K subcarrier count |
1142+
| `wasm_max` | u8 | 4 | Max concurrent WASM modules |
1143+
| `wasm_verify` | u8 | 0 | Require Ed25519 signature for uploads |
1144+
1145+
**Implementation files:**
1146+
- `firmware/esp32-csi-node/main/edge_processing.c` — DSP pipeline (~750 lines)
1147+
- `firmware/esp32-csi-node/main/edge_processing.h` — Types and API
1148+
- `firmware/esp32-csi-node/main/nvs_config.c` — NVS key reader (20 keys)
1149+
- `firmware/esp32-csi-node/provision.py` — CLI provisioning tool
1150+
1151+
**Invariants:**
1152+
- Edge processing runs on Core 1; WiFi and CSI callbacks run on Core 0 (no contention)
1153+
- CSI data flows from Core 0 to Core 1 via a lock-free SPSC ring buffer
1154+
- UDP sends are rate-limited to 50 Hz to prevent lwIP buffer exhaustion (Issue #127)
1155+
- ENOMEM backoff suppresses sends for 100 ms if lwIP runs out of packet buffers
1156+
- WASM modules are sandboxed: 160 KB arena, 10 ms frame budget, no direct hardware access
1157+
- Tier changes via NVS take effect on next reboot — no hot-reconfiguration of the DSP pipeline
1158+
- Fall detection threshold should be tuned per deployment (default 2000 causes false positives in static environments)
1159+
1160+
**Domain Events:**
1161+
```rust
1162+
pub enum EdgeEvent {
1163+
/// Presence state changed
1164+
PresenceChanged {
1165+
node_id: u8,
1166+
state: PresenceState, // Empty / Present / Moving
1167+
motion_energy: f32,
1168+
timestamp_ms: u32,
1169+
},
1170+
1171+
/// Fall detected on-device
1172+
FallDetected {
1173+
node_id: u8,
1174+
acceleration: f32, // rad/s^2
1175+
timestamp_ms: u32,
1176+
},
1177+
1178+
/// Vitals packet emitted
1179+
VitalsEmitted {
1180+
node_id: u8,
1181+
breathing_bpm: f32,
1182+
heart_rate_bpm: f32,
1183+
confidence: f32,
1184+
timestamp_ms: u32,
1185+
},
1186+
1187+
/// WASM module loaded or failed
1188+
WasmModuleLoaded {
1189+
slot: u8,
1190+
module_name: String,
1191+
success: bool,
1192+
timestamp_ms: u32,
1193+
},
1194+
}
1195+
```
1196+
1197+
**Relationship to other contexts:**
1198+
- Edge Intelligence → Multistatic Sensing: **Alternative** (edge runs on-device; multistatic runs on aggregator — same physics, different compute location)
1199+
- Edge Intelligence → Pose Tracking: **Upstream** (edge provides presence/vitals; aggregator can skip detection if edge already confirmed occupancy)
1200+
- Edge Intelligence → Coherence: **Simplified** (edge uses simple variance thresholds instead of full coherence gating)

0 commit comments

Comments
 (0)