spaxel/mothership/cmd/sim
jedarden 2094f08a91 simulator: add periodic blob count polling to stats reporting
Add fetchBlobCount() function that queries the mothership's /api/blobs
endpoint and updates reportStats() to include blob count in periodic
statistics. Also updates printFinalStats() to show final blob count.

This completes the acceptance criteria for printing per-second frame
counts AND blob counts from GET /api/blobs poll.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-05 23:46:04 -04:00
..
generator.go fix(simulator): fix TestTwoRayModel wall reflection test case 2026-05-05 22:16:49 -04:00
main.go simulator: add periodic blob count polling to stats reporting 2026-05-05 23:46:04 -04:00
main.go.bak test: implement acceptance scenario integration tests (AS-1 through AS-6) 2026-05-05 05:45:15 -04:00
main_test.go fix(simulator): fix TestTwoRayModel wall reflection test case 2026-05-05 22:16:49 -04:00
Makefile feat: implement CSI simulator CLI with GDOP overlay 2026-04-09 12:31:13 -04:00
README.md feat: implement CSI simulator CLI with GDOP overlay 2026-04-09 12:31:13 -04:00
scenario.go test: implement acceptance scenario integration tests (AS-1 through AS-6) 2026-05-05 05:45:15 -04:00
verify.go ci: add golangci-lint static analysis gate 2026-05-04 12:57:55 -04:00
walker.go fix(sim): correct default WS endpoint and remove dead walker code 2026-04-24 21:42:14 -04:00

Spaxel CSI Simulator

A Go CLI tool for testing the Spaxel mothership without ESP32 hardware. The simulator opens WebSocket connections as virtual nodes and sends synthetic CSI binary frames.

Building

cd mothership
go build -o spaxel-sim ./cmd/sim

Usage

spaxel-sim [flags]

Flags

Flag Type Default Description
--mothership string ws://localhost:8080/ws/node Mothership WebSocket URL
--token string "" Node authentication token (X-Spaxel-Token header)
--nodes int 4 Number of virtual nodes to simulate
--walkers int 1 Number of walking persons to simulate
--rate int 20 CSI packet rate in Hz per node
--duration duration 30s Simulation duration (0 = run forever)
--ble bool false Also send simulated BLE advertisements
--seed int64 42 Random seed for reproducible runs
--width float64 6.0 Space width in meters
--depth float64 5.0 Space depth in meters
--height float64 2.5 Space height in meters
--space string "" Space dimensions as "WxDxH" (overrides --width/--depth/--height)
--show-frame-rate bool true Show per-second frame counts to stdout
--verbose bool false Enable verbose logging

Examples

Basic simulation (4 nodes, 1 walker, 30 seconds):

spaxel-sim --mothership ws://localhost:8080/ws/node --nodes 4 --walkers 1 --duration 30s

With authentication:

spaxel-sim --mothership ws://localhost:8080/ws/node --token <your-token> --nodes 4

Custom space dimensions:

spaxel-sim --space "10x8x3.0" --nodes 6 --walkers 2

With BLE advertisements:

spaxel-sim --ble --walkers 2

Reproducible run (fixed seed):

spaxel-sim --seed 12345 --duration 60s

Run indefinitely (for manual testing):

spaxel-sim --duration 0

CSI Frame Format

The simulator generates valid CSI binary frames matching the Spaxel specification:

Header (24 bytes):
  node_mac:     6 bytes  — source node MAC
  peer_mac:     6 bytes  — transmitting peer MAC
  timestamp_us: 8 bytes  — uint64, microseconds since node boot
  rssi:         1 byte   — int8, dBm
  noise_floor:  1 byte   — int8, dBm
  channel:      1 byte   — uint8, WiFi channel
  n_sub:        1 byte   — uint8, subcarrier count

Payload (n_sub × 2 bytes):
  Per subcarrier: int8 I, int8 Q

Simulation Model

Virtual Nodes

  • Positioned at corners and edges of the defined space
  • Mixed heights (high at 80% of room height, low at 30%)
  • MAC addresses: AA:BB:CC:DD:E0:00 through AA:BB:CC:DD:E0:0N

Walkers

  • Start at center of room (average person height: 1.7m)
  • Random walk with Gaussian velocity updates
  • Bounce off walls
  • BLE address: 11:22:33:44:55:00 through 11:22:33:44:55:0N

Signal Model

  • Path loss: Free space path loss model (PL(d) = PL₀ + 10·n·log₁₀(d/d₀))
    • PL₀ = 40 dB at d₀ = 1m
    • n = 2.0 (free space)
  • Fresnel modulation: Amplitude increases when walker is in Fresnel zones
    • Zone 1: maximum modulation
    • Zone 5+: no modulation
  • Noise: Gaussian noise added to I/Q values

Integration Testing

The simulator is designed for integration testing:

# Start mothership
docker run -d -p 8080:8080 --name spaxel-test ghcr.io/spaxel/spaxel:latest

# Run simulator for 30 seconds
spaxel-sim --mothership ws://localhost:8080/ws/node --nodes 4 --walkers 1 --duration 30s

# Check blob count (should be > 0 if detection is working)
curl -s http://localhost:8080/api/blobs | jq '. | length > 0'

Output

The simulator logs:

  • Connection status
  • Per-second frame rates (if --show-frame-rate)
  • Final statistics on completion
  • Errors (authentication failures, WebSocket errors, etc.)

Example output:

[INFO] CSI Simulator starting
[INFO] Configuration: nodes=4, walkers=1, rate=20 Hz, duration=30s
[INFO] Space: 6.0x5.0x2.5 m
[INFO] Connecting to: ws://localhost:8080/ws/node
[INFO] Node AA:BB:CC:DD:E0:00 connected to mothership
[STATS] Node AA:BB:CC:DD:E0:00: 20 frames/s
[STATS] Node AA:BB:CC:DD:E0:00: 20 frames/s
[INFO] Simulation completed successfully
[STATS] Node AA:BB:CC:DD:E0:00: sent 600 frames

Authentication

When the mothership requires authentication (SPAXEL_INSTALL_SECRET is set), you must provide a valid node token. Generate a token for a simulated node using the mothership's provisioning API or derive it manually:

# Get token from mothership provisioning endpoint
curl -s http://localhost:8080/api/provision -d '{"mac":"AA:BB:CC:DD:E0:00"}'

Error Handling

The simulator exits with non-zero status on:

  • WebSocket connection failure
  • Authentication rejection (HTTP 401)
  • Mothership rejection ({type:"reject"} message)

Testing

Run the test suite:

cd mothership
go test ./cmd/sim/...

Tests cover:

  • Space dimension parsing
  • MAC address conversion
  • CSI frame structure validation
  • Fresnel zone modulation
  • RSSI calculation
  • Walker position updates