feat(ui): trail — fixed 1 s sampling; drop GPS upd + Trail int from settings

GPS NMEA polling is cheap, and a slower cadence just hides motion from
the trail; the min-delta gate already filters jitter without throttling
the GPS. Both knobs that exposed cadence-style controls to the user are
removed:

- Trail sampling is fixed at TrailStore::SAMPLING_SECS = 1 s, matching
  the sensor manager's built-in GPS update default. The
  intervalSecs / intervalLabel / INTERVAL_COUNT helpers go with it.
- GPS update rate ("GPS upd" entry) leaves the sensor manager on its
  built-in 1 s default. applyGPSInterval and the GPS_INTERVAL_OPTS /
  LABELS tables / gpsIntervalIndex helper are deleted.

Both NodePrefs fields (gps_interval, trail_interval_idx) are retained
as reserved so the schema sentinel doesn't have to bump — older saves
load cleanly, the bytes are just ignored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Jakub
2026-05-25 10:48:03 +02:00
parent 4eb4e78a3f
commit b574d7829a
5 changed files with 13 additions and 72 deletions

View File

@@ -99,10 +99,10 @@ struct NodePrefs { // persisted to file
static const uint8_t FAVOURITE_PREFIX_LEN = 6;
uint8_t favourite_contacts[FAVOURITES_COUNT][FAVOURITE_PREFIX_LEN];
// GPS trail cadence — indices into TrailStore::intervalSecs / minDeltaMeters.
// Logging on/off is a runtime state (Tools Trail), not a persisted preference.
uint8_t trail_interval_idx; // 0..3: 1min / 30s / 5min / 15min
uint8_t trail_min_delta_idx; // 0..2: 25m / 5m / 100m
// GPS trail cadence. Logging on/off is a runtime state (Tools Trail),
// not a persisted preference.
uint8_t trail_interval_idx; // reserved — sampling cadence is now fixed at TrailStore::SAMPLING_SECS
uint8_t trail_min_delta_idx; // indexes TrailStore::minDeltaMeters (5/10/25/100 m)
// Tail sentinel written at the end of /new_prefs. Bump the low byte when
// adding/removing/reordering fields in DataStore::savePrefs/loadPrefsInt so

View File

@@ -22,17 +22,11 @@ class TrailStore {
public:
static const int CAPACITY = 256;
// Interval options (seconds) and their settings labels. Index 0 default = 30 s,
// a reasonable middle for walking/cycling without burning GPS frames.
static const uint8_t INTERVAL_COUNT = 6;
static uint16_t intervalSecs(uint8_t idx) {
static const uint16_t OPTS[INTERVAL_COUNT] = { 30, 10, 20, 60, 300, 900 };
return OPTS[idx < INTERVAL_COUNT ? idx : 0];
}
static const char* intervalLabel(uint8_t idx) {
static const char* L[INTERVAL_COUNT] = { "30 s", "10 s", "20 s", "1 min", "5 min", "15 min" };
return L[idx < INTERVAL_COUNT ? idx : 0];
}
// Fixed sampling cadence — matches the sensor manager's default GPS update
// rate (1 s). Density is controlled by the min-delta gate (settings) rather
// than by throttling the GPS poll. The NodePrefs::trail_interval_idx field
// is retained as reserved for backwards compatibility but no longer used.
static const uint16_t SAMPLING_SECS = 1;
// Min-delta (metres) gates samples too close to the previous one.
// Default 5 m: keeps walking jitter out, dense enough for a visible trail.

View File

@@ -59,8 +59,6 @@ class SettingsScreen : public UIScreen {
#if ENV_INCLUDE_GPS == 1
// GPS section
SECTION_GPS,
GPS_INTERVAL,
TRAIL_INTERVAL,
TRAIL_MIN_DELTA,
#endif
// Contacts section
@@ -82,11 +80,9 @@ class SettingsScreen : public UIScreen {
static const char* AUTO_OFF_LABELS[5];
static const int AUTO_OFF_COUNT = 5;
#endif
#if ENV_INCLUDE_GPS == 1
static const uint32_t GPS_INTERVAL_OPTS[6];
static const char* GPS_INTERVAL_LABELS[6];
static const int GPS_INTERVAL_COUNT = 6;
#endif
// GPS update interval tables are no longer surfaced in Settings — the sensor
// manager defaults to 1 s when nothing else sets it. Pref byte _prefs.gps_interval
// is retained for backwards compatibility.
static const uint16_t LOW_BAT_OPTS[7];
static const char* LOW_BAT_LABELS[7];
static const int LOW_BAT_COUNT = 7;
@@ -130,15 +126,6 @@ class SettingsScreen : public UIScreen {
}
#endif
#if ENV_INCLUDE_GPS == 1
int gpsIntervalIndex() {
NodePrefs* p = _task->getNodePrefs();
if (!p) return 0;
for (int i = 0; i < GPS_INTERVAL_COUNT; i++)
if (GPS_INTERVAL_OPTS[i] == p->gps_interval) return i;
return 0;
}
#endif
bool isSection(int item) const {
return item == SECTION_DISPLAY || item == SECTION_SOUND ||
@@ -420,14 +407,6 @@ class SettingsScreen : public UIScreen {
display.setCursor(display.valCol(), y);
display.print((p && p->auto_lock) ? "ON" : "OFF");
#if ENV_INCLUDE_GPS == 1
} else if (item == GPS_INTERVAL) {
display.print("GPS upd");
display.setCursor(display.valCol(), y);
display.print(GPS_INTERVAL_LABELS[gpsIntervalIndex()]);
} else if (item == TRAIL_INTERVAL) {
display.print("Trail int");
display.setCursor(display.valCol(), y);
display.print(TrailStore::intervalLabel(p ? p->trail_interval_idx : 0));
} else if (item == TRAIL_MIN_DELTA) {
display.print("Trail dist");
display.setCursor(display.valCol(), y);
@@ -658,25 +637,6 @@ public:
return true;
}
#if ENV_INCLUDE_GPS == 1
if (_selected == GPS_INTERVAL && p) {
int idx = gpsIntervalIndex();
if (right) idx = (idx + 1) % GPS_INTERVAL_COUNT;
if (left) idx = (idx + GPS_INTERVAL_COUNT - 1) % GPS_INTERVAL_COUNT;
if (left || right) {
p->gps_interval = GPS_INTERVAL_OPTS[idx];
_task->applyGPSInterval();
_dirty = true;
return true;
}
}
if (_selected == TRAIL_INTERVAL && p && (left || right)) {
int idx = p->trail_interval_idx;
if (idx >= TrailStore::INTERVAL_COUNT) idx = 0;
idx = (idx + (right ? 1 : TrailStore::INTERVAL_COUNT - 1)) % TrailStore::INTERVAL_COUNT;
p->trail_interval_idx = (uint8_t)idx;
_dirty = true;
return true;
}
if (_selected == TRAIL_MIN_DELTA && p && (left || right)) {
int idx = p->trail_min_delta_idx;
if (idx >= TrailStore::MIN_DELTA_COUNT) idx = 0;
@@ -771,10 +731,6 @@ public:
const uint16_t SettingsScreen::AUTO_OFF_OPTS[5] = { 5, 15, 30, 60, 0 };
const char* SettingsScreen::AUTO_OFF_LABELS[5] = { "5s", "15s", "30s", "60s", "never" };
#endif
#if ENV_INCLUDE_GPS == 1
const uint32_t SettingsScreen::GPS_INTERVAL_OPTS[6] = { 0, 30, 60, 300, 900, 1800 };
const char* SettingsScreen::GPS_INTERVAL_LABELS[6] = { "off", "30s", "1min", "5min", "15min", "30min" };
#endif
const uint16_t SettingsScreen::LOW_BAT_OPTS[7] = { 0, 3000, 3100, 3200, 3300, 3400, 3500 };
const char* SettingsScreen::LOW_BAT_LABELS[7] = { "off", "3.0V", "3.1V", "3.2V", "3.3V", "3.4V", "3.5V" };
const char* SettingsScreen::BATT_DISPLAY_LABELS[3] = { "icon", "%", "V" };

View File

@@ -1754,8 +1754,7 @@ void UITask::loop() {
// fix; min-delta gate inside addPoint() avoids near-stationary spam.
if (_trail.isActive() && _node_prefs != NULL
&& (int32_t)(millis() - _next_trail_sample_ms) >= 0) {
uint16_t interval_s = TrailStore::intervalSecs(_node_prefs->trail_interval_idx);
_next_trail_sample_ms = millis() + (uint32_t)interval_s * 1000UL;
_next_trail_sample_ms = millis() + (uint32_t)TrailStore::SAMPLING_SECS * 1000UL;
LocationProvider* loc = _sensors ? _sensors->getLocationProvider() : nullptr;
if (loc && loc->isValid()) {
uint16_t md = TrailStore::minDeltaMeters(_node_prefs->trail_min_delta_idx);
@@ -1854,13 +1853,6 @@ void UITask::applyTxPower() {
radio_set_tx_power(_node_prefs->tx_power_dbm);
}
void UITask::applyGPSInterval() {
if (_node_prefs == NULL) return;
char buf[12];
snprintf(buf, sizeof(buf),"%u", _node_prefs->gps_interval);
sensors.setSettingValue("gps_interval", buf);
}
void UITask::applyBrightness() {
if (_display != NULL && _node_prefs != NULL) {
_display->setBrightness(_node_prefs->display_brightness);

View File

@@ -198,7 +198,6 @@ public:
void setBuzzerVolumeLevel(uint8_t level);
uint8_t getBuzzerVolume() const { return _node_prefs ? _node_prefs->buzzer_volume : 4; }
void applyTxPower();
void applyGPSInterval();
void applyFont();
void applyRotation();
void applyFullRefreshInterval();