When two wireless training targets are hit a few milliseconds apart, a coach wants to know which one came first. That sounds easy until you remember that every device has its own crystal, every crystal drifts, and the radio between them delivers packets whenever it feels like it.
This is how we think about the problem.
Offset and delay
Each device keeps a local counter. What we want is the offset between each device and a reference clock, so that any local timestamp can be converted into shared time.
The classic approach, the same idea behind NTP, uses four timestamps per exchange:
t1 device sends request (device clock)
t2 base receives it (base clock)
t3 base sends reply (base clock)
t4 device receives reply (device clock)
delay = (t4 - t1) - (t3 - t2)
offset = ((t2 - t1) + (t3 - t4)) / 2
The formula assumes the trip out and the trip back take the same time. On a quiet link that is nearly true. On a busy one it is not, and that is where the error comes from.
Trust the fastest exchange
Here is the useful observation: the exchange with the smallest round trip is the one with the least room for asymmetry. If a packet came back fast, it cannot have waited long in either direction.
So we do not average all samples. We send a burst of exchanges, keep the few with the lowest delay, and only trust those. One honest sample beats a hundred noisy ones.
Crystals drift, so model the drift
Even a perfect offset is only perfect for a moment. A typical crystal is off by tens of parts per million, which is a few milliseconds per minute. Instead of chasing it with constant corrections, we fit a line through the good offset samples over time:
offset(t) = a + b · t
The slope b is the drift. With it, a device can predict the offset between synchronisations and stay accurate even when the radio goes quiet for a while.
Timestamp at the edge
The last lesson is about where you read the clock. A timestamp taken in application code includes interrupt latency and scheduling noise. A timestamp captured by the hardware when the sensor fires, or when the radio frame arrives, does not. Moving the timestamp as close to the physical event as possible removed more error than any clever algorithm.
The short version
- Estimate offset with round trips, but only trust the fastest ones.
- Model drift as a line, not a series of jumps.
- Capture time in hardware, as close to the event as you can.
None of this is new. It is the kind of old, well understood engineering we like best: boring on paper, and very satisfying when two lights blink exactly when they should.