feat(ui): trail Units setting — km/h, mph, min/km, min/mi

Second row in the Config view: UP/DOWN moves focus between Min dist
and Units, LEFT/RIGHT cycles the focused row's value. Units propagates
to the Summary view, which now shows either "Avg: N km/h" / "N mph"
or "Pace: M:SS /km" / "/mi" depending on the choice.

Pace renders as "--:-- /unit" when distance or elapsed time is zero
(avoids divide-by-zero glitches with one-point trails). mph conversion
uses the 0.621371 ratio with rounding; pace works in floating point
off raw metres and seconds for accuracy at low speeds.

NodePrefs gains trail_units_idx as another reserved-ish byte; old saves
load with 0 = km/h, no schema bump needed since the rd() lambda already
defaults missing fields to zero.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-25 13:16:02 +02:00
parent 72fbbf2400
commit 46ac292b36
4 changed files with 91 additions and 14 deletions

View File

@@ -307,6 +307,7 @@ void DataStore::loadPrefsInt(const char *filename, NodePrefs& _prefs, double& no
rd(_prefs.favourite_contacts, sizeof(_prefs.favourite_contacts));
rd(&_prefs.trail_interval_idx, sizeof(_prefs.trail_interval_idx));
rd(&_prefs.trail_min_delta_idx, sizeof(_prefs.trail_min_delta_idx));
rd(&_prefs.trail_units_idx, sizeof(_prefs.trail_units_idx));
// Schema sentinel: bumped on layout changes. Mismatch means an older file
// (or a different schema); rd() already zero-inits any fields not present,
@@ -407,6 +408,7 @@ void DataStore::savePrefs(const NodePrefs& _prefs, double node_lat, double node_
file.write((uint8_t *)_prefs.favourite_contacts, sizeof(_prefs.favourite_contacts));
file.write((uint8_t *)&_prefs.trail_interval_idx, sizeof(_prefs.trail_interval_idx));
file.write((uint8_t *)&_prefs.trail_min_delta_idx, sizeof(_prefs.trail_min_delta_idx));
file.write((uint8_t *)&_prefs.trail_units_idx, sizeof(_prefs.trail_units_idx));
// Tail sentinel — must be last. See NodePrefs::SCHEMA_SENTINEL.
uint32_t sentinel = NodePrefs::SCHEMA_SENTINEL;