From 368aef591f69f499e4a21071b2e7d8790da7cfa9 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 11 Jun 2024 03:05:05 +1200 Subject: [PATCH 01/30] Refactor GPSPowerState enum Identifies a case where the GPS hardware is awake, but an update is not yet desired --- src/gps/GPS.cpp | 36 +++++++++++++++++++++++++----------- src/gps/GPS.h | 9 +++++---- 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 17088910ae3..28af95e8b69 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -28,6 +28,12 @@ #define GPS_STANDBY_THRESHOLD_MINUTES 15 #endif +// How many seconds of sleep make it worthwhile for the GPS to use powered-on standby +// Shorter than this, and we'll just wait instead +#ifndef GPS_RESTING_THRESHOLD_SECONDS +#define GPS_RESTING_THRESHOLD_SECONDS 10 +#endif + #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) HardwareSerial *GPS::_serial_gps = &Serial1; #else @@ -776,14 +782,20 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) { // Record the current powerState if (on) - powerState = GPS_AWAKE; - else if (!on && standbyOnly) + powerState = GPS_ACTIVE; + else if (sleepTime <= GPS_RESTING_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // Note: sleepTime==0 if from GPS::disable() + powerState = GPS_RESTING; + else if (standbyOnly) powerState = GPS_STANDBY; else powerState = GPS_OFF; LOG_DEBUG("GPS::powerState=%d\n", powerState); + // If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out. + if (!on && powerState == GPS_RESTING) + return; + if (on) { clearBuffer(); // drop any old data waiting in the buffer before re-enabling if (en_gpio) @@ -880,14 +892,14 @@ void GPS::setConnected() void GPS::setAwake(bool wantAwake) { - // If user has disabled GPS, make sure it is off, not just in standby + // If user has disabled GPS, make sure it is off, not just in standby or "resting" if (!wantAwake && !enabled && powerState != GPS_OFF) { setGPSPower(false, false, 0); return; } // If GPS power state needs to change - if ((wantAwake && powerState != GPS_AWAKE) || (!wantAwake && powerState == GPS_AWAKE)) { + if ((wantAwake && powerState != GPS_ACTIVE) || (!wantAwake && powerState == GPS_ACTIVE)) { LOG_DEBUG("WANT GPS=%d\n", wantAwake); // Calculate how long it takes to get a GPS lock @@ -910,8 +922,10 @@ void GPS::setAwake(bool wantAwake) return; } - // If waking frequently: standby only. Would use more power trying to reacquire lock each time - else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby + // If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time + // We'll either use a "powered-on" standby, or just wait it out, depending on how soon the next update is due + // Will decide which inside setGPSPower method + else { #ifdef GPS_UC6580 setGPSPower(wantAwake, false, getSleepTime() - averageLockTime); #else @@ -1033,14 +1047,14 @@ int32_t GPS::runOnce() uint32_t timeAsleep = now - lastSleepStartMsec; auto sleepTime = getSleepTime(); - if (powerState != GPS_AWAKE && (sleepTime != UINT32_MAX) && + if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) && ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - averageLockTime)))) { // We now want to be awake - so wake up the GPS setAwake(true); } // While we are awake - if (powerState == GPS_AWAKE) { + if (powerState == GPS_ACTIVE) { // LOG_DEBUG("looking for location\n"); // If we've already set time from the GPS, no need to ask the GPS bool gotTime = (getRTCQuality() >= RTCQualityGPS); @@ -1086,7 +1100,7 @@ int32_t GPS::runOnce() // 9600bps is approx 1 byte per msec, so considering our buffer size we never need to wake more often than 200ms // if not awake we can run super infrquently (once every 5 secs?) to see if we need to wake. - return (powerState == GPS_AWAKE) ? GPS_THREAD_INTERVAL : 5000; + return (powerState == GPS_ACTIVE) ? GPS_THREAD_INTERVAL : 5000; } // clear the GPS rx buffer as quickly as possible @@ -1617,9 +1631,9 @@ bool GPS::whileIdle() { unsigned int charsInBuf = 0; bool isValid = false; - if (powerState != GPS_AWAKE) { + if (powerState != GPS_ACTIVE) { clearBuffer(); - return (powerState == GPS_AWAKE); + return (powerState == GPS_ACTIVE); } #ifdef SERIAL_BUFFER_SIZE if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) { diff --git a/src/gps/GPS.h b/src/gps/GPS.h index e9ec111a75c..da74e415102 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -39,9 +39,10 @@ typedef enum { } GPS_RESPONSE; enum GPSPowerState : uint8_t { - GPS_OFF = 0, - GPS_AWAKE = 1, - GPS_STANDBY = 2, + GPS_OFF = 0, // Physically powered off + GPS_ACTIVE = 1, // Awake and want a position + GPS_STANDBY = 2, // Physically powered on, but soft-sleeping + GPS_RESTING = 3, // Awake, but not wanting another position yet }; // Generate a string representation of DOP @@ -93,7 +94,7 @@ class GPS : private concurrency::OSThread bool GPSInitFinished = false; // Init thread finished? bool GPSInitStarted = false; // Init thread finished? - GPSPowerState powerState = GPS_OFF; // GPS_AWAKE if we want a location right now + GPSPowerState powerState = GPS_OFF; // GPS_ACTIVE if we want a location right now uint8_t numSatellites = 0; From 059388694cd6c658cc71e23665eb8a69a968d989 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 11 Jun 2024 03:56:24 +1200 Subject: [PATCH 02/30] Change terminology --- src/gps/GPS.cpp | 12 ++++++------ src/gps/GPS.h | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 28af95e8b69..c9558dc7150 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -30,8 +30,8 @@ // How many seconds of sleep make it worthwhile for the GPS to use powered-on standby // Shorter than this, and we'll just wait instead -#ifndef GPS_RESTING_THRESHOLD_SECONDS -#define GPS_RESTING_THRESHOLD_SECONDS 10 +#ifndef GPS_IDLE_THRESHOLD_SECONDS +#define GPS_IDLE_THRESHOLD_SECONDS 10 #endif #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) @@ -783,8 +783,8 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) // Record the current powerState if (on) powerState = GPS_ACTIVE; - else if (sleepTime <= GPS_RESTING_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // Note: sleepTime==0 if from GPS::disable() - powerState = GPS_RESTING; + else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // Note: sleepTime==0 if from GPS::disable() + powerState = GPS_IDLE; else if (standbyOnly) powerState = GPS_STANDBY; else @@ -793,7 +793,7 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) LOG_DEBUG("GPS::powerState=%d\n", powerState); // If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out. - if (!on && powerState == GPS_RESTING) + if (!on && powerState == GPS_IDLE) return; if (on) { @@ -892,7 +892,7 @@ void GPS::setConnected() void GPS::setAwake(bool wantAwake) { - // If user has disabled GPS, make sure it is off, not just in standby or "resting" + // If user has disabled GPS, make sure it is off, not just in standby or idle if (!wantAwake && !enabled && powerState != GPS_OFF) { setGPSPower(false, false, 0); return; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index da74e415102..e742df72660 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -42,7 +42,7 @@ enum GPSPowerState : uint8_t { GPS_OFF = 0, // Physically powered off GPS_ACTIVE = 1, // Awake and want a position GPS_STANDBY = 2, // Physically powered on, but soft-sleeping - GPS_RESTING = 3, // Awake, but not wanting another position yet + GPS_IDLE = 3, // Awake, but not wanting another position yet }; // Generate a string representation of DOP From 1799f6cb0fc437ab7b36ae96e5cf770737a393bb Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Wed, 12 Jun 2024 15:36:42 +1200 Subject: [PATCH 03/30] Clear old lock-time prediction on triple press --- src/gps/GPS.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index c9558dc7150..6cd00575eb0 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -783,7 +783,9 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) // Record the current powerState if (on) powerState = GPS_ACTIVE; - else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // Note: sleepTime==0 if from GPS::disable() + else if (!enabled) // User has disabled with triple press + powerState = GPS_OFF; + else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL) powerState = GPS_IDLE; else if (standbyOnly) powerState = GPS_STANDBY; @@ -1664,6 +1666,10 @@ bool GPS::whileIdle() } void GPS::enable() { + // Clear the old lock-time prediction + GPSCycles = 0; + averageLockTime = 0; + enabled = true; setInterval(GPS_THREAD_INTERVAL); setAwake(true); From 25700a141955dcd83c284756d227e9004120e11c Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Wed, 12 Jun 2024 15:38:34 +1200 Subject: [PATCH 04/30] Use exponential smoothing to predict lock time --- src/gps/GPS.cpp | 51 +++++++++++++++++++++++++++++++------------------ 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6cd00575eb0..c78ba96487c 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -906,22 +906,45 @@ void GPS::setAwake(bool wantAwake) // Calculate how long it takes to get a GPS lock if (wantAwake) { + // Record the time we start looking for a lock lastWakeStartMsec = millis(); } else { + // Record by how much we missed our ideal target postion.gps_update_interval (for logging only) + // Need to calculate this before we update lastSleepStartMsec, to make the new prediction + int32_t lateByMsec = (int32_t)(millis() - lastSleepStartMsec) - (int32_t)getSleepTime(); + + // Record the time we finish looking for a lock lastSleepStartMsec = millis(); - if (GPSCycles == 1) { // Skipping initial lock time, as it will likely be much longer than average - averageLockTime = lastSleepStartMsec - lastWakeStartMsec; - } else if (GPSCycles > 1) { - averageLockTime += ((int32_t)(lastSleepStartMsec - lastWakeStartMsec) - averageLockTime) / (int32_t)GPSCycles; + + // How long did it take to get GPS lock this time? + uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec; + + // Update the lock-time prediction + // Used pre-emptively, attemtping to hit target of gps.position_update_interal + switch (GPSCycles) { + case 0: + LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000); + break; + case 1: + averageLockTime = lockTime; // Avoid slow ramp-up - start with a real value + LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000); + break; + default: + // Predict lock-time using exponential smoothing: respond slowly to changes + averageLockTime = (lockTime * 0.2) + (averageLockTime * 0.8); // Latest lock time has 20% weight on prediction + LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000, + (lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, averageLockTime / 1000); } GPSCycles++; - LOG_DEBUG("GPS Lock took %d, average %d\n", (lastSleepStartMsec - lastWakeStartMsec) / 1000, averageLockTime / 1000); } + // How long to wait before attempting next GPS update + // Aims to hit position.gps_update_interval by using the lock-time prediction + uint32_t compensatedSleepTime = (getSleepTime() > averageLockTime) ? (getSleepTime() - averageLockTime) : 0; + // If long interval between updates: power off between updates - if ((int32_t)getSleepTime() - averageLockTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { + if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { setGPSPower(wantAwake, false, getSleepTime() - averageLockTime); - return; } // If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time @@ -929,21 +952,11 @@ void GPS::setAwake(bool wantAwake) // Will decide which inside setGPSPower method else { #ifdef GPS_UC6580 - setGPSPower(wantAwake, false, getSleepTime() - averageLockTime); + setGPSPower(wantAwake, false, compensatedSleepTime); #else - setGPSPower(wantAwake, true, getSleepTime() - averageLockTime); + setGPSPower(wantAwake, true, compensatedSleepTime); #endif - return; } - - // Gradually recover from an abnormally long "time to get lock" - if (averageLockTime > 20000) { - averageLockTime -= 1000; // eventually want to sleep again. - } - - // Make sure we don't have a fallthrough where GPS is stuck off - if (wantAwake) - setGPSPower(true, true, 0); } } From b7d43c55cbbc8efe9a753ba31983df7faf8d92d4 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Wed, 12 Jun 2024 15:48:04 +1200 Subject: [PATCH 05/30] Rename averageLockTime to predictedLockTime --- src/gps/GPS.cpp | 16 ++++++++-------- src/gps/GPS.h | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index c78ba96487c..8d46742baab 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -920,31 +920,31 @@ void GPS::setAwake(bool wantAwake) uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec; // Update the lock-time prediction - // Used pre-emptively, attemtping to hit target of gps.position_update_interal + // Used pre-emptively, attempting to hit target of gps.position_update_interval switch (GPSCycles) { case 0: LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000); break; case 1: - averageLockTime = lockTime; // Avoid slow ramp-up - start with a real value + predictedLockTime = lockTime; // Avoid slow ramp-up - start with a real value LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000); break; default: // Predict lock-time using exponential smoothing: respond slowly to changes - averageLockTime = (lockTime * 0.2) + (averageLockTime * 0.8); // Latest lock time has 20% weight on prediction + predictedLockTime = (lockTime * 0.2) + (predictedLockTime * 0.8); // Latest lock time has 20% weight on prediction LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000, - (lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, averageLockTime / 1000); + (lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, predictedLockTime / 1000); } GPSCycles++; } // How long to wait before attempting next GPS update // Aims to hit position.gps_update_interval by using the lock-time prediction - uint32_t compensatedSleepTime = (getSleepTime() > averageLockTime) ? (getSleepTime() - averageLockTime) : 0; + uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0; // If long interval between updates: power off between updates if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { - setGPSPower(wantAwake, false, getSleepTime() - averageLockTime); + setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime); } // If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time @@ -1063,7 +1063,7 @@ int32_t GPS::runOnce() auto sleepTime = getSleepTime(); if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) && - ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - averageLockTime)))) { + ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - predictedLockTime)))) { // We now want to be awake - so wake up the GPS setAwake(true); } @@ -1681,7 +1681,7 @@ void GPS::enable() { // Clear the old lock-time prediction GPSCycles = 0; - averageLockTime = 0; + predictedLockTime = 0; enabled = true; setInterval(GPS_THREAD_INTERVAL); diff --git a/src/gps/GPS.h b/src/gps/GPS.h index e742df72660..34e1844c358 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -73,7 +73,7 @@ class GPS : private concurrency::OSThread uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; uint32_t en_gpio = 0; - int32_t averageLockTime = 0; + int32_t predictedLockTime = 0; uint32_t GPSCycles = 0; int speedSelect = 0; From 9d29ec7603a88056b9115796b29b5023165a93bb Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 13 Jun 2024 01:06:28 +1200 Subject: [PATCH 06/30] Attempt: Send PMREQ with duration 0 on MCU deep-sleep --- src/gps/GPS.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 8d46742baab..6a8d6d59c14 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1131,7 +1131,8 @@ int GPS::prepareDeepSleep(void *unused) { LOG_INFO("GPS deep sleep!\n"); - setAwake(false); + // Manually enter GPSPowerState::OFF, so we can ensure a PMREQ with duration 0 has been sent + setGPSPower(false, false, 0); return 0; } From 8b697cd2a445355dcfab5b33e0ce7a3128cab151 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 13 Jun 2024 02:09:57 +1200 Subject: [PATCH 07/30] Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep --- src/gps/GPS.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6a8d6d59c14..e67e129c160 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -785,7 +785,7 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) powerState = GPS_ACTIVE; else if (!enabled) // User has disabled with triple press powerState = GPS_OFF; - else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL) + else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // sleepTime=0 indicates indefinite GPS poweroff powerState = GPS_IDLE; else if (standbyOnly) powerState = GPS_STANDBY; @@ -940,7 +940,8 @@ void GPS::setAwake(bool wantAwake) // How long to wait before attempting next GPS update // Aims to hit position.gps_update_interval by using the lock-time prediction - uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0; + // Sleep for at least 1 second, so we don't ask GPS hardware to sleep indefinitely with a "0 second PMREQ" + uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 1; // If long interval between updates: power off between updates if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { @@ -1129,11 +1130,10 @@ void GPS::clearBuffer() /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs int GPS::prepareDeepSleep(void *unused) { - LOG_INFO("GPS deep sleep!\n"); - - // Manually enter GPSPowerState::OFF, so we can ensure a PMREQ with duration 0 has been sent - setGPSPower(false, false, 0); - + /* + * GPS power was previously set here. + * Now removed, as the same call is already made directly in doDeepSleep. + */ return 0; } From 2d98353689318705a69408b14f47c534b4c0c8b5 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 13 Jun 2024 11:43:22 +1200 Subject: [PATCH 08/30] Revert "Attempt 2: Send PMREQ with duration 0 on MCU deep-sleep" This reverts commit 8b697cd2a445355dcfab5b33e0ce7a3128cab151. --- src/gps/GPS.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index e67e129c160..6a8d6d59c14 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -785,7 +785,7 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) powerState = GPS_ACTIVE; else if (!enabled) // User has disabled with triple press powerState = GPS_OFF; - else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL && sleepTime > 0) // sleepTime=0 indicates indefinite GPS poweroff + else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL) powerState = GPS_IDLE; else if (standbyOnly) powerState = GPS_STANDBY; @@ -940,8 +940,7 @@ void GPS::setAwake(bool wantAwake) // How long to wait before attempting next GPS update // Aims to hit position.gps_update_interval by using the lock-time prediction - // Sleep for at least 1 second, so we don't ask GPS hardware to sleep indefinitely with a "0 second PMREQ" - uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 1; + uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0; // If long interval between updates: power off between updates if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { @@ -1130,10 +1129,11 @@ void GPS::clearBuffer() /// Prepare the GPS for the cpu entering deep or light sleep, expect to be gone for at least 100s of msecs int GPS::prepareDeepSleep(void *unused) { - /* - * GPS power was previously set here. - * Now removed, as the same call is already made directly in doDeepSleep. - */ + LOG_INFO("GPS deep sleep!\n"); + + // Manually enter GPSPowerState::OFF, so we can ensure a PMREQ with duration 0 has been sent + setGPSPower(false, false, 0); + return 0; } From 2fb91d5e3281090edd61edf525d12366b8d2df07 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 13 Jun 2024 11:43:44 +1200 Subject: [PATCH 09/30] Revert "Attempt: Send PMREQ with duration 0 on MCU deep-sleep" This reverts commit 9d29ec7603a88056b9115796b29b5023165a93bb. --- src/gps/GPS.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6a8d6d59c14..8d46742baab 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1131,8 +1131,7 @@ int GPS::prepareDeepSleep(void *unused) { LOG_INFO("GPS deep sleep!\n"); - // Manually enter GPSPowerState::OFF, so we can ensure a PMREQ with duration 0 has been sent - setGPSPower(false, false, 0); + setAwake(false); return 0; } From a0726609ac64a1ebc66a222e6a53d50cf403203b Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Sat, 22 Jun 2024 19:13:05 +1200 Subject: [PATCH 10/30] Remove unused notifyGPSSleep Observable Handled with notifyDeepSleep, and enable() / disable() --- src/gps/GPS.cpp | 2 -- src/gps/GPS.h | 1 - src/sleep.cpp | 8 -------- src/sleep.h | 2 -- 4 files changed, 13 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 8d46742baab..6bbee8d7ca0 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -766,7 +766,6 @@ bool GPS::setup() } notifyDeepSleepObserver.observe(¬ifyDeepSleep); - notifyGPSSleepObserver.observe(¬ifyGPSSleep); return true; } @@ -775,7 +774,6 @@ GPS::~GPS() { // we really should unregister our sleep observer notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); - notifyGPSSleepObserver.observe(¬ifyGPSSleep); } void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 34e1844c358..2c5dc3d27b0 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -99,7 +99,6 @@ class GPS : private concurrency::OSThread uint8_t numSatellites = 0; CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); - CallbackObserver notifyGPSSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); public: /** If !NULL we will use this serial port to construct our GPS */ diff --git a/src/sleep.cpp b/src/sleep.cpp index 590610e6c83..317ee2962b1 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -36,10 +36,7 @@ Observable preflightSleep; /// Called to tell observers we are now entering sleep and you should prepare. Must return 0 /// notifySleep will be called for light or deep sleep, notifyDeepSleep is only called for deep sleep -/// notifyGPSSleep will be called when config.position.gps_enabled is set to 0 or from buttonthread when GPS_POWER_TOGGLE is -/// enabled. Observable notifySleep, notifyDeepSleep; -Observable notifyGPSSleep; // deep sleep support RTC_DATA_ATTR int bootCount = 0; @@ -236,11 +233,6 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) pinMode(PIN_POWER_EN, INPUT); // power off peripherals // pinMode(PIN_POWER_EN1, INPUT_PULLDOWN); #endif -#endif -#if HAS_GPS - // Kill GPS power completely (even if previously we just had it in sleep mode) - if (gps) - gps->setGPSPower(false, false, 0); #endif setLed(false); diff --git a/src/sleep.h b/src/sleep.h index 8d5b9a94f34..f154b8d4459 100644 --- a/src/sleep.h +++ b/src/sleep.h @@ -41,8 +41,6 @@ extern Observable notifySleep; /// Called to tell observers we are now entering (deep) sleep and you should prepare. Must return 0 extern Observable notifyDeepSleep; -/// Called to tell GPS thread to enter deep sleep independently of LoRa/MCU sleep, prior to full poweroff. Must return 0 -extern Observable notifyGPSSleep; void enableModemSleep(); #ifdef ARCH_ESP32 void enableLoraInterrupt(); From 576a26d0846a487fa093854b4f8483f4862886a2 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Sat, 22 Jun 2024 19:38:40 +1200 Subject: [PATCH 11/30] WIP: simplify GPS power management An initial attempt only. --- src/gps/GPS.cpp | 473 +++++++++++++++++--------------- src/gps/GPS.h | 45 +-- src/gps/GPSUpdateScheduling.cpp | 92 +++++++ src/gps/GPSUpdateScheduling.h | 28 ++ 4 files changed, 395 insertions(+), 243 deletions(-) create mode 100644 src/gps/GPSUpdateScheduling.cpp create mode 100644 src/gps/GPSUpdateScheduling.h diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 6bbee8d7ca0..ee11388f2b1 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -8,6 +8,7 @@ #include "main.h" // pmu_found #include "sleep.h" +#include "GPSUpdateScheduling.h" #include "cas.h" #include "ubx.h" @@ -21,17 +22,16 @@ #define GPS_RESET_MODE HIGH #endif -// How many minutes of sleep make it worthwhile to power-off the GPS -// Shorter than this, and GPS will only enter standby -// Affected by lock-time, and config.position.gps_update_interval -#ifndef GPS_STANDBY_THRESHOLD_MINUTES -#define GPS_STANDBY_THRESHOLD_MINUTES 15 +// How many minutes between updates makes it worthwhile to power-off the GPS (hard sleep) +// Shorter than this, and GPS will only enter standby (soft sleep) +#ifndef GPS_HARDSLEEP_THRESHOLD_MINUTES +#define GPS_HARDSLEEP_THRESHOLD_MINUTES 15 #endif -// How many seconds of sleep make it worthwhile for the GPS to use powered-on standby -// Shorter than this, and we'll just wait instead -#ifndef GPS_IDLE_THRESHOLD_SECONDS -#define GPS_IDLE_THRESHOLD_SECONDS 10 +// How many seconds between updates make it worthwhile to enter standby (soft sleep) +// Shorter than this, and we'll just remain active instead +#ifndef GPS_SOFTSLEEP_THRESHOLD_SECONDS +#define GPS_SOFTSLEEP_THRESHOLD_SECONDS 10 #endif #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) @@ -42,6 +42,8 @@ HardwareSerial *GPS::_serial_gps = NULL; GPS *gps = nullptr; +GPSUpdateScheduling scheduling; + /// Multiple GPS instances might use the same serial port (in sequence), but we can /// only init that port once. static bool didSerialInit; @@ -51,6 +53,25 @@ uint8_t uBloxProtocolVersion; #define GPS_SOL_EXPIRY_MS 5000 // in millis. give 1 second time to combine different sentences. NMEA Frequency isn't higher anyway #define NMEA_MSG_GXGSA "GNGSA" // GSA message (GPGSA, GNGSA etc) +// For logging +const char *getGPSPowerStateString(GPSPowerState state) +{ + switch (state) { + case GPS_ACTIVE: + return "ACTIVE"; + case GPS_IDLE: + return "IDLE"; + case GPS_SOFTSLEEP: + return "SOFTSLEEP"; + case GPS_HARDSLEEP: + return "HARDSLEEP"; + case GPS_OFF: + return "OFF"; + default: + assert(false); // Unhandled enum value.. + } +} + void GPS::UBXChecksum(uint8_t *message, size_t length) { uint8_t CK_A = 0, CK_B = 0; @@ -776,102 +797,178 @@ GPS::~GPS() notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); } -void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime) +// Put the GPS hardware into a specified state +void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) { - // Record the current powerState - if (on) - powerState = GPS_ACTIVE; - else if (!enabled) // User has disabled with triple press - powerState = GPS_OFF; - else if (sleepTime <= GPS_IDLE_THRESHOLD_SECONDS * 1000UL) - powerState = GPS_IDLE; - else if (standbyOnly) - powerState = GPS_STANDBY; - else - powerState = GPS_OFF; +#ifdef GPS_UC6580 + // Special case: no soft sleep? + // TODO: enquire why + if (newState == GPS_SOFTSLEEP) + newState == GPS_HARDSLEEP; +#endif - LOG_DEBUG("GPS::powerState=%d\n", powerState); + // Update the stored GPSPowerstate, and create local copies + GPSPowerState oldState = powerState; + powerState = newState; + LOG_INFO("GPS power state moving from %s to %s\n", getGPSPowerStateString(oldState), getGPSPowerStateString(newState)); + + switch (newState) { + case GPS_ACTIVE: + case GPS_IDLE: + assert(sleepTime == 0); // sleepTime arg has no impact here! + writePinEN(true); // Power (EN pin): on + setPowerPMU(true); // Power (PMU): on + writePinStandby(true); // Standby (pin): awake + setPowerUBLOX(true); // Standby (UBLOX): awake + break; + + case GPS_SOFTSLEEP: + assert(sleepTime > 0); // This is a timed sleep! + writePinEN(true); // Power (EN pin): on + setPowerPMU(true); // Power (PMU): on + writePinStandby(true); // Standby (pin): asleep + setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed + break; + + case GPS_HARDSLEEP: + assert(sleepTime > 0); // This is a timed sleep! + writePinEN(true); // Power (EN pin): off + setPowerPMU(true); // Power (PMU): off + writePinStandby(true); // Standby (pin): off + setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed + break; + + case GPS_OFF: + assert(sleepTime == 0); // This is an indefinite sleep + writePinEN(false); // Power (EN pin): off + setPowerPMU(false); // Power (PMU): off + writePinStandby(false); // Standby (pin): off + setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely + break; + } +} - // If the next update is due *really soon*, don't actually power off or enter standby. Just wait it out. - if (!on && powerState == GPS_IDLE) +// Set power with EN pin, if relevant +void GPS::writePinEN(bool on) +{ + // Abort: if conflict with Canned Messages when using Wisblock(?) + if (HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1)) return; - if (on) { - clearBuffer(); // drop any old data waiting in the buffer before re-enabling - if (en_gpio) - digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); // turn this on if defined, every time - } - isInPowersave = !on; - if (!standbyOnly && en_gpio != 0 && - !(HW_VENDOR == meshtastic_HardwareModel_RAK4631 && (rotaryEncoderInterruptImpl1 || upDownInterruptImpl1))) { - LOG_DEBUG("GPS powerdown using GPS_EN_ACTIVE\n"); - digitalWrite(en_gpio, on ? GPS_EN_ACTIVE : !GPS_EN_ACTIVE); + // Abort: if pin unset + if (!en_gpio) return; - } -#ifdef HAS_PMU // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, so treat as a standby. - if (pmu_found && PMU) { - uint8_t model = PMU->getChipModel(); - if (model == XPOWERS_AXP2101) { - if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) { - // t-beam v1.2 GNSS power channel - on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3); - } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) { - // t-beam-s3-core GNSS power channel - on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4); - } - } else if (model == XPOWERS_AXP192) { - // t-beam v1.1 GNSS power channel - on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3); - } - return; - } + + // Determine new value for the pin + bool val = GPS_EN_ACTIVE ? on : !on; + + // Write and log + pinMode(en_gpio, OUTPUT); + digitalWrite(en_gpio, val); +#ifdef GPS_EXTRAVERBOSE + LOG_DEBUG("Pin EN %s\n", val == HIGH ? "HIGH" : "LOW"); #endif +} + +// Set the value of the STANDBY pin, if relevant +void GPS::writePinStandby(bool standby) +{ #ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones - if (on) { - LOG_INFO("Waking GPS\n"); - pinMode(PIN_GPS_STANDBY, OUTPUT); - // Some PCB's use an inverse logic due to a transistor driver - // Example for this is the Pico-Waveshare Lora+GPS HAT -#ifdef PIN_GPS_STANDBY_INVERTED - digitalWrite(PIN_GPS_STANDBY, 0); + +// Determine the new value for the pin +// Normally: active HIGH for awake +#if PIN_GPS_STANDBY_INVERTED + bool val = standby; #else - digitalWrite(PIN_GPS_STANDBY, 1); + bool val = !standby; #endif - return; - } else { - LOG_INFO("GPS entering sleep\n"); - // notifyGPSSleep.notifyObservers(NULL); - pinMode(PIN_GPS_STANDBY, OUTPUT); -#ifdef PIN_GPS_STANDBY_INVERTED - digitalWrite(PIN_GPS_STANDBY, 1); -#else - digitalWrite(PIN_GPS_STANDBY, 0); + + // Write and log + pinMode(PIN_GPS_STANDBY, OUTPUT); + digitalWrite(PIN_GPS_STANDBY, val); +#ifdef GPS_EXTRAVERBOSE + LOG_DEBUG("Pin STANDBY %s\n", val == HIGH ? "HIGH" : "LOW"); #endif +#endif +} + +// Enable / Disable GPS with PMU, if present +void GPS::setPowerPMU(bool on) +{ + // We only have PMUs on the T-Beam, and that board has a tiny battery to save GPS ephemera, + // so treat as a standby. +#ifdef HAS_PMU + // Abort: if no PMU + if (!pmu_found) + return; + + // Abort: if PMU not initialized + if (!PMU) return; + + uint8_t model = PMU->getChipModel(); + if (model == XPOWERS_AXP2101) { + if (HW_VENDOR == meshtastic_HardwareModel_TBEAM) { + // t-beam v1.2 GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_ALDO3) : PMU->disablePowerOutput(XPOWERS_ALDO3); + } else if (HW_VENDOR == meshtastic_HardwareModel_LILYGO_TBEAM_S3_CORE) { + // t-beam-s3-core GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_ALDO4) : PMU->disablePowerOutput(XPOWERS_ALDO4); + } + } else if (model == XPOWERS_AXP192) { + // t-beam v1.1 GNSS power channel + on ? PMU->enablePowerOutput(XPOWERS_LDO3) : PMU->disablePowerOutput(XPOWERS_LDO3); } + +#ifdef GPS_EXTRAVERBOSE + LOG_DEBUG("PMU %s\n", on ? "on" : "off"); #endif - if (!on) { - if (gnssModel == GNSS_MODEL_UBLOX) { - uint8_t msglen; - LOG_DEBUG("Sleep Time: %i\n", sleepTime); - if (strncmp(info.hwVersion, "000A0000", 8) != 0) { - for (int i = 0; i < 4; i++) { - gps->_message_PMREQ[0 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet - } - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ); - } else { - for (int i = 0; i < 4; i++) { - gps->_message_PMREQ_10[4 + i] = sleepTime >> (i * 8); // Encode the sleep time in millis into the packet - } - msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10); - } - gps->_serial_gps->write(gps->UBXscratch, msglen); - } - } else { - if (gnssModel == GNSS_MODEL_UBLOX) { - gps->_serial_gps->write(0xFF); - clearBuffer(); // This often returns old data, so drop it +#endif +} + +// Set UBLOX power, if relevant +void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) +{ + // Abort: if not UBLOX hardware + if (gnssModel != GNSS_MODEL_UBLOX) + return; + + // If waking + if (on) { + gps->_serial_gps->write(0xFF); + clearBuffer(); // This ofter returns old data, so drop it +#ifdef GPS_EXTRAVERBOSE + LOG_DEBUG("UBLOX: wake\n"); +#endif + } + + // If putting to sleep + else { + uint8_t msglen; + + // Determine hardware version + if (strncmp(info.hwVersion, "000A0000", 8) != 0) { + // Encode the sleep time in millis into the packet + for (int i = 0; i < 4; i++) + gps->_message_PMREQ[0 + i] = sleepMs >> (i * 8); + + // Record the message length + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ), gps->_message_PMREQ); + } else { + // Encode the sleep time in millis into the packet + for (int i = 0; i < 4; i++) + gps->_message_PMREQ_10[4 + i] = sleepMs >> (i * 8); + + // Record the message length + msglen = gps->makeUBXPacket(0x02, 0x41, sizeof(_message_PMREQ_10), gps->_message_PMREQ_10); } + + // Send the UBX packet + gps->_serial_gps->write(gps->UBXscratch, msglen); + +#ifdef GPS_EXTRAVERBOSE + LOG_DEBUG("UBLOX: sleep for %dmS\n", sleepMs); +#endif } } @@ -884,78 +981,32 @@ void GPS::setConnected() } } -/** - * Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode - * - * calls sleep/wake - */ -void GPS::setAwake(bool wantAwake) +// We want a GPS lock. Wake the hardware +void GPS::up() { + scheduling.informSearching(); + setPowerState(GPS_ACTIVE); +} - // If user has disabled GPS, make sure it is off, not just in standby or idle - if (!wantAwake && !enabled && powerState != GPS_OFF) { - setGPSPower(false, false, 0); - return; - } - - // If GPS power state needs to change - if ((wantAwake && powerState != GPS_ACTIVE) || (!wantAwake && powerState == GPS_ACTIVE)) { - LOG_DEBUG("WANT GPS=%d\n", wantAwake); - - // Calculate how long it takes to get a GPS lock - if (wantAwake) { - // Record the time we start looking for a lock - lastWakeStartMsec = millis(); - } else { - // Record by how much we missed our ideal target postion.gps_update_interval (for logging only) - // Need to calculate this before we update lastSleepStartMsec, to make the new prediction - int32_t lateByMsec = (int32_t)(millis() - lastSleepStartMsec) - (int32_t)getSleepTime(); - - // Record the time we finish looking for a lock - lastSleepStartMsec = millis(); - - // How long did it take to get GPS lock this time? - uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec; +// We've got a GPS lock. Enter a low power state, potentially. +void GPS::down() +{ + scheduling.informGotLock(); + uint32_t sleepTime = scheduling.msUntilNextSearch(); - // Update the lock-time prediction - // Used pre-emptively, attempting to hit target of gps.position_update_interval - switch (GPSCycles) { - case 0: - LOG_DEBUG("Initial GPS lock took %ds\n", lockTime / 1000); - break; - case 1: - predictedLockTime = lockTime; // Avoid slow ramp-up - start with a real value - LOG_DEBUG("GPS Lock took %ds\n", lockTime / 1000); - break; - default: - // Predict lock-time using exponential smoothing: respond slowly to changes - predictedLockTime = (lockTime * 0.2) + (predictedLockTime * 0.8); // Latest lock time has 20% weight on prediction - LOG_INFO("GPS Lock took %ds. %s by %ds. Next lock predicted to take %ds.\n", lockTime / 1000, - (lateByMsec > 0) ? "Late" : "Early", abs(lateByMsec) / 1000, predictedLockTime / 1000); - } - GPSCycles++; - } + LOG_DEBUG("%us until next search\n", sleepTime / 1000); - // How long to wait before attempting next GPS update - // Aims to hit position.gps_update_interval by using the lock-time prediction - uint32_t compensatedSleepTime = (getSleepTime() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0; + // If long interval between updates: hard sleep (power off) + if (sleepTime > GPS_HARDSLEEP_THRESHOLD_MINUTES * MS_IN_MINUTE) + setPowerState(GPS_HARDSLEEP, sleepTime); - // If long interval between updates: power off between updates - if (compensatedSleepTime > GPS_STANDBY_THRESHOLD_MINUTES * MS_IN_MINUTE) { - setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime); - } + // If moderate interval between updates: soft sleep (standby) + else if (sleepTime > GPS_SOFTSLEEP_THRESHOLD_SECONDS * 1000UL) + setPowerState(GPS_SOFTSLEEP, sleepTime); - // If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time - // We'll either use a "powered-on" standby, or just wait it out, depending on how soon the next update is due - // Will decide which inside setGPSPower method - else { -#ifdef GPS_UC6580 - setGPSPower(wantAwake, false, compensatedSleepTime); -#else - setGPSPower(wantAwake, true, compensatedSleepTime); -#endif - } - } + // If short interval between updates: just wait + else + setPowerState(GPS_IDLE); } /** Get how long we should stay looking for each acquisition in msecs @@ -970,22 +1021,6 @@ uint32_t GPS::getWakeTime() const return Default::getConfiguredOrDefaultMs(t, default_broadcast_interval_secs); } -/** Get how long we should sleep between aqusition attempts in msecs - */ -uint32_t GPS::getSleepTime() const -{ - uint32_t t = config.position.gps_update_interval; - - // We'll not need the GPS thread to wake up again after first acq. with fixed position. - if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED || config.position.fixed_position) - t = UINT32_MAX; // Sleep forever now - - if (t == UINT32_MAX) - return t; // already maxint - - return Default::getConfiguredOrDefaultMs(t, default_gps_update_interval); -} - void GPS::publishUpdate() { if (shouldPublish) { @@ -1034,13 +1069,13 @@ int32_t GPS::runOnce() return disable(); } - if (whileIdle()) { + if (whileActive()) { // if we have received valid NMEA claim we are connected setConnected(); } else { if ((config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) && (gnssModel == GNSS_MODEL_UBLOX)) { // reset the GPS on next bootup - if (devicestate.did_gps_reset && (millis() - lastWakeStartMsec > 60000) && !hasFlow()) { + if (devicestate.did_gps_reset && scheduling.elapsedSearchMs() > 60 * 1000UL && !hasFlow()) { LOG_DEBUG("GPS is not communicating, trying factory reset on next bootup.\n"); devicestate.did_gps_reset = false; nodeDB->saveDeviceStateToDisk(); @@ -1055,54 +1090,42 @@ int32_t GPS::runOnce() // gps->factoryReset(); } - // If we are overdue for an update, turn on the GPS and at least publish the current status - uint32_t now = millis(); - uint32_t timeAsleep = now - lastSleepStartMsec; + // If we're due for an update, wake the GPS + if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue()) + up(); - auto sleepTime = getSleepTime(); - if (powerState != GPS_ACTIVE && (sleepTime != UINT32_MAX) && - ((timeAsleep > sleepTime) || (isInPowersave && timeAsleep > (sleepTime - predictedLockTime)))) { - // We now want to be awake - so wake up the GPS - setAwake(true); + // If we've already set time from the GPS, no need to ask the GPS + bool gotTime = (getRTCQuality() >= RTCQualityGPS); + if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time + gotTime = true; + shouldPublish = true; } - // While we are awake - if (powerState == GPS_ACTIVE) { - // LOG_DEBUG("looking for location\n"); - // If we've already set time from the GPS, no need to ask the GPS - bool gotTime = (getRTCQuality() >= RTCQualityGPS); - if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time - gotTime = true; - shouldPublish = true; - } - - bool gotLoc = lookForLocation(); - if (gotLoc && !hasValidLocation) { // declare that we have location ASAP - LOG_DEBUG("hasValidLocation RISING EDGE\n"); - hasValidLocation = true; - shouldPublish = true; - } + bool gotLoc = lookForLocation(); + if (gotLoc && !hasValidLocation) { // declare that we have location ASAP + LOG_DEBUG("hasValidLocation RISING EDGE\n"); + hasValidLocation = true; + shouldPublish = true; + } - now = millis(); - auto wakeTime = getWakeTime(); - bool tooLong = wakeTime != UINT32_MAX && (now - lastWakeStartMsec) > wakeTime; + auto wakeTime = getWakeTime(); + bool tooLong = wakeTime != UINT32_MAX && scheduling.elapsedSearchMs() > wakeTime; - // Once we get a location we no longer desperately want an update - // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); - if ((gotLoc && gotTime) || tooLong) { + // Once we get a location we no longer desperately want an update + // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); + if ((gotLoc && gotTime) || tooLong) { - if (tooLong) { - // we didn't get a location during this ack window, therefore declare loss of lock - if (hasValidLocation) { - LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc); - } - p = meshtastic_Position_init_default; - hasValidLocation = false; + if (tooLong) { + // we didn't get a location during this ack window, therefore declare loss of lock + if (hasValidLocation) { + LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc); } - - setAwake(false); - shouldPublish = true; // publish our update for this just finished acquisition window + p = meshtastic_Position_init_default; + hasValidLocation = false; } + + down(); + shouldPublish = true; // publish our update for this just finished acquisition window } // If state has changed do a publish @@ -1128,9 +1151,7 @@ void GPS::clearBuffer() int GPS::prepareDeepSleep(void *unused) { LOG_INFO("GPS deep sleep!\n"); - - setAwake(false); - + disable(); return 0; } @@ -1346,7 +1367,7 @@ GPS *GPS::createGps() LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n"); #endif - new_gps->setGPSPower(true, false, 0); + new_gps->up(); #ifdef PIN_GPS_RESET pinMode(PIN_GPS_RESET, OUTPUT); @@ -1354,7 +1375,6 @@ GPS *GPS::createGps() delay(10); digitalWrite(PIN_GPS_RESET, !GPS_RESET_MODE); #endif - new_gps->setAwake(true); // Wake GPS power before doing any init if (_serial_gps) { #ifdef ARCH_ESP32 @@ -1640,13 +1660,13 @@ bool GPS::hasFlow() return reader.passedChecksum() > 0; } -bool GPS::whileIdle() +bool GPS::whileActive() { unsigned int charsInBuf = 0; bool isValid = false; if (powerState != GPS_ACTIVE) { clearBuffer(); - return (powerState == GPS_ACTIVE); + return false; } #ifdef SERIAL_BUFFER_SIZE if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) { @@ -1677,20 +1697,21 @@ bool GPS::whileIdle() } void GPS::enable() { - // Clear the old lock-time prediction - GPSCycles = 0; - predictedLockTime = 0; + // Clear the old scheduling info (reset the lock-time prediction) + scheduling.reset(); enabled = true; setInterval(GPS_THREAD_INTERVAL); - setAwake(true); + + scheduling.informSearching(); + setPowerState(GPS_ACTIVE); } int32_t GPS::disable() { enabled = false; setInterval(INT32_MAX); - setAwake(false); + setPowerState(GPS_OFF); return INT32_MAX; } diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 2c5dc3d27b0..ef93f96ba45 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -39,10 +39,11 @@ typedef enum { } GPS_RESPONSE; enum GPSPowerState : uint8_t { - GPS_OFF = 0, // Physically powered off - GPS_ACTIVE = 1, // Awake and want a position - GPS_STANDBY = 2, // Physically powered on, but soft-sleeping - GPS_IDLE = 3, // Awake, but not wanting another position yet + GPS_ACTIVE, // Awake and want a position + GPS_IDLE, // Awake, but not wanting another position yet + GPS_SOFTSLEEP, // Physically powered on, but soft-sleeping + GPS_HARDSLEEP, // Physically powered off, but scheduled to wake + GPS_OFF // Powered off indefinitely }; // Generate a string representation of DOP @@ -67,14 +68,11 @@ class GPS : private concurrency::OSThread uint8_t fixType = 0; // fix type from GPGSA #endif private: - uint32_t lastWakeStartMsec = 0, lastSleepStartMsec = 0; const int serialSpeeds[6] = {9600, 4800, 38400, 57600, 115200, 9600}; uint32_t rx_gpio = 0; uint32_t tx_gpio = 0; uint32_t en_gpio = 0; - int32_t predictedLockTime = 0; - uint32_t GPSCycles = 0; int speedSelect = 0; int probeTries = 2; @@ -174,7 +172,8 @@ class GPS : private concurrency::OSThread // toggle between enabled/disabled void toggleGpsMode(); - void setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime); + // Change the power state of the GPS - for power saving / shutdown + void setPowerState(GPSPowerState newState, uint32_t sleepMs = 0); /// Returns true if we have acquired GPS lock. virtual bool hasLock(); @@ -205,18 +204,18 @@ class GPS : private concurrency::OSThread GPS_RESPONSE getACKCas(uint8_t class_id, uint8_t msg_id, uint32_t waitMillis); - /** - * Switch the GPS into a mode where we are actively looking for a lock, or alternatively switch GPS into a low power mode - * - * calls sleep/wake - */ - void setAwake(bool on); virtual bool factoryReset(); // Creates an instance of the GPS class. // Returns the new instance or null if the GPS is not present. static GPS *createGps(); + // Wake the GPS hardware - ready for an update + void up(); + + // Let the GPS hardware save power between updates + void down(); + protected: /** * Perform any processing that should be done only while the GPS is awake and looking for a fix. @@ -239,7 +238,7 @@ class GPS : private concurrency::OSThread * * Return true if we received a valid message from the GPS */ - virtual bool whileIdle(); + virtual bool whileActive(); /** * Perform any processing that should be done only while the GPS is awake and looking for a fix. @@ -270,9 +269,21 @@ class GPS : private concurrency::OSThread */ uint32_t getWakeTime() const; - /** Get how long we should sleep between aqusition attempts + /** Set power with EN pin, if relevant + */ + void writePinEN(bool on); + + /** Set the value of the STANDBY pin, if relevant + */ + void writePinStandby(bool standby); + + /** Set GPS power with PMU, if relevant + */ + void setPowerPMU(bool on); + + /** Set UBLOX power, if relevant */ - uint32_t getSleepTime() const; + void setPowerUBLOX(bool on, uint32_t sleepMs = 0); /** * Tell users we have new GPS readings diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp new file mode 100644 index 00000000000..a8065838ce3 --- /dev/null +++ b/src/gps/GPSUpdateScheduling.cpp @@ -0,0 +1,92 @@ +#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 + remainingMs = max(remainingMs, 0); + LOG_DEBUG("msUntilNextSearch() = %i\n", remainingMs); + + return 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); +} + +// 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; + lockTime = max(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); +} \ No newline at end of file diff --git a/src/gps/GPSUpdateScheduling.h b/src/gps/GPSUpdateScheduling.h new file mode 100644 index 00000000000..7a8bfb91a94 --- /dev/null +++ b/src/gps/GPSUpdateScheduling.h @@ -0,0 +1,28 @@ +#pragma once + +#include "configuration.h" + +// Encapsulates code responsible for the timing of GPS updates +class GPSUpdateScheduling +{ + public: + // Marks the time of these events, for calculation use + void informSearching(); + void informGotLock(); // Predicted lock-time is recalculated here + + void reset(); // Reset the prediction - after GPS::disable() / GPS::enable() + bool isUpdateDue(); // Is it time to begin searching for a GPS position? + + uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep + uint32_t elapsedSearchMs(); // How long have we been searching so far? + + private: + void updateLockTimePrediction(); // Called from informGotLock + uint32_t nextSearchAtMs(); // Used by msUntilNextSearch & elapsedSearchMs + uint32_t searchStartedMs = 0; + uint32_t searchEndedMs = 0; + uint32_t searchCount = 0; + uint32_t predictedMsToGetLock = 0; + + const float weighting = 0.2; // Controls exponential smoothing of lock-times prediction. 20% weighting of "latest lock-time". +}; \ No newline at end of file From 3b244b94b4859fe1b62feeae46ae288aecf2d9b9 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Sat, 22 Jun 2024 20:42:31 +1200 Subject: [PATCH 12/30] Honor #3e9e0fd --- src/sleep.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sleep.cpp b/src/sleep.cpp index 317ee2962b1..52f3b00be7d 100644 --- a/src/sleep.cpp +++ b/src/sleep.cpp @@ -228,11 +228,9 @@ void doDeepSleep(uint32_t msecToWake, bool skipPreflight = false) nodeDB->saveToDisk(); -#ifdef TTGO_T_ECHO #ifdef PIN_POWER_EN pinMode(PIN_POWER_EN, INPUT); // power off peripherals // pinMode(PIN_POWER_EN1, INPUT_PULLDOWN); -#endif #endif setLed(false); From 603f84c8e5c57b4ccd37742fa13364fbe3e0e77a Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 25 Jun 2024 02:44:02 +1200 Subject: [PATCH 13/30] No-op when moving between GPS_IDLE and GPS_ACTIVE --- src/gps/GPS.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index ee11388f2b1..3a22d3d5f76 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -815,6 +815,8 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) switch (newState) { case GPS_ACTIVE: case GPS_IDLE: + if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed + break; assert(sleepTime == 0); // sleepTime arg has no impact here! writePinEN(true); // Power (EN pin): on setPowerPMU(true); // Power (PMU): on From 18b3c766d18780b9d84832083aa65ffc843555cc Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 25 Jun 2024 02:50:26 +1200 Subject: [PATCH 14/30] Ensure U-blox GPS is awake to receive indefinite sleep command --- src/gps/GPS.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 3a22d3d5f76..e0564e6e2e2 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -948,6 +948,12 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) else { uint8_t msglen; + // If we're being asked to sleep indefinitely, make *sure* we're awake first, to process the new sleep command + if (sleepMs == 0) { + setPowerUBLOX(true); + delay(100); + } + // Determine hardware version if (strncmp(info.hwVersion, "000A0000", 8) != 0) { // Encode the sleep time in millis into the packet @@ -1722,11 +1728,11 @@ void GPS::toggleGpsMode() { if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_ENABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_DISABLED; - LOG_DEBUG("Flag set to false for gps power. GpsMode: DISABLED\n"); + LOG_INFO("User toggled GpsMode. Now DISABLED.\n"); disable(); } else if (config.position.gps_mode == meshtastic_Config_PositionConfig_GpsMode_DISABLED) { config.position.gps_mode = meshtastic_Config_PositionConfig_GpsMode_ENABLED; - LOG_DEBUG("Flag set to true to restore power. GpsMode: ENABLED\n"); + LOG_INFO("User toggled GpsMode. Now ENABLED\n"); enable(); } } From e94b0e3b5f59e76a55bf5a79ca6e68083e3ff593 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 25 Jun 2024 04:17:31 +1200 Subject: [PATCH 15/30] Longer pause when waking U-blox to send sleep command --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index e0564e6e2e2..3c73bc566c1 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -951,7 +951,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) // If we're being asked to sleep indefinitely, make *sure* we're awake first, to process the new sleep command if (sleepMs == 0) { setPowerUBLOX(true); - delay(100); + delay(500); } // Determine hardware version From 5519503b00a7f4dc21bb47a3a0f89a0aeef696e8 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 25 Jun 2024 18:44:00 +1200 Subject: [PATCH 16/30] Actually implement soft and hard sleep.. --- src/gps/GPS.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 3c73bc566c1..baa6a53eb45 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -815,28 +815,28 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) switch (newState) { case GPS_ACTIVE: case GPS_IDLE: + assert(sleepTime == 0); // sleepTime arg has no impact here! if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed break; - assert(sleepTime == 0); // sleepTime arg has no impact here! - writePinEN(true); // Power (EN pin): on - setPowerPMU(true); // Power (PMU): on - writePinStandby(true); // Standby (pin): awake - setPowerUBLOX(true); // Standby (UBLOX): awake + writePinEN(true); // Power (EN pin): on + setPowerPMU(true); // Power (PMU): on + writePinStandby(true); // Standby (pin): awake + setPowerUBLOX(true); // Standby (UBLOX): awake break; case GPS_SOFTSLEEP: assert(sleepTime > 0); // This is a timed sleep! writePinEN(true); // Power (EN pin): on setPowerPMU(true); // Power (PMU): on - writePinStandby(true); // Standby (pin): asleep + writePinStandby(false); // Standby (pin): asleep setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed break; case GPS_HARDSLEEP: assert(sleepTime > 0); // This is a timed sleep! - writePinEN(true); // Power (EN pin): off - setPowerPMU(true); // Power (PMU): off - writePinStandby(true); // Standby (pin): off + writePinEN(false); // Power (EN pin): off + setPowerPMU(false); // Power (PMU): off + writePinStandby(false); // Standby (pin): asleep setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed break; @@ -844,7 +844,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) assert(sleepTime == 0); // This is an indefinite sleep writePinEN(false); // Power (EN pin): off setPowerPMU(false); // Power (PMU): off - writePinStandby(false); // Standby (pin): off + writePinStandby(false); // Standby (pin): asleep setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely break; } From 6da644b74c4e5096fc2e10471213f5eddc7a2c5d Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Thu, 27 Jun 2024 01:49:27 +1200 Subject: [PATCH 17/30] Dynamically estimate the threshold for GPS_HARDSLEEP --- src/gps/GPS.cpp | 43 ++++++++++++++++----------------- src/gps/GPSUpdateScheduling.cpp | 6 +++++ src/gps/GPSUpdateScheduling.h | 2 +- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index baa6a53eb45..77651b8b4e3 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -22,18 +22,6 @@ #define GPS_RESET_MODE HIGH #endif -// How many minutes between updates makes it worthwhile to power-off the GPS (hard sleep) -// Shorter than this, and GPS will only enter standby (soft sleep) -#ifndef GPS_HARDSLEEP_THRESHOLD_MINUTES -#define GPS_HARDSLEEP_THRESHOLD_MINUTES 15 -#endif - -// How many seconds between updates make it worthwhile to enter standby (soft sleep) -// Shorter than this, and we'll just remain active instead -#ifndef GPS_SOFTSLEEP_THRESHOLD_SECONDS -#define GPS_SOFTSLEEP_THRESHOLD_SECONDS 10 -#endif - #if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO) HardwareSerial *GPS::_serial_gps = &Serial1; #else @@ -1000,21 +988,32 @@ void GPS::up() void GPS::down() { scheduling.informGotLock(); + uint32_t predictedSearchDuration = scheduling.predictedSearchDurationMs(); uint32_t sleepTime = scheduling.msUntilNextSearch(); + uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval); LOG_DEBUG("%us until next search\n", sleepTime / 1000); - // If long interval between updates: hard sleep (power off) - if (sleepTime > GPS_HARDSLEEP_THRESHOLD_MINUTES * MS_IN_MINUTE) - setPowerState(GPS_HARDSLEEP, sleepTime); - - // If moderate interval between updates: soft sleep (standby) - else if (sleepTime > GPS_SOFTSLEEP_THRESHOLD_SECONDS * 1000UL) - setPowerState(GPS_SOFTSLEEP, sleepTime); - - // If short interval between updates: just wait - else + // If update interval less than 10 seconds, no attempt to sleep + if (updateInterval <= 10 * 1000UL) setPowerState(GPS_IDLE); + + else { + // How long does gps_update_interval need to be to justify hardsleep? + // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 + // https://www.desmos.com/calculator/6gvjghoumr + // This is not particularly accurate, but probably an impromevement over a single, fixed threshold + uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); + LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000); + + // If update interval too short: softsleep + if (updateInterval < hardsleepThreshold) + setPowerState(GPS_SOFTSLEEP, sleepTime); + + // If update interval long enough: hardsleep + else + setPowerState(GPS_HARDSLEEP, sleepTime); + } } /** Get how long we should stay looking for each acquisition in msecs diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index a8065838ce3..590512f78f8 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -89,4 +89,10 @@ void GPSUpdateScheduling::updateLockTimePrediction() 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; } \ No newline at end of file diff --git a/src/gps/GPSUpdateScheduling.h b/src/gps/GPSUpdateScheduling.h index 7a8bfb91a94..4e549013903 100644 --- a/src/gps/GPSUpdateScheduling.h +++ b/src/gps/GPSUpdateScheduling.h @@ -15,10 +15,10 @@ class GPSUpdateScheduling uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep uint32_t elapsedSearchMs(); // How long have we been searching so far? + uint32_t predictedSearchDurationMs(); // How long do we expect to spend searching for a lock? private: void updateLockTimePrediction(); // Called from informGotLock - uint32_t nextSearchAtMs(); // Used by msUntilNextSearch & elapsedSearchMs uint32_t searchStartedMs = 0; uint32_t searchEndedMs = 0; uint32_t searchCount = 0; From 93756c9bb0bc97b8bc1eca464987034055107b38 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 04:55:43 +1200 Subject: [PATCH 18/30] Fallback to GPS_HARDSLEEP, if GPS_SOFTSLEEP unsupported --- src/gps/GPS.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 77651b8b4e3..93fba03b419 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -788,13 +788,6 @@ GPS::~GPS() // Put the GPS hardware into a specified state void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) { -#ifdef GPS_UC6580 - // Special case: no soft sleep? - // TODO: enquire why - if (newState == GPS_SOFTSLEEP) - newState == GPS_HARDSLEEP; -#endif - // Update the stored GPSPowerstate, and create local copies GPSPowerState oldState = powerState; powerState = newState; @@ -999,18 +992,27 @@ void GPS::down() setPowerState(GPS_IDLE); else { - // How long does gps_update_interval need to be to justify hardsleep? + // Check whether the GPS hardware is capable of GPS_SOFTSLEEP + // If not, fallback to GPS_HARDSLEEP instead + bool softsleepSupported = false; + if (gnssModel != GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ + softsleepSupported = true; + #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin + softsleepSupported = true; + #endif + + // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 // https://www.desmos.com/calculator/6gvjghoumr // This is not particularly accurate, but probably an impromevement over a single, fixed threshold uint32_t hardsleepThreshold = (2750 * pow(predictedSearchDuration / 1000, 1.22)); LOG_DEBUG("gps_update_interval >= %us needed to justify hardsleep\n", hardsleepThreshold / 1000); - // If update interval too short: softsleep - if (updateInterval < hardsleepThreshold) + // If update interval too short: softsleep (if supported by hardware) + if (softsleepSupported && updateInterval < hardsleepThreshold) setPowerState(GPS_SOFTSLEEP, sleepTime); - // If update interval long enough: hardsleep + // If update interval long enough (or softsleep unsupported): hardsleep instead else setPowerState(GPS_HARDSLEEP, sleepTime); } From 4d23a828485060b73d1c61c290637cbcaba286a0 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 04:57:43 +1200 Subject: [PATCH 19/30] Move "excessive search time" behavior to scheduler class --- src/gps/GPS.cpp | 15 +-------------- src/gps/GPS.h | 4 ---- src/gps/GPSUpdateScheduling.cpp | 17 +++++++++++++++++ src/gps/GPSUpdateScheduling.h | 1 + 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 93fba03b419..f7463f428bb 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1018,18 +1018,6 @@ void GPS::down() } } -/** Get how long we should stay looking for each acquisition in msecs - */ -uint32_t GPS::getWakeTime() const -{ - uint32_t t = config.position.position_broadcast_secs; - - if (t == UINT32_MAX) - return t; // already maxint - - return Default::getConfiguredOrDefaultMs(t, default_broadcast_interval_secs); -} - void GPS::publishUpdate() { if (shouldPublish) { @@ -1117,8 +1105,7 @@ int32_t GPS::runOnce() shouldPublish = true; } - auto wakeTime = getWakeTime(); - bool tooLong = wakeTime != UINT32_MAX && scheduling.elapsedSearchMs() > wakeTime; + bool tooLong = scheduling.searchedTooLong(); // Once we get a location we no longer desperately want an update // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 11085360a64..7cbf771bccd 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -265,10 +265,6 @@ class GPS : private concurrency::OSThread void UBXChecksum(uint8_t *message, size_t length); void CASChecksum(uint8_t *message, size_t length); - /** Get how long we should stay looking for each aquisition - */ - uint32_t getWakeTime() const; - /** Set power with EN pin, if relevant */ void writePinEN(bool on); diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index 590512f78f8..7334b3c5438 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -67,6 +67,23 @@ 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() { diff --git a/src/gps/GPSUpdateScheduling.h b/src/gps/GPSUpdateScheduling.h index 4e549013903..0e73010ad52 100644 --- a/src/gps/GPSUpdateScheduling.h +++ b/src/gps/GPSUpdateScheduling.h @@ -12,6 +12,7 @@ class GPSUpdateScheduling void reset(); // Reset the prediction - after GPS::disable() / GPS::enable() bool isUpdateDue(); // Is it time to begin searching for a GPS position? + bool searchedTooLong(); // Have we been searching for too long? uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep uint32_t elapsedSearchMs(); // How long have we been searching so far? From 68a8b9cd01bc4953cc873c14b15c26e089953775 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 04:58:32 +1200 Subject: [PATCH 20/30] Minor logging adjustments --- src/gps/GPS.cpp | 4 +++- src/gps/GPSUpdateScheduling.cpp | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index f7463f428bb..37e8b01270a 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1106,6 +1106,8 @@ int32_t GPS::runOnce() } bool tooLong = scheduling.searchedTooLong(); + if (tooLong) + LOG_INFO("Searching for GPS lock taking too long. Giving up; try again later\n"); // Once we get a location we no longer desperately want an update // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); @@ -1114,7 +1116,7 @@ int32_t GPS::runOnce() if (tooLong) { // we didn't get a location during this ack window, therefore declare loss of lock if (hasValidLocation) { - LOG_DEBUG("hasValidLocation FALLING EDGE (last read: %d)\n", gotLoc); + LOG_DEBUG("hasValidLocation FALLING EDGE\n"); } p = meshtastic_Position_init_default; hasValidLocation = false; diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index 7334b3c5438..1d1ea5492c9 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -43,7 +43,6 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch() // If we should have already started (negative value), start ASAP remainingMs = max(remainingMs, 0); - LOG_DEBUG("msUntilNextSearch() = %i\n", remainingMs); return remainingMs; } From f6cd45f1cb6bcb0424f5663cbd1ae1a534e67a48 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 05:31:06 +1200 Subject: [PATCH 21/30] Promote log to warning --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 37e8b01270a..13af9b89e92 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1107,7 +1107,7 @@ int32_t GPS::runOnce() bool tooLong = scheduling.searchedTooLong(); if (tooLong) - LOG_INFO("Searching for GPS lock taking too long. Giving up; try again later\n"); + LOG_WARN("Searching for GPS lock taking too long: giving up for now\n"); // Once we get a location we no longer desperately want an update // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); From c50b43292c72617500067d172972892272e3510c Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 13:55:56 +1200 Subject: [PATCH 22/30] Gratuitous buffer clearing on boot --- src/gps/GPS.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 13af9b89e92..0be1c05fe71 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -799,6 +799,8 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) assert(sleepTime == 0); // sleepTime arg has no impact here! if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed break; + if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer + clearBuffer(); writePinEN(true); // Power (EN pin): on setPowerPMU(true); // Power (PMU): on writePinStandby(true); // Standby (pin): awake @@ -919,7 +921,7 @@ void GPS::setPowerUBLOX(bool on, uint32_t sleepMs) // If waking if (on) { gps->_serial_gps->write(0xFF); - clearBuffer(); // This ofter returns old data, so drop it + clearBuffer(); // This often returns old data, so drop it #ifdef GPS_EXTRAVERBOSE LOG_DEBUG("UBLOX: wake\n"); #endif From a687f2c86643fce1f595ef1d65de3ad5a3739fa0 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 14:38:21 +1200 Subject: [PATCH 23/30] 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. --- src/gps/GPS.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 0be1c05fe71..92a1f420b03 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -801,17 +801,17 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) break; if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer clearBuffer(); - writePinEN(true); // Power (EN pin): on - setPowerPMU(true); // Power (PMU): on - writePinStandby(true); // Standby (pin): awake - setPowerUBLOX(true); // Standby (UBLOX): awake + writePinEN(true); // Power (EN pin): on + setPowerPMU(true); // Power (PMU): on + writePinStandby(false); // Standby (pin): awake (not standby) + setPowerUBLOX(true); // Standby (UBLOX): awake break; case GPS_SOFTSLEEP: assert(sleepTime > 0); // This is a timed sleep! writePinEN(true); // Power (EN pin): on setPowerPMU(true); // Power (PMU): on - writePinStandby(false); // Standby (pin): asleep + writePinStandby(true); // Standby (pin): asleep (not awake) setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed break; @@ -819,7 +819,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) assert(sleepTime > 0); // This is a timed sleep! writePinEN(false); // Power (EN pin): off setPowerPMU(false); // Power (PMU): off - writePinStandby(false); // Standby (pin): asleep + writePinStandby(true); // Standby (pin): asleep (not awake) setPowerUBLOX(false, sleepTime); // Standby (UBLOX): asleep, timed break; @@ -856,6 +856,7 @@ void GPS::writePinEN(bool on) } // Set the value of the STANDBY pin, if relevant +// true for standby state, false for awake void GPS::writePinStandby(bool standby) { #ifdef PIN_GPS_STANDBY // Specifically the standby pin for L76B, L76K and clones @@ -999,9 +1000,9 @@ void GPS::down() bool softsleepSupported = false; if (gnssModel != GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ softsleepSupported = true; - #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin +#ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin softsleepSupported = true; - #endif +#endif // How long does gps_update_interval need to be, for GPS_HARDSLEEP to become more efficient than GPS_SOFTSLEEP? // Heuristic equation. A compromise manually fitted to power observations from U-blox NEO-6M and M10050 From 5e69c7e069028da9680f9799c4249b0862702d00 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 14:40:10 +1200 Subject: [PATCH 24/30] Remove redundant pin init Now handled by setPowerState --- src/gps/GPS.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 92a1f420b03..1a035e9db00 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1348,12 +1348,6 @@ GPS *GPS::createGps() new_gps->tx_gpio = _tx_gpio; new_gps->en_gpio = _en_gpio; - if (_en_gpio != 0) { - LOG_DEBUG("Setting %d to output.\n", _en_gpio); - pinMode(_en_gpio, OUTPUT); - digitalWrite(_en_gpio, !GPS_EN_ACTIVE); - } - #ifdef PIN_GPS_PPS // pulse per second pinMode(PIN_GPS_PPS, INPUT); @@ -1368,6 +1362,7 @@ GPS *GPS::createGps() LOG_DEBUG("Using " NMEA_MSG_GXGSA " for 3DFIX and PDOP\n"); #endif + // Make sure the GPS is awake before performing any init. new_gps->up(); #ifdef PIN_GPS_RESET From 1337d717d1ecc3c594b332dad30a8e34c202becf Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 14:54:38 +1200 Subject: [PATCH 25/30] Replace max() with if statements Avoid those platform specific implementations.. --- src/gps/GPSUpdateScheduling.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index 1d1ea5492c9..fcae16239b2 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -42,9 +42,10 @@ uint32_t GPSUpdateScheduling::msUntilNextSearch() int32_t remainingMs = compensatedStart - now; // If we should have already started (negative value), start ASAP - remainingMs = max(remainingMs, 0); + if (remainingMs < 0) + remainingMs = 0; - return remainingMs; + return (uint32_t)remainingMs; } // How long have we already been searching? @@ -90,7 +91,8 @@ void GPSUpdateScheduling::updateLockTimePrediction() // How long did it take to get GPS lock this time? // Duration between down() calls int32_t lockTime = searchEndedMs - searchStartedMs; - lockTime = max(lockTime, 0); + if (lockTime < 0) + lockTime = 0; // Ignore the first lock-time: likely to be long, will skew data From 7118b475574cb53f1a55ea42695cec5f83107e04 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 14:57:50 +1200 Subject: [PATCH 26/30] Trunk formatting New round of settings.json changes keep catching me out, have to remember to re-enable my "clang-format" for windows workaround. --- src/gps/GPSUpdateScheduling.cpp | 8 +++++--- src/gps/GPSUpdateScheduling.h | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/gps/GPSUpdateScheduling.cpp b/src/gps/GPSUpdateScheduling.cpp index fcae16239b2..949ef603975 100644 --- a/src/gps/GPSUpdateScheduling.cpp +++ b/src/gps/GPSUpdateScheduling.cpp @@ -68,9 +68,11 @@ bool GPSUpdateScheduling::isUpdateDue() } // 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); - +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; diff --git a/src/gps/GPSUpdateScheduling.h b/src/gps/GPSUpdateScheduling.h index 0e73010ad52..7e121c9b688 100644 --- a/src/gps/GPSUpdateScheduling.h +++ b/src/gps/GPSUpdateScheduling.h @@ -10,8 +10,8 @@ class GPSUpdateScheduling void informSearching(); void informGotLock(); // Predicted lock-time is recalculated here - void reset(); // Reset the prediction - after GPS::disable() / GPS::enable() - bool isUpdateDue(); // Is it time to begin searching for a GPS position? + void reset(); // Reset the prediction - after GPS::disable() / GPS::enable() + bool isUpdateDue(); // Is it time to begin searching for a GPS position? bool searchedTooLong(); // Have we been searching for too long? uint32_t msUntilNextSearch(); // How long until we need to begin searching for a GPS? Info provided to GPS hardware for sleep From 5a573d064637824ebcad75ac1b6363ba8d798b43 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 18:08:33 +1200 Subject: [PATCH 27/30] 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. --- src/gps/GPS.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 1a035e9db00..8877d2f0817 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -796,7 +796,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) switch (newState) { case GPS_ACTIVE: case GPS_IDLE: - assert(sleepTime == 0); // sleepTime arg has no impact here! if (oldState == GPS_ACTIVE || oldState == GPS_IDLE) // If hardware already awake, no changes needed break; if (oldState != GPS_ACTIVE && oldState != GPS_IDLE) // If hardware just waking now, clear buffer @@ -808,7 +807,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) break; case GPS_SOFTSLEEP: - assert(sleepTime > 0); // This is a timed sleep! writePinEN(true); // Power (EN pin): on setPowerPMU(true); // Power (PMU): on writePinStandby(true); // Standby (pin): asleep (not awake) @@ -816,7 +814,6 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) break; case GPS_HARDSLEEP: - assert(sleepTime > 0); // This is a timed sleep! writePinEN(false); // Power (EN pin): off setPowerPMU(false); // Power (PMU): off writePinStandby(true); // Standby (pin): asleep (not awake) From 69a2656e49ee58f7efdeffc141501b8e95cf2a1e Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Mon, 1 Jul 2024 19:41:58 +1200 Subject: [PATCH 28/30] Fix an error determining whether GPS_SOFTSLEEP is supported --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 8877d2f0817..b0315097a5c 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -995,7 +995,7 @@ void GPS::down() // Check whether the GPS hardware is capable of GPS_SOFTSLEEP // If not, fallback to GPS_HARDSLEEP instead bool softsleepSupported = false; - if (gnssModel != GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ + if (gnssModel == GNSS_MODEL_UBLOX) // U-blox is supported via PMREQ softsleepSupported = true; #ifdef PIN_GPS_STANDBY // L76B, L76K and clones have a standby pin softsleepSupported = true; From c753a4fbce22612729582655d5bf9f558fbcbbe0 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 2 Jul 2024 01:53:31 +1200 Subject: [PATCH 29/30] Clarify a log entry --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index b0315097a5c..55c31fd0901 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -1107,7 +1107,7 @@ int32_t GPS::runOnce() bool tooLong = scheduling.searchedTooLong(); if (tooLong) - LOG_WARN("Searching for GPS lock taking too long: giving up for now\n"); + LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time.\n"); // Once we get a location we no longer desperately want an update // LOG_DEBUG("gotLoc %d, tooLong %d, gotTime %d\n", gotLoc, tooLong, gotTime); From 3f64bb3134855c251a55bcd7a92aecd8fa884822 Mon Sep 17 00:00:00 2001 From: Todd Herbert Date: Tue, 2 Jul 2024 16:01:21 +1200 Subject: [PATCH 30/30] Set PIN_STANDBY for MCU deep-sleep Required to reach TTGO's advertised 0.25mA sleep current for T-Echo. Without this change: ~6mA. --- src/gps/GPS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index 55c31fd0901..4fd34358890 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -824,7 +824,7 @@ void GPS::setPowerState(GPSPowerState newState, uint32_t sleepTime) assert(sleepTime == 0); // This is an indefinite sleep writePinEN(false); // Power (EN pin): off setPowerPMU(false); // Power (PMU): off - writePinStandby(false); // Standby (pin): asleep + writePinStandby(true); // Standby (pin): asleep setPowerUBLOX(false, 0); // Standby (UBLOX): asleep, indefinitely break; }