Speed
Algorithm speed-1.0.0 · source engine/src/dtta/engine/speed/{analysis,metrics,filters}.py.
Which speed
The GoPro receiver reports a 2-D speed with every position. Reconnaissance on the real files showed it behaves like a receiver velocity solution: smooth and unbiased against the positions (1 s window bias −0.02…+0.09 m/s), while sample-to-sample position differencing at 18 Hz is unusable (RMS disagreement 3.2–3.8 m/s, "roughness" 7.4 vs 0.13). The reported speed is therefore the primary channel and position-derived speed is the independent witness (the consistency check in GPS validation).
Definitions
- : reported 2-D speed with its quality flags (served as
v_raw). - : with
SPEED_SPIKE,NO_FIX,PRELOCKsamples removed; gaps up tospeed_gap_fill_s(0.5 s) linearly filled and flaggedINTERPOLATED; longer gaps left open. - : zero-phase Butterworth low-pass of — order 2 applied forward and
backward (
sosfiltfilt, effective order 4, no phase lag) with cutoffspeed_cutoff_hz= 1.0 Hz, computed on a uniform time grid per contiguous run and mapped back to the samples, then resampled onto the 1 m track grid. This is the speed on every chart and in every metric. - Longitudinal (path) acceleration: by Savitzky–Golay derivative with
window
sg_window_long_s= 0.7 s and polynomial ordersg_polyorder= 2. Positive = accelerating.
Why 1.0 Hz
Selected in Phase 6 on the real files (docs/proofs/phase-06/filter_selection.md) from candidates
0.5 / 1.0 / 2.0 Hz: the lowest cutoff whose residual is near-white and whose brake/acceleration events
shift by less than 3 m against the 2 Hz candidate; a one-sample injected spike changes the maximum
speed by < 0.1 m/s. The heading window (1.0 s) was chosen for Cornering G noise of 0.02–0.04 g (1σ) on
straights with no corner bias; the longitudinal window (0.7 s) reproduces a synthetic ramp within 3 %.
Metrics
| metric | definition | units shown |
|---|---|---|
| Max / min / average speed (lap) | max and min of in the lap; average = centerline length / lap time | mph |
| Entry / exit speed (segment) | at the first / last grid point of the segment | mph |
| Minimum speed (segment) | lowest in the segment and its position min_speed_s |
mph, m |
| Speed loss | entry − minimum | mph |
| Brake point | first position where brake_threshold_ms2 (2 m/s²) is sustained for brake_sustain_s (0.3 s) |
m of track position |
| Acceleration point | first position after the minimum speed where accel_threshold_ms2 (1 m/s²) is sustained for accel_sustain_s (0.5 s) |
m of track position |
Positions are track progress s in metres from the start/finish gate (the API keys end in _s for
"s coordinate", not seconds).
Display conversions
mph = m/s × 3600 / 1609.344 (exact). Nothing else is recomputed in the browser: the values shown at the cursor are the engine's grid values at the nearest 1 m grid point.
Limitations
- The receiver's speed trails true motion by 0.15–0.40 s (
gps_lag_s, measured per session against the accelerometer). It is constant within a session and common to both laps of a comparison, so it does not affect deltas; it does shift brake points slightly later on the video. - Speeds below 3 m/s carry no heading (Cornering G undefined); lap detection ignores samples below
lap_cross_min_speed_msat the gate. - Reported speed above 80 m/s (179 mph) or a physically impossible step is rejected as a spike.
Verified
- Per-
sstandard deviation of speed across racing laps < 2 m/s on the straights of both fixtures. - m/s² everywhere on the fixtures; minimum-speed locations fall inside the corner arcs.
- Tests:
engine/tests/unit/test_speed_filters.py(impulse symmetry, unit DC gain, SG derivative of a known polynomial), synthetic ramp within 3 %, regression goldens (max/min/avg ± 0.05 m/s).