Lap Tracking
The backend runs its own lap counter, independent of the firmware's onboard lap tracker on the Telemetry Node. It works from the GPS fields already present in the CSV stream and needs no CAN frame from the car; the finish line and its thresholds are tunable at runtime from the web dashboard, which is the point of running a second tracker here rather than relying only on the board.
Two lap trackers exist in this system
The Telemetry Node also has a lap counter (lap_app.c; see the Telemetry firmware's Lap Tracking docs, currently work in progress on feature/lap-tracking). It is a separate implementation with separate state, and the two are not synchronized. This backend tracker is what the pit dashboard and Marple's Lap column use today.
Where it runs
analysis/lap_tracker.py consumes analysis_q, fed by udp-receiver whenever the analysis sink is enabled. It only looks at packets with a valid GPS fix (gps_fix > 0); everything else is skipped.
The finish line
The finish line is two GPS points, (finish_lat_1, finish_lon_1) and (finish_lat_2, finish_lon_2), set from .env at startup and updatable live via POST /api/settings.
For every fix, the tracker computes:
- Side of line: the sign of the 2D cross product of the line vector against the vector to the car's position (
-1,0, or+1). - Distance to the line: the perpendicular distance from the car to the finish line segment, clamped to the segment rather than its infinite extension.
Debounce and re-arm
A crossing only counts when all of these hold:
- The tracker is armed (a lap wasn't just closed on the same side without the car moving away first).
- The car's side of the line flipped since the last fix.
- The distance to the line is within
line_tolerance_m. - At least
min_lap_time_shas elapsed since the lap started. - The car has covered at least
min_lap_distance_msince the lap started.
After a lap closes, the tracker disarms until the car moves at least rearm_distance_m away from the line, so idling on the line (in the pits, under a red flag) can't rack up phantom laps.
Runtime tunables
All adjustable via POST /api/system_state:
| Parameter | Default | Meaning |
|---|---|---|
min_lap_time_s | 20.0 | Minimum seconds before a lap can close |
min_lap_distance_m | 250.0 | Minimum metres travelled before a lap can close |
rearm_distance_m | 50.0 | Distance from the line required to re-arm after a lap |
line_tolerance_m | 15.0 | How close to the line segment counts as a crossing |
force_lap_close | false | Close the current lap immediately, bypassing the gates above |
force_session_reset | false | Wipe all lap state and start over at lap 0 |
Finish line coordinates are set separately, via POST /api/settings (finish_lat_1/finish_lon_1/finish_lat_2/finish_lon_2).
Session handling
Lap state is keyed by session (the active sd_filename). When the session changes mid-stream, the tracker writes a lap_reset event for the old session, clears all state, and starts a fresh lap_0_start event for the new one. force_session_reset triggers the same reset on demand, without needing an actual session change.
What gets published
analysis/lap_state.pyholds a thread-safeLAP_METRICSsnapshot (current/last lap time and distance, total distance, lap index) that the web API and the Marple sender both read live.- Every closed lap writes a
lap_trackingpoint toINFLUX_ANALYSIS_BUCKETwith the lap number, time, distance, and the finish line coordinates used, alongsidesession_start,lap_0_start, andlap_resetevents. See InfluxDB. - The Marple sender attaches the current
lap_indexas aLapcolumn to every row it sends, whenever the analysis sink is enabled.
