Files
ruvnet--RuView/docs/adr/ADR-275-rf-aware-gaussian-spatial-memory.md
T
Claude a1a59baf72 feat(ruview-unified): unified RF spatial world model P1 (ADR-273..278)
One shared representation instead of another isolated RF classifier: new
v2 leaf crate ruview-unified implementing all five ADR-273 pillars, plus
six ADRs with measured, grade-labeled results.

- Canonical RfTensor + fail-closed hardware adapter registry (802.11 CSI
  via wifi-densepose-core::CsiFrame, FMCW radar cubes, UWB CIR, 5G SRS);
  shared layout/gain/phase normalization proven by tests (ADR-274).
- Universal RF foundation encoder: CFO-aligned, median-scaled tokenizer;
  masked-reconstruction pretraining with hand-derived backprop verified
  against central finite differences (max rel err 1.31e-5 over all 12
  parameter groups); fusion contract z = Enc ⊙ σ(AgeEnc) + GeomEnc;
  task adapters under the 1% budget (129/268/387/2 params vs 40,856
  backbone), enforced by test.
- RF-aware Gaussian spatial memory: anisotropic primitives with per-band
  reflectivity, confidence-weighted fusion, decay, spatial-hash/semantic
  queries, closed-form Beer-Lambert channel gain (exact Friis on empty
  map), inverse gain updates (unseen 6.1 dB wall learned to <0.5 dB in
  20 observations), task-gated scene graph (ADR-275).
- Physics-guided synthetic RF worlds: image-method multipath (order ≤2),
  complex-permittivity Fresnel materials, emergent Doppler proven against
  the analytic phase rate, seeded ChaCha20 randomization of physics and
  hardware nuisances; byte-deterministic per seed (ADR-276).
- Edge sensing control plane: 802.11bf/ETSI-ISAC-aligned purposes/zones,
  fail-closed authorization, double-gated identity, retention bounds;
  BoundedEvent-only trust boundary makes raw RF export unrepresentable
  (ADR-277). Radar inverse rendering stays a gated research program
  (ADR-278, no code by design).

Anti-leakage acceptance pipeline (strict splits by room/day/person/
chipset/firmware/layout with independent disjointness verification):
presence F1 1.00 on held-out rooms and held-out chipset, degradation
0.0, ECE 0.012, p95 latency 2.0 ms debug / 105 µs release — ALL
SYNTHETIC until P2 real-data validation.

Benchmarks + optimization pass: channel_gain 139→27 µs (O(1) in map
size via segment-corridor AABB sweep), observe_link 305→74 µs, DFT
twiddle plan 4.9x; hash/linear crossover (~4k Gaussians) reported
honestly.

Tests: ruview-unified 66 unit + 3 acceptance, 0 failed; workspace
3,771 passed 0 failed (--exclude wifi-densepose-desktop: GTK headers
unavailable in this container). Python proof: VERDICT PASS. Also
gitignore sensing-server test-run artifacts (incl. generated
session-secret).

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01Q1R5zhz6sSfXGRXpgBwpFX
2026-07-26 19:03:39 +00:00

7.0 KiB
Raw Blame History

ADR-275: RF-aware Gaussian spatial memory — the persistent scene representation

Field Value
Status Accepted — P1 implemented (ruview-unified/src/gaussian/: primitive.rs, map.rs, gain.rs, graph.rs; 16 unit tests, criterion benches)
Date 2026-07-26
Parent ADR-273
Relates to ADR-030 (persistent field model — superseded in direction by this), ADR-134 (CIR/ISTA), ADR-147 (OccWorld priors), ADR-261 (RuVector graph-ANN — the retrieval layer this memory will index into)

0. PROOF discipline

Grades per ADR-273 §0. The July 2026 external motivators (EmbodiedSplat ~5 fps online semantic Gaussian mapping, ~67× memory efficiency; TGSFormer bounded temporal Gaussian memory; physics-informed channel-gain mapping with incremental Gaussian insertion; JITOMA task-gated activation) are EXTERNAL-UNVERIFIED throughout.

1. Context

RuView's spatial state is currently scattered (pose tracker state, field-model eigenstructure, worldgraph tracks). Vision-side SOTA converged on Gaussian fields as the common continuous scene memory, and — the July signal that matters here — the representation crossed into RF: propagation geometry, opacity, attenuation, and scattering as Gaussian primitives, updated incrementally when the environment changes. That is exactly the bridge from RuView sensing to a queryable digital twin: one store that answers both geometric questions ("what is near the sofa") and RF questions ("which object caused the channel anomaly", "where did multipath change").

2. Decision — the primitive

RfGaussian (primitive.rs) carries all six ADR-273 attribute groups:

  1. Geometry: position, per-axis scale (σ), unit-quaternion orientation → anisotropic metric Σ⁻¹ = R·diag(1/σ²)·Rᵀ.
  2. Semantics: 16-d embedding (RuVector-alignable).
  3. RF response: reflectivity [4 bands × 4 incident-angle bins] (2.4/5/6/60 GHz), plus occupancy = peak extinction coefficient (nepers/m) used by the gain model.
  4. Motion: signed Doppler m/s + {Static, Slow, Fast} class.
  5. Trust/lifecycle: confidence ∈ [0,1], timestamp, decay τ, Provenance {device, model_version, synthetic}.
  6. Links: typed references into the scene graph / RuVector entities.

Validated constructor (quaternion normalized, ranges checked); anisotropy and rotation are proven behaviorally (thin axis decays ≥ 80× faster at 0.3 m — the analytic ratio is 86; a 90° quaternion rotates the metric with it).

3. Decision — the map

GaussianMap (map.rs): spatial-hash grid (1 m default pitch) over a flat store.

  • Fusion, not accumulation: an insert within Mahalanobis² 9 of a same-entity-kind Gaussian merges — confidence-weighted position/scale/occupancy/semantics/reflectivity/Doppler, noisy-OR confidence (c₁+c₂−c₁c₂), newest provenance wins, links union. Test: two 0.5-confidence observations 0.1 m apart fuse to one Gaussian at the weighted midpoint with confidence 0.75.
  • Decay: exponential confidence decay per Gaussian τ; prune below 0.02; deterministic (replay test).
  • Queries: radius (hash + linear reference impl, equivalence-tested on 100-Gaussian grids), kNN (expanding ring), semantic cosine top-k, and the segment-corridor query below.

4. Decision — channel gain as a first-class query + inverse update

gain.rs implements the RF query surface:

H(tx,rx,f) = (λ/4πd)·e^{-j2πd/λ} · exp(−Σ_g occ_g·I_g)

with I_g the closed-form line integral of each Gaussian's density along the TX→RX segment (1-D Gaussian integral via erf; derivation in the module doc).

Exactness anchors (MEASURED-CODE):

  • Empty map ⇒ exact Friis amplitude (< 1e-15) and propagation phase (empty_map_returns_exact_friis).
  • Closed-form line integral matches 1 mm trapezoid quadrature through a rotated anisotropic Gaussian to < 1e-6 (line_integral_matches_numeric_quadrature).
  • On-path absorber attenuates strictly monotonically in occupancy; a 10σ off-path absorber changes LoS gain < 1e-6 dB.

Inverse update (observe_link) — the incremental-mapping move: measured link amplitude → target optical depth τ* = ln(friis/measured); a projected-gradient step distributes the residual over intersected Gaussians proportional to their path integrals (exact Newton along the link at lr = 1), clamped at occupancy ≥ 0; if nothing intersects and attenuation is demanded, a compact absorber is spawned at the midpoint sized to close the residual. Measured: from an empty map, 20 observations of a link with an unseen 0.7-neper (≈6.1 dB) obstruction converge to < 0.06 neper residual and < 0.5 dB prediction error (inverse_update_learns_a_wall_from_link_residuals).

5. Decision — task-gated scene graph

graph.rs: sparse typed nodes (Object/Room/PersonClass/Device/Event — person classes only; identity lives behind ADR-277's double gate) and relations (Contains/Near/CausedBy/ObservedBy). The only sanctioned read is activate(relevant_kinds, seeds, max_nodes) — bounded BFS that reports truncation instead of silently scanning (the JITOMA lesson). Tests: an "which object caused the anomaly" activation pulls exactly {event, object, room} and gates out devices/person-classes; the node budget is enforced and truncation is flagged.

6. Performance (criterion, release, this machine)

Benchmark Result Note
channel_gain, 1 k Gaussians 26.9 µs was 139 µs with the midpoint-ball candidate query
channel_gain, 16 k Gaussians 27.7 µs ~O(1) in map size after the corridor rewrite
segment corridor query, hash vs linear 24 µs vs 6 µs (1 k) / 24 µs vs 163 µs (16 k) crossover ≈ 4 k Gaussians — reported honestly; both paths kept + equivalence-tested
radius query, hash vs linear 4.3 µs vs 101 µs @ 16 k (23×) hash loses at 1 k (4.0 vs 1.9 µs) — small maps are brute-force territory
observe_link inverse update 74 µs was 305 µs pre-optimization
map insert+fuse (64 Gaussians, in observe bench setup) included above

The optimization pass replaced a midpoint-ball candidate search ((2·(L/2+3)+1)³ ≈ 9,300 cell lookups on a 14 m link) with an AABB sweep prefiltered by cell-centre-to-segment distance (bound margin + √3/2·cell), after a first corridor attempt (per-sample cube inserts into a BTreeSet) measured worse (1.2 ms) and was discarded — kept in this record as the honest negative result.

7. Consequences

  • The map answers "where is a person likely", "where did multipath change", and "which object caused a channel anomaly" (gain residual → CausedBy edge) from one store.
  • RuVector integration (ADR-261) becomes: vector search retrieves candidate Gaussians/nodes → graph traversal enforces relations → the gain model verifies answers against geometry. The LLM plans the query; it never invents the spatial answer.
  • Not yet done (P3): live wiring into wifi-densepose-sensing-server, visual/depth Gaussian ingestion, and RuVector index sync.