Web Serial-based wizard that takes a non-technical user from unboxed
ESP32-S3 to streaming CSI in under 5 minutes. 8-step state machine:
browser check → connect → flash firmware (esp-web-tools) → provision
WiFi → detect node → guided calibration → placement guidance → complete.
Includes sessionStorage persistence for resumability across page refreshes,
live CSI waveform during calibration, human-friendly error messages for
all failure modes, and comprehensive Jest test coverage (34 tests).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements the CSI recording buffer for time-travel debugging. Each link
writes to append-only 1-hour segment files under data/csi/{linkID}/
with configurable 48-hour retention and 1GB/link max size guard. The
recorder uses buffered channels (capacity 1000) per link so Write never
blocks the ingestion goroutine. Background cleanup sweeps hourly to
delete expired segment files.
New package: mothership/internal/recorder/
- segment.go: append-only segment file I/O with [length][timestamp][frame] records
- manager.go: Manager with Write/ReadFrom/AvailableRange/Close, per-link goroutines
- Full test coverage: 18 tests covering write/read, retention cleanup, max bytes,
concurrent writes, buffer overflow drops, segment rotation, and edge cases
Wire-up: recorder.Manager created in main.go and injected into ingestion server.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add 500ms periodic presence_update broadcast from the dashboard hub with
per-link motion state (is_motion, delta_rms, confidence). Surface this in
a new Presence panel on the dashboard with coloured dot indicators
(green=clear, amber=motion, red=high-confidence) and deltaRMS values.
Includes a rolling 10s Canvas 2D line chart of deltaRMS with a threshold
line at 0.02 for the selected link.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements plan item 17 — full Three.js Phase 3 visualization layer:
- Room bounds: floor, ceiling, semi-transparent walls and wireframe edges
built from registry_state room config (width/depth/height/origin)
- Floor plan texture: upload any image via status-bar button; mapped to
ground plane via THREE.TextureLoader
- Humanoid figures: SkinnedMesh + AnimationMixer with 13-bone skeleton;
four AnimationClip postures (standing, walking, seated, lying); smooth
0.35 s crossfade transitions; walking speed scales animation timeScale;
figures orient in direction of travel
- Vertical pillar anchors: line from floor to ceiling at each blob position
- Footprint trails: floor-level Line geometry following blob.trail history
- Node meshes: OctahedronGeometry at registry node 3-D positions; link
lines redrawn whenever link_active/inactive events arrive
- View presets: 3D perspective, top-down orthographic, first-person follow
(lerps camera to track first active blob)
- WebSocket: handles registry_state, loc_update, link_inactive message types
newly routed through handleJSONMessage; existing CSI/motion pipeline intact
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New package mothership/internal/tracker/ implementing full 3-D
Unscented Kalman Filter tracking for human figures detected by the
fusion engine.
Key features:
- 6-D UKF state [x, y, z, vx, vy, vz] using gonum.org/v1/gonum/mat
- Biomechanical constraints: max horiz velocity 2 m/s, max vert 0.8 m/s,
max acceleration 3 m/s², minimum turning radius 0.3 m
- Gravity-consistent Z: separate vertical speed cap for natural motion
- Blob ID assignment with persistence through up to 3 s occlusion gaps
- Collision avoidance: repulsion nudge when blobs closer than 0.4 m
- Posture estimation: lying (<0.4 m), seated (<0.8 m), standing/walking
from centroid height + horizontal speed
- 11 unit tests covering single-person tracking, occlusion recovery,
gap persistence, posture transitions, and constraint enforcement
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New package mothership/internal/fusion implementing spatial localization
via Fresnel zone inverse-distance weighting across multiple TX-RX links:
- Grid3D: voxel grid (default 0.2m cells) with AddLinkInfluence using
weight = activation / (1 + normalised_excess), where normalised_excess
= excess_path_length / first_Fresnel_zone_radius
- Engine: fuses LinkMotion slices into a 3D activation map, normalises,
and extracts peak blobs with confidence scores
- FresnelZoneRadius helper for callers choosing grid resolution
- 15 tests covering edge cases, ±1m position accuracy with 4+ links,
off-centre target, and performance (<50ms for 20 links)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- firmware: wire on-device motion hints to websocket_send_motion_hint()
with 1s rate-limit; csi_set_rate() now writes to g_state.packet_rate
- firmware: websocket_send_motion_hint() sends variance + MAC + timestamp
to mothership so rate controller ramps ahead of server-side detection
- mothership: RateController.OnMotionHint() preemptively ramps adjacent
nodes via SetAdjacentNodesFn callback (topology-aware burst propagation)
- mothership: idle timeout extended to 30s; variance_threshold=0 in active
mode (server handles detection), DefaultVarianceThreshold=1.0 in idle
- mothership: SendRoleToMAC() exposes dynamic role changes post-connect
- mothership: SendOTAToMAC() enables pushing firmware updates to nodes
- mothership: OTA status events are now logged with state and progress %
Protocol is backward-compatible: binary CSI frames work as in Phase 1;
JSON control messages are additive on the same single WebSocket per node.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New package mothership/internal/recording implements a disk-backed circular
buffer for continuous CSI frame recording. Frames are persisted in the same
binary format used over WebSocket (raw frame bytes + 8-byte recv timestamp).
Key features:
- Time-based retention (default 48h) prunes expired records automatically
on each Append and via an explicit Prune() method
- Configurable retention via SPAXEL_RECORDING_RETENTION env var (e.g. "24h")
- ScanRange(from, to time.Time, fn) for time-windowed read-back
- Space-bounded: fixed-size file with circular eviction prevents disk exhaustion
- Crash-safe: 32-byte header (magic + write/oldest/wrap positions) survives restarts
- 18 tests covering write, read-back, time-based pruning, wrap-around,
crash recovery, ScanRange, env var configuration, and storage bounds
Foundation for Phase 8 time-travel replay.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dashboard hub broadcasts motion state changes immediately on transition
(idle↔motion) via BroadcastMotionState; periodic state snapshots include
motion_states for new client init
- Per-link presence badge (green CLEAR / red MOTION) rendered in link list
alongside global presence indicator in status bar
- Amplitude mean time-series chart (60 s rolling window) for selected link,
line segments colored by motion state at each sample
- Fix: links created from JSON link_active/state events now initialize
ampHistory and lastAmpSample so time-series accumulates from first frame
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dashboard presence indicator: per-link MOTION/CLEAR badges with global
status bar indicator, motion_state WebSocket messages, amplitude chart
- CSI recording buffer: disk-backed circular buffer (replay/store.go) with
magic-tagged binary format, wrap/eviction, 360 MB default (~48 h at 20 Hz)
- Adaptive sensing rate: RateController ramps nodes to 20 Hz on motion,
drops to 2 Hz after 10 s idle; wires to SendConfigToMAC over WebSocket
- Fix: alias internal/signal as sigproc to avoid conflict with os/signal
- Fix: add GetAllMotionStates() to MockIngestionState in dashboard tests
All tests pass (signal, ingestion, replay, dashboard).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Static HTML/JS dashboard served by mothership at /
- Three.js 3D scene with ground grid, OrbitControls (pan/zoom/rotate)
- WebSocket connection at /ws/dashboard for real-time CSI streaming
- Binary CSI frame parsing (24-byte header + I/Q payload)
- Amplitude bar chart (64 subcarriers) as 2D Canvas overlay
- Node/link panels with live status and click-to-select
- Dashboard Go package with Hub for client broadcasting
- Ingestion server broadcasts CSI frames and node events to dashboard
- 5 new tests for dashboard hub operations
Complete: 3D scene, WebSocket, amplitude chart, node/link panels
Remaining: Docker packaging (Phase 1 final item)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>