M2 step 6: Tracks, recorded to GPX in the background

`r` in the GNSS App (or `gnss track start|stop`) records a Track to
/gnss/tracks/YYYYMMDD-HHMMSS.gpx: a point every 5 s once moved 5 m
(lib/gnss/track, 7 tests: haversine, the rule, GPX text, the file name).
It keeps recording with the App closed, shows REC in the Status Bar, and
announces start and stop with a Toast. It needs a card and the time;
switching GNSS off stops it. Tracks are written like Captures: past 90 %
card usage, until full. Storage Clean-up gets a GNSS tracks category.

A Track cut short by a reset or power loss has no GPX footer; the GNSS
Service closes such files at the next boot.

Verified on the device: a recorded Track and one interrupted by a reset
both parse as GPX 1.1 on the PC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-04 22:46:57 +02:00
co-authored by Claude Opus 5.5
parent 463ed2dda8
commit d7952612dd
13 changed files with 335 additions and 11 deletions
+3 -1
View File
@@ -26,11 +26,13 @@ struct StatusInfo {
int unread = 0; // unread messages across Services
enum class Gnss { None, Searching, TwoD, ThreeD } gnss = Gnss::None; // None: GNSS off (Q61)
int gnssSatellites = 0; // used in the Fix
bool tracking = false; // a Track is recording (Q63)
bool operator==(const StatusInfo& o) const {
return title == o.title && batteryPercent == o.batteryPercent && clock == o.clock &&
sdPresent == o.sdPresent && sdLevel == o.sdLevel && compose == o.compose && wifi == o.wifi &&
wifiBars == o.wifiBars && unread == o.unread && gnss == o.gnss && gnssSatellites == o.gnssSatellites;
wifiBars == o.wifiBars && unread == o.unread && gnss == o.gnss && gnssSatellites == o.gnssSatellites &&
tracking == o.tracking;
}
bool operator!=(const StatusInfo& o) const { return !(*this == o); }
};