Tracking

Chosen path, player ceiling, TDMA, and sensor fusion — why 12–16 players fit ~450 m².

Tracking & business plan — chosen path

Ship UWB position + IMU aim with TDMA slotting and gateway-driven anchor assignment on a dedicated isolated VLAN (WPA2 on; no app-layer TLS on the firehose). Target 12–16 concurrent players. The player experience (wand + phone, point-and-cast) is unchanged — only the tracking layer evolves.

done software complete hw pending needs physical validation next not started / in progress
12–16
Target concurrent players
~20
Room comfort cap (450 m²)
~16
UWB+TDMA ceiling @10Hz, 4 anchors
8–12
Anchors installed · range only 4
Key insight: at a 12–16 ceiling, the tracker is not the binding constraint — the room cap (~20) and demand are. So "make TDMA reliable at 16" is sufficient engineering; the real business risk is demand generation and opex, not UWB capacity.
On-device spell gestures software: the wand runs a tiny TFLite Micro model (6-DOF accel + gyro) and recognizes the cast gesture locally on button release, then sends it to the gateway, which validates legality (mana, cooldown, bans, tier) before casting. Gestures are trained in wand_tinyml, loaded into wand_sw, and renamed/assigned to spells in the admin panel. Note: the repo ships no pre-trained model — each deployment records its own gestures and exports a model; gesture casting stays disabled until one is flashed.

Player ceiling — why the numbers work

UWB ceiling = 1000·η / (rate × anchors_per_fix × t_ex), η=0.8, t_ex=1.2 ms. Reference line = 450 m² room comfort cap (~1 player / 22 m²).

Anchors installed vs ranged

Installing more anchors (8–12) improves coverage and NLOS resilience at zero capacity cost. What costs capacity is the number of anchors ranged per fix — ranging 8 instead of 4 halves the ceiling.

Design rule: gateway assigns each wand its best 3–4 anchors by geometry; physical anchor count is free to grow.

Levers

  • 10 Hz / 4 anchors → ~16 players
  • 8 Hz / 4 anchors → ~20 players (IMU fills gaps)
  • Drop to 3 anchors under load → +33% headroom

TDMA in one minute — why wands don't collide

All wands share one UWB radio channel. TDMA (Time-Division Multiple Access) is just an agreed turn order: instead of everyone ranging at once, the 100 ms "superframe" is sliced into 16 time slots, and each wand transmits only in its own slot. Two wands never range at the same instant, so positions stay clean as the fleet grows.

100 ms superframe - 16 slots repeats every 100 ms -> 10 Hz per wand W1 W2 W3 W4 W5 W6 W7 W8 W9 W10 W11 W12 W13 W14 W15 W16 050 ms100 ms W3's slot, zoomed in: TWR A1 TWR A2 TWR A3 TWR A4 guard one slot ~6 ms = one two-way-ranging (TWR) cycle to the wand's 3-4 anchors + a guard band

The problem (free-running)

Let every wand range whenever it wants and the transmissions collide as the fleet grows - dropped ranges, jitter, lost fixes. Fine for 1-2 wands; it falls apart near 16.

The fix (slots)

100 ms / 16 = ~6 ms per slot. In its slot a wand runs one TWR cycle to its 3-4 anchors, then goes quiet. 16 x 6 = 96 ms < 100 ms. One slot per superframe = 10 Hz per wand (drop to 8 Hz for more headroom).

Keeping time (sync)

Slots only work if clocks agree. Phase 1: gateway pushes a WiFi time reference (ms jitter, so ~3-5 ms guard bands). Phase 2 (if needed): a UWB beacon from a sync-anchor - ns-level, tight enough for all 16 slots.

Payoff: deterministic 8-10 Hz for all 16 wands with <10% timeouts (Gate B). The gateway also assigns each wand its best 3-4 anchors. The design adds adaptive backpressure - dropping stragglers to half-rate or 4 -> 3 anchors - so the schedule degrades gracefully; the gateway handler is ready, the wand-side uwb_report telemetry is still pending in firmware.

Sensor fusion — cameraless v1 + retro optical v2

v1 (launch): UWB position + IMU aim + ZUPT + soft-aim + game-design re-zeros (match start, respawn, shrine beacons) — zero camera CAPEX. v2 (after bench gates G0–G3 + Gate D): sparse ceiling cameras + 850 nm illuminators read passive retroreflective bands on the wand; optical bias-corrects gyro at capture time. Spec: docs/superpowers/specs/2026-07-21-retroreflective-optical-tracking-design.md. Casting is never blocked when optical is down.

IMU

High rate, gestures + aim between corrections. Drifts → ZUPT + re-zeros + v2 optical.

UWB

8–10 Hz absolute position. Breaks optical ID ties when players cluster.

Retro optical (v2)

≥3 collinear bands → aim vector from one camera. No wand IR electronics; no shutter sync. Tip RGB ignored (850 nm mono + bandpass).

Why retro bands? Active pulsed IR failed on sync, viewing angle, and blink-ID during fast casts. Retro tape + camera-side 850 nm illumination removes all three. Bands can look like decorative metal rings. Color on grip = staff/player ID only — cameras are monochrome IR.
Retroreflective band patterns on three wands
Band pattern: tip double-band + unique spacing = wand ID. IR camera sees bright collinear blobs → aim vector.

Wand cost: ~50 CZK bands

Scotchlite-class tape strips — no LEDs, no MOSFETs, no firmware sync. Apply with fleet jig; optional before v1 launch.

v2 optical CAPEX: sparse first

4–6 cameras at yaw wells (~25–35k CZK) after G0–G2 pass. Full 12–14 ceiling grid only if G3 drift data requires it.

Bench gates (before v2 buy)

  • G0 — bands detectable @ 3–5 m with 850 nm illuminator
  • G1 — 16 spacing codes separable (or ≥6 + UWB tie-break)
  • G2 — aim ≤5° RMS single camera @ 4 m, wand horizontal
  • G3 — 20-min cameraless match → drift cadence → camera count
  • Gate D — real UWB room fix (prerequisite for any arena spend)
  • C0–C2 — sensor characterization: UWB range error at surveyed points, optical reprojection residual, gyro bias drift → filter covariances

Fusion estimator roadmap — error-state Kalman filter planned

Today's fusion is a confidence-weighted average with hand-tuned source weights — a v1 scaffold, not real estimation. The target (before v2 optical fusion ships) is an ESKF — the aided inertial-navigation design proven in PX4/ArduPilot and Oculus CV1: the IMU predicts one state (position, velocity, quaternion, gyro bias) at firehose rate; every UWB range, optical line pose, and ZUPT arrives as a covariance-weighted correction. A missing source is just a skipped update — the state coasts on IMU prediction — so there is no source switching and no visible pose jump.

  • Mahalanobis gating — multipath UWB ranges (the dominant real-room error, not Gaussian noise) are rejected statistically before they bend the solve
  • Measured covariances — from characterization gates C0–C2 in the actual room, not the datasheet
  • Optical as interim ground truth — after G0–G2, multi-view triangulation (mocap-class math) tunes UWB covariances without renting a reference system
  • Degradation telemetry — covariance trace + active source count logged per pose, correlated with player feel

Same external contract — SpellEngine and broadcast unchanged.

Eye safety: wand emitters = visible RGB + Class 1 laser target. IR tracking light lives on ceiling illuminators (IEC 62471 check on venue install).
Haze: retro path is worse in dense fog (double attenuation). UWB + IMU + re-zeros carry the match; prefer hazer over fog; optical opportunistic under haze.