A force curve contains two measurements: how much force, and when.
We spent time investigating how well a compressed recording preserved the first one. The second one turned out to need attention before we could trust parts of that comparison.
In the sessions examined during the investigation, many consecutive force samples had the same timestamp. The sensor had measured them at different times. Our capture path was assigning the time their Bluetooth packet arrived.
The finding is documented. The clock correction described below remains proposed: the current phone and browser capture paths still stamp samples with application arrival time.
The gap in the timestamps
The July investigation examined two real sessions. Its most useful result was the distribution of time differences between adjacent samples:
| Time between adjacent samples | Share of intervals |
|---|---|
| Exactly zero | 93.4% |
| Between 5 and 30 milliseconds | Approximately zero |
| More than 100 milliseconds | 6.0% |
The readings arrived in bursts of roughly 16 samples about every 192 milliseconds. That is roughly 84 samples per second on average. The expected spacing between individual measurements is about 12 milliseconds, but that spacing was almost absent from the stored timestamps.
The average sample rate looked plausible. The distribution exposed the problem. An average can tell you how many readings arrived over a session while hiding whether each reading has a useful position within it.
One clock reading for a whole packet
The Progressor protocol includes a device timestamp with each force value. Our Swift parser already extracts it. The capture client then does the equivalent of this:
let t = now()
for sample in packet.samples {
emit(force: sample.force, time: t)
}
This is a simplified version of the current flow. It gives every sample in the packet the same application timestamp. The browser path also uses the application clock at notification handling time rather than carrying sensor measurement time through to the recording.
Bluetooth delivery can batch readings and add scheduling jitter. Arrival time answers when the application handled the data. It cannot recover the spacing between measurements inside a packet.
Why the force values alone are insufficient
Consider an illustrative sequence of measurements:
Force: 10 20 30 40
Measurement time: 0 12 24 36 ms
Arrival timestamp: 36 36 36 36 ms
The force sequence survives. Calculations involving elapsed time change.
Impulse is the area under the force curve. A trapezoid calculation uses each pair of force values and the time between them:
area += (forceBefore + forceAfter) / 2 * elapsedTime
When elapsed time is zero, that interval contributes zero area. In the examined recordings, the calculation was carried largely by the gaps between bursts, using the force values at their edges. The error depends on the signal around those edges; a plausible total does not establish faithful integration.
Derivatives are even more sensitive. Dividing a change in force by an extremely small interval can manufacture an enormous slope. A detector that suppresses peaks occurring too close together also inherits the wrong spacing. Changing timestamps can therefore change both a calculated metric and which events the detector emits.
The investigation had initially blamed compression for damage to rate of force development. That conclusion had to be withdrawn when the timing problem became clear. A comparison against a flawed reference cannot establish which part of the pipeline lost the information.
Give each clock a specific job
The sensor clock is useful for spacing its own measurements. The application clock is useful for placing the recording relative to the rest of a session.
The proposed design anchors the sensor timeline once, then uses sensor deltas:
applicationTime = applicationAnchor
+ (extendedSensorTime - sensorAnchor) / 1000
Here sensor timestamps are in microseconds and application timestamps are in milliseconds. The initial anchor places the stream; the sensor’s clock supplies the spacing afterward. Anchoring every packet independently would preserve intra-packet spacing but leave packet-to-packet jitter in the timeline.
Two details make this more than a substitution of one field for another.
The device counter is a 32-bit microsecond value. It wraps after about 71.6 minutes, so the mapping must extend it across rollover and distinguish rollover from other discontinuities such as a restarted stream. One recorded session lasted 114 minutes; long sessions are already part of the problem.
Clocks can also drift relative to one another. The design proposes estimating that drift from device and application observations and retaining the residual error. Delivery delays can bias that fit, so its quality needs measurement rather than an assumption that more observations remove every error.
Old recordings cannot acquire missing measurements
Correcting future capture does not restore device timestamps that were discarded from old recordings. Sample order and a nominal rate can support reconstruction, but reconstructed timing must remain distinguishable from measured timing.
The proposed provenance marker separates measured, interpolated, and nominal timing. That distinction also needs to survive sharing, so another device knows what kind of timeline its calculations are using.
There is a product decision here: should historical derived metrics be recomputed using estimated timing, or retained with their original measurement method identified? Recomputing could improve some estimates while changing numbers a user has already seen. Keeping them preserves history while leaving different measurement regimes side by side.
Neither choice can be hidden inside a codec update. The newer recording container also does not, by itself, correct the clock feeding it.
What a correction needs to demonstrate
The next useful evidence is a capture that preserves device spacing through packet batching, rollover, and stream interruption, followed by checks of the metrics and rep detector against that timeline. Sensors without per-sample time need an explicitly identified fallback.
The lesson has already changed the investigation: inspect time before judging the fidelity of a time series. Force values can survive a pipeline intact while the intervals needed to interpret them do not.