diff --git a/bincio/extract/metrics.py b/bincio/extract/metrics.py index 67ef765..1823bd0 100644 --- a/bincio/extract/metrics.py +++ b/bincio/extract/metrics.py @@ -181,16 +181,32 @@ def compute_best_efforts( # 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 # 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_ele: dict[int, Optional[float]] = {} last_t = -1 + _prev: Optional[DataPoint] = None for p in pts: t = int((p.timestamp - started_at).total_seconds()) if t < 0 or t == last_t: 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 + 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: return None, None