Add trip history and route replay

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-09 19:48:06 -04:00
co-authored by Copilot
parent 3a981f5c6d
commit d3bc313e7f
13 changed files with 1158 additions and 25 deletions
+55 -14
View File
@@ -925,20 +925,61 @@ Same file-browser treatment for videos already imported to the Pi's own disk
returned to exactly 87 uploaded files. Background uploads were paused for the disposable test,
restored afterward, and the service remained active.
### Phase 18: Trip history & replay — 🚧 PLANNED
**New phase.** Right now the `tracking` table (Phase 7) is just a raw stream of GPS/speed points
with no concept of a discrete "trip" - there's no way to look back and see "trip #47, left home
8:03am, arrived work 8:31am, 22km." Add:
- Trip boundary detection: group existing tracking rows into discrete trips based on speed>0
gaps (a simple, dependency-free heuristic - e.g. a gap of N minutes with no motion ends a trip),
computed either live as rows are logged or in a batch pass over existing data. Does not need to
wait on Phase 13's OBD hardening - can ship with GPS-only boundary detection first, refine with
ignition-on/off signals from OBD later if useful.
- A `trips` table (start/end time, start/end coordinates, distance, duration) in
`modules/app_sql.py`.
- A new history page/list (built on the Phase 15 shared list component) showing past trips, and
a map replay view (reusing the existing offline-tile Leaflet setup from Phase 3/4) that draws
the recorded route for a selected trip.
### Phase 18: Trip history & replay — ✅ DONE
Added a derived, idempotently rebuilt `trips` index over the original non-destructive `tracking`
stream, plus `/api/trips`, bounded `/api/trip/<id>` route responses, and a responsive `/history`
page using the Phase 15 sortable list and the existing offline Leaflet bundle. Trip rows and replay
details show distance in kilometres/miles and average/maximum speed in km/h and mph; speed metrics
prefer OBD wheel speed and fall back to plausible GPS-derived segment speed. The map replay includes
a timestamped scrubber and play/pause timeline that moves a vehicle-position marker through every
recorded point while showing its date, time, and recorded speed.
**Motion-boundary decision after auditing the real Pi data:** the logger records every five seconds
for as long as GPS has a fix, including while parked, so timestamp gaps alone are not trip
boundaries. OBD wheel speed above zero is the primary motion signal. When OBD is absent or stuck at
zero, three sustained GPS displacements of at least eight metres at normal logger cadence act as
the fallback; isolated jumps, implausible jumps, malformed coordinates, stationary jitter, clock
rollback, and five-minute motion gaps are handled explicitly. Trip summaries contain start/end
time and coordinates, Haversine distance, duration, and point count. Route responses retain both
endpoints, filter implausible GPS outliers, and downsample only above 2,000 points. OBD samples are
used only while their last successful query is fresh, preventing a disconnected adapter's stale
last-known speed from creating a false trip. Rebuilds process an overlap after the persisted
high-water mark plus the latest trip rather than loading the unbounded full history every time.
**Implemented and verified 2026-08-09:**
- Audited the live database before choosing the heuristic: 5,132 initial rows covering about 25.5
hours, no null coordinates, all stored speeds zero, only 114 five-decimal coordinate positions,
and one old NUL-prefixed latitude. Future coordinates are normalized before insertion; invalid
snapshots are logged and rejected instead of poisoning trip calculations.
- Replaced unconditional five-second database writes with motion-aware persistence. Sensors are
still checked every five seconds, but rows are written only for the first fix, fresh OBD motion,
at least eight metres of accumulated GPS displacement, or a one-minute stationary heartbeat.
The Pi normally runs only while the car has power, so this is not presented as a full-day parked
storage problem; it reduces needless writes during bench testing and long idling while retaining
enough stopped points for traffic pauses in the replay timeline.
- Nine focused detector tests cover sustained GPS fallback, parked jitter/malformed coordinates,
inactivity splitting, OBD wheel-speed detection and slow-distance calculation, clock rollback,
route outlier filtering, and route downsampling. SQLite incremental-build integration was also
exercised against a disposable database, and five logger tests cover first-fix, parked,
wheel-speed, GPS-fallback, and heartbeat decisions. A copy of the real database
produced zero false trips across 5,179 parked rows.
- Deployed the uncommitted implementation to the Pi and built the schema against the real database
without modifying any original tracking rows. Inserted an eight-point disposable GPS route while
the service was stopped; the live API detected one 0.078km/35-second trip and returned all eight
ordered replay points. Restored the exact pre-test database afterward, confirmed zero synthetic
rows, `PRAGMA quick_check=ok`, zero derived trips, and an active service.
- Browser-tested `http://yaris.local/history` through the deployed mDNS/port-80 redirect at a
390x844 mobile viewport. Empty state, populated sortable row, replay action, offline route
polyline following every route point, start/end markers, metric/imperial distance and speed
summary, and no-horizontal-overflow behavior passed. Offline tiles
now attach only after selecting a route, avoiding irrelevant missing-tile requests around the
generic empty-map center.
- With user approval, removed the accumulated stationary bench backlog after verification:
5,454 local SQLite rows and 2,907 remotely synced Postgres rows, all with zero/null speed and no
detected trip. Preserved a dated pre-cleanup SQLite backup under `/home/yaris/backups/`, vacuumed
and integrity-checked SQLite, vacuum-analyzed Postgres, and left unrelated settings, upload
markers, DTCs, and video data untouched. The deployed motion-aware logger then proved two
consecutive stationary heartbeats were 60.02 seconds apart rather than five seconds.
### Phase 19: Pi health monitoring card — 🚧 PLANNED
**New phase, small and standalone - no dependencies.** CPU temperature, free disk space, uptime,