Status: measured result (room temperature, one platform) · Date: 2026-06-13
Answers: red-team finding F6 — "the TDMA timing thesis rests on ±1 ms jitter measured on a bench
over minutes; temperature drift, crystal aging, and missed-beacon free-run are untested on real
hardware." · Method: t114_jitter firmware (per-beacon (epoch, arrival-ms) log) on a live T114
client joined to the tower coordinator, captured against the bench's NTP clock for 3.8 hours.
The axis the red team feared most — clock drift — is comfortable at room temperature: the T114 runs at −12.6 ppm, giving a ~4-minute free-run/sleep budget before drift consumes the 3 ms guard. The beacon cadence jitter (~4 ms) is real but is absorbed by per-beacon re-anchoring and does not threaten the guard. Two follow-ups remain: the thermal curve (needs a unit in a temperature swing) and a direct measurement of within-superframe slot-alignment margin. The TDMA thesis survives its first real-hardware look.
A real T114 client, joined to the live tower coordinator, logged one line per received beacon:
(coordinator epoch, the T114's millis() at beacon RX, RSSI). The capture ran 3.8 h / 5,295 beacons
through normal operation (including a few coordinator restarts and one re-join from the LAN-backbone M2
test — those discontinuities are excluded from the fits). Two independent clocks are compared: the
T114's millis() (the device whose timing must hold the schedule) and the bench's NTP-disciplined
wall clock (logged alongside each beacon), which removes the coordinator's own cadence wander from the
clock-rate measurement.
Fitting the T114's millis() against NTP wall time over the full 3.8 h (5,295 points):
T114 oscillator rate = −12.6 ppm (999.987 ms per real second).
That is a normal, modest crystal error. Its consequence for the schedule is the free-run budget — how long a client can miss beacons (e.g. while sleeping) before its slot timing walks out of the 3 ms guard, since the client re-anchors on every beacon it does hear:
| Superframes missed | Wall time | Drift accrued | vs 3 ms guard |
|---|---|---|---|
| 1 | 3 s | 0.03 ms | ✓ |
| 15 | 38 s | 0.48 ms | ✓ |
| 30 | 76 s | 0.96 ms | ✓ |
| 60 | 152 s | 1.92 ms | ✓ |
| 120 | 304 s | 3.84 ms | ✗ |
Max free-run before the 3 ms guard: ~94 superframes ≈ 4.0 minutes at −12.6 ppm, room temperature.
This resolves red-team F9 (sleep vs sync) favorably: a sleepy client can stay down for minutes and re-acquire on a single beacon, no full re-join. The "sleep aggressively / wake to hold sync" tension is real but the budget is generous — wake once every ~2 minutes and you never lose the schedule to drift.
Consecutive-beacon intervals (clean segments) had stddev 4.0 ms (p95 deviation 6.7 ms, max 15 ms) around the 2.53 s superframe period. At face value that exceeds the 3 ms guard — but it is the wrong number to compare against the guard:
beacon_anchor = rx_time), and the
coordinator opens each slot's RX window at a fixed delay from its own beacon TX. Both sides
reference the same beacon event every superframe, so cadence wander between beacons is re-absorbed
each superframe — it does not accumulate into slot misalignment.So the 4 ms cadence jitter is a property of the coordinator's scheduling, not a guard violation. It would only matter if a client free-ran across many superframes (covered by §2) or if the within-superframe beacon-to-slot offset were itself noisy (§4).
The number that directly tests the 3 ms guard is the within-superframe slot-alignment margin: how tightly each client's slot transmission lands inside the coordinator's expected RX window for that slot. By construction it should be small (both sides key off the same beacon; the residual is propagation + decode-detection timing, expected sub-millisecond), but it has not been measured directly here.
The clean way to get it: have the coordinator log the arrival time of each client slot frame relative to
its own beacon (t+X ms), compare to the expected GUARD + slot·SLOT_W, and take the distribution. The
coordinator already computes this offset for some frame types; extending it to position frames + a short
run gives the definitive guard-margin number. Deferred to avoid disrupting the live tower service; it
is the next timing measurement.
All cheap, all known — none block the design:
- Real-time beacon scheduling on the coordinator (SCHED_FIFO / a tighter loop) to cut the 4 ms
cadence jitter, if §4 shows it leaking into slot margin.
- Slightly wider guard (e.g. 3 → 5 ms) — costs a sliver of slot efficiency, buys margin. The geometry
is airtime-computed (mac-spec.md §5), so this is a one-constant change.
- Adaptive guard scaled to measured clock dispersion across the cell (red-team F6 option B), and
opportunistic GPS/PPS discipline where available (option C, opportunistic-gps-time-note.md).
t114_jitter-equivalent
build ports trivially to the ESP32 leg).On the dimension the red team flagged as the single biggest risk to the TDMA thesis, the first real hardware measurement is reassuring: drift is modest, the sleep budget is minutes, and the visible jitter is coordinator-side and absorbed by re-anchoring. The thesis is not yet proven (thermal curve + direct slot-margin pending), but it is no longer resting on a bench-minutes result — and every mitigation for the remaining unknowns is cheap and identified. This is a green light to keep building, with two specific follow-up measurements named.