metrics: fall back to GPS-derived speed in compute_best_efforts when device speed absent
FIT files from older devices (and GPX/TCX files) often omit the speed field. The sliding-window best-effort algorithm was treating all such points as speed=0, so no records were ever produced for these activities. Fix: when p.speed_kmh is None but consecutive lat/lon are available, compute haversine segment speed and spread it evenly across the 1Hz interval slots. This mirrors what _gps_stats already does for avg/max speed computation.
This commit is contained in:
@@ -181,16 +181,32 @@ def compute_best_efforts(
|
|||||||
# Build dense 1 Hz speed (km/h) and elevation (m) arrays with gap zero-filling.
|
# Build dense 1 Hz speed (km/h) and elevation (m) arrays with gap zero-filling.
|
||||||
# Zero-filling speed gaps (0 km/h) prevents best-effort windows from spanning
|
# Zero-filling speed gaps (0 km/h) prevents best-effort windows from spanning
|
||||||
# recording pauses and producing artificially fast times.
|
# recording pauses and producing artificially fast times.
|
||||||
|
# When the device didn't record speed (common in older FIT files), fall back to
|
||||||
|
# GPS-derived speed: spread the haversine segment speed evenly across the interval
|
||||||
|
# so the sliding window accumulates the correct distance.
|
||||||
sparse_speed: dict[int, float] = {}
|
sparse_speed: dict[int, float] = {}
|
||||||
sparse_ele: dict[int, Optional[float]] = {}
|
sparse_ele: dict[int, Optional[float]] = {}
|
||||||
last_t = -1
|
last_t = -1
|
||||||
|
_prev: Optional[DataPoint] = None
|
||||||
for p in pts:
|
for p in pts:
|
||||||
t = int((p.timestamp - started_at).total_seconds())
|
t = int((p.timestamp - started_at).total_seconds())
|
||||||
if t < 0 or t == last_t:
|
if t < 0 or t == last_t:
|
||||||
continue
|
continue
|
||||||
last_t = t
|
|
||||||
sparse_speed[t] = p.speed_kmh if p.speed_kmh is not None else 0.0
|
|
||||||
sparse_ele[t] = p.elevation_m
|
sparse_ele[t] = p.elevation_m
|
||||||
|
if p.speed_kmh is not None:
|
||||||
|
sparse_speed[t] = p.speed_kmh
|
||||||
|
elif (_prev is not None
|
||||||
|
and _prev.lat is not None and _prev.lon is not None
|
||||||
|
and p.lat is not None and p.lon is not None):
|
||||||
|
dt_s = t - last_t
|
||||||
|
seg_m = _haversine_m(_prev.lat, _prev.lon, p.lat, p.lon)
|
||||||
|
seg_kmh = (seg_m / dt_s) * 3.6
|
||||||
|
for slot in range(last_t, t):
|
||||||
|
sparse_speed[slot] = seg_kmh
|
||||||
|
else:
|
||||||
|
sparse_speed[t] = 0.0
|
||||||
|
last_t = t
|
||||||
|
_prev = p
|
||||||
|
|
||||||
if not sparse_speed:
|
if not sparse_speed:
|
||||||
return None, None
|
return None, None
|
||||||
|
|||||||
Reference in New Issue
Block a user