GPS Power State tidy-up (#4161)

* Refactor GPSPowerState enum
Identifies a case where the GPS hardware is awake, but an update is not yet desired

* Change terminology

* Clear old lock-time prediction on triple press

* Use exponential smoothing to predict lock time

* Rename averageLockTime to predictedLockTime

* Attempt: Send PMREQ with duration 0 on MCU deep-sleep

* Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep

* Revert "Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep"

This reverts commit 8b697cd2a4.

* Revert "Attempt: Send PMREQ with duration 0 on MCU deep-sleep"

This reverts commit 9d29ec7603.

* Remove unused notifyGPSSleep Observable
Handled with notifyDeepSleep, and enable() / disable()

* WIP: simplify GPS power management
An initial attempt only.

* Honor #3e9e0fd

* No-op when moving between GPS_IDLE and GPS_ACTIVE

* Ensure U-blox GPS is awake to receive indefinite sleep command

* Longer pause when waking U-blox to send sleep command

* Actually implement soft and hard sleep..

* Dynamically estimate the threshold for GPS_HARDSLEEP

* Fallback to GPS_HARDSLEEP, if GPS_SOFTSLEEP unsupported

* Move "excessive search time" behavior to scheduler class

* Minor logging adjustments

* Promote log to warning

* Gratuitous buffer clearing on boot

* Fix inverted standby pin logic
Specifically the standby pin for L76B, L76K and clones
Discovered during T-Echo testing: totally broken function, probe method failing.

* Remove redundant pin init
Now handled by setPowerState

* Replace max() with if statements
Avoid those platform specific implementations..

* Trunk formatting
New round of settings.json changes keep catching me out, have to remember to re-enable my "clang-format" for windows workaround.

* Remove some asserts from setPowerState
Original aim was to prevent sending a 0 second PMREQ to U-blox hardware as part of a timed sleep (GPS_HARDSLEEP, GPS_SOFTSLEEP). I'm not sure this is super important, and it feels tidier to just allow the 0 second sleeptime here, rather than fudge the sleeptime further up.

* Fix an error determining whether GPS_SOFTSLEEP is supported

* Clarify a log entry

* Set PIN_STANDBY for MCU deep-sleep
Required to reach TTGO's advertised 0.25mA sleep current for T-Echo. Without this change: ~6mA.
This commit is contained in:
todd-herbert
2024-07-11 15:26:43 +12:00
committed by GitHub
parent 11bca437fd
commit 33831cd41c
6 changed files with 436 additions and 296 deletions

View File

@@ -0,0 +1,118 @@
#include "GPSUpdateScheduling.h"
#include "Default.h"
// Mark the time when searching for GPS position begins
void GPSUpdateScheduling::informSearching()
{
searchStartedMs = millis();
}
// Mark the time when searching for GPS is complete,
// then update the predicted lock-time
void GPSUpdateScheduling::informGotLock()
{
searchEndedMs = millis();
LOG_DEBUG("Took %us to get lock\n", (searchEndedMs - searchStartedMs) / 1000);
updateLockTimePrediction();
}
// Clear old lock-time prediction data.
// When re-enabling GPS with user button.
void GPSUpdateScheduling::reset()
{
searchStartedMs = 0;
searchEndedMs = 0;
searchCount = 0;
predictedMsToGetLock = 0;
}
// How many milliseconds before we should next search for GPS position
// Used by GPS hardware directly, to enter timed hardware sleep
uint32_t GPSUpdateScheduling::msUntilNextSearch()
{
uint32_t now = millis();
// Target interval (seconds), between GPS updates
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval, default_gps_update_interval);
// Check how long until we should start searching, to hopefully hit our target interval
uint32_t dueAtMs = searchEndedMs + updateInterval;
uint32_t compensatedStart = dueAtMs - predictedMsToGetLock;
int32_t remainingMs = compensatedStart - now;
// If we should have already started (negative value), start ASAP
if (remainingMs < 0)
remainingMs = 0;
return (uint32_t)remainingMs;
}
// How long have we already been searching?
// Used to abort a search in progress, if it runs unnaceptably long
uint32_t GPSUpdateScheduling::elapsedSearchMs()
{
// If searching
if (searchStartedMs > searchEndedMs)
return millis() - searchStartedMs;
// If not searching - 0ms. We shouldn't really consume this value
else
return 0;
}
// Is it now time to begin searching for a GPS position?
bool GPSUpdateScheduling::isUpdateDue()
{
return (msUntilNextSearch() == 0);
}
// Have we been searching for a GPS position for too long?
bool GPSUpdateScheduling::searchedTooLong()
{
uint32_t maxSearchMs =
Default::getConfiguredOrDefaultMs(config.position.position_broadcast_secs, default_broadcast_interval_secs);
// If broadcast interval set to max, no such thing as "too long"
if (maxSearchMs == UINT32_MAX)
return false;
// If we've been searching longer than our position broadcast interval: that's too long
else if (elapsedSearchMs() > maxSearchMs)
return true;
// Otherwise, not too long yet!
else
return false;
}
// Updates the predicted time-to-get-lock, by exponentially smoothing the latest observation
void GPSUpdateScheduling::updateLockTimePrediction()
{
// How long did it take to get GPS lock this time?
// Duration between down() calls
int32_t lockTime = searchEndedMs - searchStartedMs;
if (lockTime < 0)
lockTime = 0;
// Ignore the first lock-time: likely to be long, will skew data
// Second locktime: likely stable. Use to intialize the smoothing filter
if (searchCount == 1)
predictedMsToGetLock = lockTime;
// Third locktime and after: predict using exponential smoothing. Respond slowly to changes
else if (searchCount > 1)
predictedMsToGetLock = (lockTime * weighting) + (predictedMsToGetLock * (1 - weighting));
searchCount++; // Only tracked so we can diregard initial lock-times
LOG_DEBUG("Predicting %us to get next lock\n", predictedMsToGetLock / 1000);
}
// How long do we expect to spend searching for a lock?
uint32_t GPSUpdateScheduling::predictedSearchDurationMs()
{
return GPSUpdateScheduling::predictedMsToGetLock;
}