From 9db15780f9f5464f35f9819cdda1aca605421e79 Mon Sep 17 00:00:00 2001 From: Ben Meadors Date: Wed, 6 May 2026 13:44:09 -0500 Subject: [PATCH] Enhance GPS search failure handling backoff logic --- .claude/worktrees/naughty-payne-60fdb7 | 1 + src/gps/GPSUpdateScheduling.cpp | 29 +++++++++++++++++++++++--- src/gps/GPSUpdateScheduling.h | 1 + 3 files changed, 28 insertions(+), 3 deletions(-) create mode 160000 .claude/worktrees/naughty-payne-60fdb7 diff --git a/.claude/worktrees/naughty-payne-60fdb7 b/.claude/worktrees/naughty-payne-60fdb7 new file mode 160000 index 000000000..f2923590b --- /dev/null +++ b/.claude/worktrees/naughty-payne-60fdb7 @@ -0,0 +1 @@ +Subproject commit f2923590bc458027dd826cebd52b239055f79a78 diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index 45634d2d3..27d7f5963 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -15,6 +15,7 @@ void GPSUpdateScheduling::informGotLock() searchEndedMs = millis(); LOG_DEBUG("Took %us to get lock", (searchEndedMs - searchStartedMs) / 1000); updateLockTimePrediction(); + consecutiveFailures = 0; // Drop back to fast cadence as soon as we acquire any fix } // Search finished without obtaining a fix. We still need to mark the end time so @@ -24,7 +25,9 @@ void GPSUpdateScheduling::informGotLock() void GPSUpdateScheduling::informSearchFailed() { searchEndedMs = millis(); - LOG_DEBUG("GPS search ended without fix after %us", (searchEndedMs - searchStartedMs) / 1000); + consecutiveFailures++; + LOG_DEBUG("GPS search ended without fix after %us (consecutive failures: %u)", (searchEndedMs - searchStartedMs) / 1000, + consecutiveFailures); } // Clear old lock-time prediction data. @@ -35,6 +38,7 @@ void GPSUpdateScheduling::reset() searchEndedMs = 0; searchCount = 0; predictedMsToGetLock = 0; + consecutiveFailures = 0; } // How many milliseconds before we should next search for GPS position @@ -46,6 +50,21 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch() // Target interval (seconds), between GPS updates uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval, default_gps_update_interval); + // After a failed search, back off: indoors / no-sky environments will keep failing, + // so wake at most once per broadcast interval rather than once per gps_update_interval. + // Capped at 1 hour so a user-configured very-long broadcast interval still retries + // periodically (in case conditions change). Reset on any successful lock. + if (consecutiveFailures > 0) { + constexpr uint32_t failureRetryCapMs = 60UL * 60UL * 1000UL; // 1 hour cap + uint32_t broadcastSecs = + Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs); + uint32_t failureSleepMs = broadcastSecs * 1000UL; + if (failureSleepMs > failureRetryCapMs) + failureSleepMs = failureRetryCapMs; + if (updateInterval < failureSleepMs) + updateInterval = failureSleepMs; + } + // Check how long until we should start searching, to hopefully hit our target interval uint32_t dueAtMs = searchEndedMs + updateInterval; uint32_t compensatedStart = dueAtMs - predictedMsToGetLock; @@ -81,14 +100,18 @@ bool GPSUpdateScheduling::isUpdateDue() bool GPSUpdateScheduling::searchedTooLong() { constexpr uint32_t oneMinuteMs = 60UL * 1000UL; - constexpr uint32_t maxSearchClampMs = 15UL * oneMinuteMs; // Hard cap: 15 minutes is always too long + constexpr uint32_t maxSearchClampMs = 15UL * oneMinuteMs; // Hard cap: 15 minutes is always too long + constexpr uint32_t postFailureSearchMs = 5UL * oneMinuteMs; // Tighter dwell once we know the environment is hostile uint32_t elapsed = elapsedSearchMs(); // Anything over 15 minutes is too long, regardless of the broadcast interval. - // TODO: Make a smarter algorithm that backs off the search dwell time when not getting a lock. if (elapsed > maxSearchClampMs) return true; + // After a prior failed search, shorten the dwell + if (consecutiveFailures > 0 && elapsed > postFailureSearchMs) + return true; + uint32_t minimumOrConfiguredSecs = Default::getConfiguredOrMinimumValue(config.position.position_broadcast_secs, default_broadcast_interval_secs); uint32_t maxSearchMs = Default::getConfiguredOrDefaultMs(minimumOrConfiguredSecs, default_broadcast_interval_secs); diff --git a/src/gps/GPSUpdateScheduling.h b/src/gps/GPSUpdateScheduling.h index 64835a469..120605c4e 100644 --- a/src/gps/GPSUpdateScheduling.h +++ b/src/gps/GPSUpdateScheduling.h @@ -25,6 +25,7 @@ class GPSUpdateScheduling uint32_t searchEndedMs = 0; uint32_t searchCount = 0; uint32_t predictedMsToGetLock = 0; + uint32_t consecutiveFailures = 0; // Count of search cycles that ended without a fix; reset on lock const float weighting = 0.2; // Controls exponential smoothing of lock-times prediction. 20% weighting of "latest lock-time". }; \ No newline at end of file