suboptimumg.loganalysis.laps#
Lap division, splitting, and alignment utilities.
Ported from vnav_lap_analyzer.ipynb cells 8 (lap detection) and 16 (splitting + alignment).
- suboptimumg.loganalysis.laps.align_laps(lap_dfs, baseline_lap=None, sf_line=None, pos_cols=('posN', 'posE'), time_col='time_s')[source]#
Align all laps to a baseline via translation-only optimisation.
Uses cKDTree + Nelder-Mead to minimise mean log-nearest-neighbour distance after a rigid translation (no rotation, preserving yaw).
If
sf_lineis given, laps are re-trimmed at the S/F line after alignment.- Parameters:
lap_dfs (dict[int, DataFrame]) – As returned by
split_laps().baseline_lap (int or None) – Lap number to align to.
Nonepicks the fastest (shortestLapTime).sf_line (((n1,e1), (n2,e2)) or None) – S/F line for re-trimming.
pos_cols (str)
time_col (str)
- Returns:
Aligned (and optionally trimmed) lap DataFrames.
- Return type:
dict[int, pd.DataFrame]
- suboptimumg.loganalysis.laps.detect_lap_crossings(df, sf_line, time_col='time_s', pos_cols=('posN', 'posE'), min_lap_time_s=20.0, start_time=None, end_time=None)[source]#
Detect start/finish line crossings and label laps.
Works in local cartesian coordinates (
posN,posE). Thesf_lineis a pair of (N, E) tuples defining the S/F segment.If
sf_linecontains lat/lon tuples instead, convert them first withgps_to_local_cartesianand pick coordinates from the map plot.- Parameters:
df (pd.DataFrame) – Must contain pos_cols and time_col.
sf_line (((n1, e1), (n2, e2))) – Start/finish line endpoints in local cartesian.
time_col (str)
pos_cols ((str, str)) – (North column, East column).
min_lap_time_s (float) – Minimum time between crossings to accept as a real lap.
start_time (float or None) – Time window to search within.
Noneuses full range.end_time (float or None) – Time window to search within.
Noneuses full range.
- Returns:
Copy of df with a
Lapcolumn added (0 = before first crossing, 1 = first lap, etc.).- Return type:
pd.DataFrame
- suboptimumg.loganalysis.laps.plot_lap_overlay(lap_dfs, baseline_lap=None, sf_line=None, pos_cols=('posN', 'posE'), time_col='time_s', max_display_hz=100.0)[source]#
Overlay all laps on a 2D scatter plot.
- Parameters:
lap_dfs (dict[int, DataFrame])
baseline_lap (int or None)
sf_line (S/F line endpoints or None)
pos_cols ((north_col, east_col))
time_col (str) – Time column used to estimate sample rate for downsampling.
max_display_hz (float) – Target display sample rate.
0orNonedisables.
- Return type:
go.Figure
- suboptimumg.loganalysis.laps.split_laps(df, buffer_distance_m=5.0, min_lap_distance_m=20.0, pos_cols=('posN', 'posE'), time_col='time_s')[source]#
Split a DataFrame by
Lapcolumn with arc-length buffer.Each lap DataFrame gets
LapTimeandLapDistcolumns added.- Parameters:
df (pd.DataFrame) – Must have
Lap, pos_cols, and time_col columns.buffer_distance_m (float) – Extra path distance before/after each lap boundary.
min_lap_distance_m (float) – Discard laps shorter than this.
pos_cols (str)
time_col (str)
- Returns:
Keyed by lap number (>0).
- Return type:
dict[int, pd.DataFrame]