fix: BUG-22 include GPS-derived speeds in trip max (Pi + server)
This commit is contained in:
@@ -24,6 +24,18 @@ When CLOSED, move the entire entry to Resolved — don't duplicate.
|
|||||||
fills the cards immediately on load (and every 15 s) via REST. Needs in-car
|
fills the cards immediately on load (and every 15 s) via REST. Needs in-car
|
||||||
verification.
|
verification.
|
||||||
|
|
||||||
|
### BUG-22 — Trip max speed ignores GPS-only stretch (shows OBD max only)
|
||||||
|
- **Status**: `OPEN` 2026-08-14
|
||||||
|
- **Phase**: 29
|
||||||
|
- **Symptom**: trip #9 shows "max 111.0 km/h" but the replay scrubber shows "GPS: 135.6 km/h"
|
||||||
|
at 6:26 p.m. — the top speed reached during the OBD dropout isn't reflected in the trip max.
|
||||||
|
- **Cause**: `max_speed_kmh` was computed from OBD speeds only (`max(obd_speeds or segment_speeds)`
|
||||||
|
on the Pi, `max(speeds)` on the server). When a trip has any OBD speed at all, the GPS-only
|
||||||
|
stretch is ignored for the max. The replay already shows GPS-derived speed for that stretch
|
||||||
|
(BUG-21 fallback), so the scrubber shows a higher speed than the stored max.
|
||||||
|
- **Fix**: union OBD + GPS-derived segment speeds when computing the max, on both the Pi
|
||||||
|
(`build_trip_summaries`) and the server (`compute_trips_for_device`).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Resolved
|
## Resolved
|
||||||
|
|||||||
@@ -2286,6 +2286,9 @@ logged as `BUG-XX` entries in [BUGS.md](./BUGS.md) (BUG-16 onward) and fixed + v
|
|||||||
on 2026-08-13 (22:23:50→22:43:41 UTC). Fixed: server `get_trip_route()` mirrors the Pi's
|
on 2026-08-13 (22:23:50→22:43:41 UTC). Fixed: server `get_trip_route()` mirrors the Pi's
|
||||||
GPS-speed fallback (derives speed from consecutive GPS points when OBD speed is missing), so
|
GPS-speed fallback (derives speed from consecutive GPS points when OBD speed is missing), so
|
||||||
the portal replay no longer flatlines at 0 km/h. Follow-up: OBD reconnect on stalled reads.
|
the portal replay no longer flatlines at 0 km/h. Follow-up: OBD reconnect on stalled reads.
|
||||||
|
- **BUG-22 (trip max ignores GPS-only stretch) — 🚧 IN PROGRESS**: `max_speed_kmh` used OBD speeds
|
||||||
|
only, so the top speed during the OBD dropout (e.g. 135 GPS) wasn't reflected. Fix: union OBD +
|
||||||
|
GPS-derived segment speeds in the max on both Pi + server.
|
||||||
|
|
||||||
Open items from the drive — fill in as reported:
|
Open items from the drive — fill in as reported:
|
||||||
- (none clock-related — BUG-17/BUG-18 closed 2026-08-14).
|
- (none clock-related — BUG-17/BUG-18 closed 2026-08-14).
|
||||||
|
|||||||
+1
-1
@@ -227,7 +227,7 @@ def build_trip_summaries(raw_rows):
|
|||||||
distance_km / duration_seconds * 3600, 1
|
distance_km / duration_seconds * 3600, 1
|
||||||
) if duration_seconds else 0,
|
) if duration_seconds else 0,
|
||||||
"max_speed_kmh": round(
|
"max_speed_kmh": round(
|
||||||
max(obd_speeds or segment_speeds or [0]), 1
|
max((obd_speeds + segment_speeds) or [0]), 1
|
||||||
),
|
),
|
||||||
"start_odometer": next((r["odometer"] for r in trip_rows if r.get("odometer")), None),
|
"start_odometer": next((r["odometer"] for r in trip_rows if r.get("odometer")), None),
|
||||||
"end_odometer": next((r["odometer"] for r in reversed(trip_rows) if r.get("odometer")), None),
|
"end_odometer": next((r["odometer"] for r in reversed(trip_rows) if r.get("odometer")), None),
|
||||||
|
|||||||
+12
-2
@@ -450,7 +450,8 @@ def compute_trips_for_device(device_id):
|
|||||||
seg_rows = rows[start_idx:end_idx + 1]
|
seg_rows = rows[start_idx:end_idx + 1]
|
||||||
|
|
||||||
distance = 0
|
distance = 0
|
||||||
speeds = []
|
speeds = [] # OBD speeds — used for avg
|
||||||
|
max_speeds = [] # OBD + GPS-derived — used for max
|
||||||
for i in range(1, len(seg_rows)):
|
for i in range(1, len(seg_rows)):
|
||||||
d = haversine_km(seg_rows[i - 1][1], seg_rows[i - 1][2],
|
d = haversine_km(seg_rows[i - 1][1], seg_rows[i - 1][2],
|
||||||
seg_rows[i][1], seg_rows[i][2])
|
seg_rows[i][1], seg_rows[i][2])
|
||||||
@@ -458,6 +459,15 @@ def compute_trips_for_device(device_id):
|
|||||||
s = seg_rows[i][3]
|
s = seg_rows[i][3]
|
||||||
if s is not None and s > 0:
|
if s is not None and s > 0:
|
||||||
speeds.append(s)
|
speeds.append(s)
|
||||||
|
max_speeds.append(s)
|
||||||
|
else:
|
||||||
|
# GPS-derived fallback for max (mirror the Pi): when OBD speed
|
||||||
|
# is missing, use the segment's GPS speed (>= 8 m, <= 220 km/h).
|
||||||
|
seconds = (seg_rows[i][0] - seg_rows[i - 1][0]).total_seconds()
|
||||||
|
if seconds > 0 and d * 1000 >= 8:
|
||||||
|
seg_speed = d / seconds * 3600
|
||||||
|
if seg_speed <= 220:
|
||||||
|
max_speeds.append(seg_speed)
|
||||||
|
|
||||||
duration_s = int((seg_rows[-1][0] - seg_rows[0][0]).total_seconds())
|
duration_s = int((seg_rows[-1][0] - seg_rows[0][0]).total_seconds())
|
||||||
if duration_s < 30 or distance < 0.05:
|
if duration_s < 30 or distance < 0.05:
|
||||||
@@ -469,7 +479,7 @@ def compute_trips_for_device(device_id):
|
|||||||
"distance_km": round(distance, 3),
|
"distance_km": round(distance, 3),
|
||||||
"duration_s": duration_s,
|
"duration_s": duration_s,
|
||||||
"avg_speed_kmh": round(sum(speeds) / len(speeds), 1) if speeds else None,
|
"avg_speed_kmh": round(sum(speeds) / len(speeds), 1) if speeds else None,
|
||||||
"max_speed_kmh": round(max(speeds), 1) if speeds else None,
|
"max_speed_kmh": round(max(max_speeds), 1) if max_speeds else None,
|
||||||
"start_lat": seg_rows[0][1],
|
"start_lat": seg_rows[0][1],
|
||||||
"start_lon": seg_rows[0][2],
|
"start_lon": seg_rows[0][2],
|
||||||
"end_lat": seg_rows[-1][1],
|
"end_lat": seg_rows[-1][1],
|
||||||
|
|||||||
Reference in New Issue
Block a user