mirror of
https://github.com/meshtastic/firmware.git
synced 2025-06-09 06:32:06 +00:00
GPS: short update intervals, lock-time prediction (#4070)
* 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 commit8b697cd2a4
. * Revert "Attempt: Send PMREQ with duration 0 on MCU deep-sleep" This reverts commit9d29ec7603
.
This commit is contained in:
parent
16b41b51af
commit
39c9f92c6e
@ -28,6 +28,12 @@
|
|||||||
#define GPS_STANDBY_THRESHOLD_MINUTES 15
|
#define GPS_STANDBY_THRESHOLD_MINUTES 15
|
||||||
#endif
|
#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
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA) || defined(ARCH_ESP32) || defined(ARCH_PORTDUINO)
|
||||||
HardwareSerial *GPS::_serial_gps = &Serial1;
|
HardwareSerial *GPS::_serial_gps = &Serial1;
|
||||||
#else
|
#else
|
||||||
@ -776,14 +782,22 @@ void GPS::setGPSPower(bool on, bool standbyOnly, uint32_t sleepTime)
|
|||||||
{
|
{
|
||||||
// Record the current powerState
|
// Record the current powerState
|
||||||
if (on)
|
if (on)
|
||||||
powerState = GPS_AWAKE;
|
powerState = GPS_ACTIVE;
|
||||||
else if (!on && standbyOnly)
|
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;
|
powerState = GPS_STANDBY;
|
||||||
else
|
else
|
||||||
powerState = GPS_OFF;
|
powerState = GPS_OFF;
|
||||||
|
|
||||||
LOG_DEBUG("GPS::powerState=%d\n", powerState);
|
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_IDLE)
|
||||||
|
return;
|
||||||
|
|
||||||
if (on) {
|
if (on) {
|
||||||
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
|
clearBuffer(); // drop any old data waiting in the buffer before re-enabling
|
||||||
if (en_gpio)
|
if (en_gpio)
|
||||||
@ -880,54 +894,69 @@ void GPS::setConnected()
|
|||||||
void GPS::setAwake(bool wantAwake)
|
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 idle
|
||||||
if (!wantAwake && !enabled && powerState != GPS_OFF) {
|
if (!wantAwake && !enabled && powerState != GPS_OFF) {
|
||||||
setGPSPower(false, false, 0);
|
setGPSPower(false, false, 0);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If GPS power state needs to change
|
// 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);
|
LOG_DEBUG("WANT GPS=%d\n", wantAwake);
|
||||||
|
|
||||||
// Calculate how long it takes to get a GPS lock
|
// Calculate how long it takes to get a GPS lock
|
||||||
if (wantAwake) {
|
if (wantAwake) {
|
||||||
|
// Record the time we start looking for a lock
|
||||||
lastWakeStartMsec = millis();
|
lastWakeStartMsec = millis();
|
||||||
} else {
|
} 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();
|
lastSleepStartMsec = millis();
|
||||||
if (GPSCycles == 1) { // Skipping initial lock time, as it will likely be much longer than average
|
|
||||||
averageLockTime = lastSleepStartMsec - lastWakeStartMsec;
|
// How long did it take to get GPS lock this time?
|
||||||
} else if (GPSCycles > 1) {
|
uint32_t lockTime = lastSleepStartMsec - lastWakeStartMsec;
|
||||||
averageLockTime += ((int32_t)(lastSleepStartMsec - lastWakeStartMsec) - averageLockTime) / (int32_t)GPSCycles;
|
|
||||||
|
// 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++;
|
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() > predictedLockTime) ? (getSleepTime() - predictedLockTime) : 0;
|
||||||
|
|
||||||
// If long interval between updates: power off between updates
|
// 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);
|
setGPSPower(wantAwake, false, getSleepTime() - predictedLockTime);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If waking frequently: standby only. Would use more power trying to reacquire lock each time
|
// If waking relatively frequently: don't power off. Would use more energy trying to reacquire lock each time
|
||||||
else if ((int32_t)getSleepTime() - averageLockTime > 10000) { // 10 seconds is enough for standby
|
// 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
|
#ifdef GPS_UC6580
|
||||||
setGPSPower(wantAwake, false, getSleepTime() - averageLockTime);
|
setGPSPower(wantAwake, false, compensatedSleepTime);
|
||||||
#else
|
#else
|
||||||
setGPSPower(wantAwake, true, getSleepTime() - averageLockTime);
|
setGPSPower(wantAwake, true, compensatedSleepTime);
|
||||||
#endif
|
#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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,14 +1062,14 @@ int32_t GPS::runOnce()
|
|||||||
uint32_t timeAsleep = now - lastSleepStartMsec;
|
uint32_t timeAsleep = now - lastSleepStartMsec;
|
||||||
|
|
||||||
auto sleepTime = getSleepTime();
|
auto sleepTime = getSleepTime();
|
||||||
if (powerState != GPS_AWAKE && (sleepTime != UINT32_MAX) &&
|
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
|
// We now want to be awake - so wake up the GPS
|
||||||
setAwake(true);
|
setAwake(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// While we are awake
|
// While we are awake
|
||||||
if (powerState == GPS_AWAKE) {
|
if (powerState == GPS_ACTIVE) {
|
||||||
// LOG_DEBUG("looking for location\n");
|
// LOG_DEBUG("looking for location\n");
|
||||||
// If we've already set time from the GPS, no need to ask the GPS
|
// If we've already set time from the GPS, no need to ask the GPS
|
||||||
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
||||||
@ -1086,7 +1115,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
|
// 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.
|
// 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
|
// clear the GPS rx buffer as quickly as possible
|
||||||
@ -1617,9 +1646,9 @@ bool GPS::whileIdle()
|
|||||||
{
|
{
|
||||||
unsigned int charsInBuf = 0;
|
unsigned int charsInBuf = 0;
|
||||||
bool isValid = false;
|
bool isValid = false;
|
||||||
if (powerState != GPS_AWAKE) {
|
if (powerState != GPS_ACTIVE) {
|
||||||
clearBuffer();
|
clearBuffer();
|
||||||
return (powerState == GPS_AWAKE);
|
return (powerState == GPS_ACTIVE);
|
||||||
}
|
}
|
||||||
#ifdef SERIAL_BUFFER_SIZE
|
#ifdef SERIAL_BUFFER_SIZE
|
||||||
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
|
if (_serial_gps->available() >= SERIAL_BUFFER_SIZE - 1) {
|
||||||
@ -1650,6 +1679,10 @@ bool GPS::whileIdle()
|
|||||||
}
|
}
|
||||||
void GPS::enable()
|
void GPS::enable()
|
||||||
{
|
{
|
||||||
|
// Clear the old lock-time prediction
|
||||||
|
GPSCycles = 0;
|
||||||
|
predictedLockTime = 0;
|
||||||
|
|
||||||
enabled = true;
|
enabled = true;
|
||||||
setInterval(GPS_THREAD_INTERVAL);
|
setInterval(GPS_THREAD_INTERVAL);
|
||||||
setAwake(true);
|
setAwake(true);
|
||||||
|
@ -39,9 +39,10 @@ typedef enum {
|
|||||||
} GPS_RESPONSE;
|
} GPS_RESPONSE;
|
||||||
|
|
||||||
enum GPSPowerState : uint8_t {
|
enum GPSPowerState : uint8_t {
|
||||||
GPS_OFF = 0,
|
GPS_OFF = 0, // Physically powered off
|
||||||
GPS_AWAKE = 1,
|
GPS_ACTIVE = 1, // Awake and want a position
|
||||||
GPS_STANDBY = 2,
|
GPS_STANDBY = 2, // Physically powered on, but soft-sleeping
|
||||||
|
GPS_IDLE = 3, // Awake, but not wanting another position yet
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate a string representation of DOP
|
// Generate a string representation of DOP
|
||||||
@ -72,7 +73,7 @@ class GPS : private concurrency::OSThread
|
|||||||
uint32_t rx_gpio = 0;
|
uint32_t rx_gpio = 0;
|
||||||
uint32_t tx_gpio = 0;
|
uint32_t tx_gpio = 0;
|
||||||
uint32_t en_gpio = 0;
|
uint32_t en_gpio = 0;
|
||||||
int32_t averageLockTime = 0;
|
int32_t predictedLockTime = 0;
|
||||||
uint32_t GPSCycles = 0;
|
uint32_t GPSCycles = 0;
|
||||||
|
|
||||||
int speedSelect = 0;
|
int speedSelect = 0;
|
||||||
@ -93,7 +94,7 @@ class GPS : private concurrency::OSThread
|
|||||||
bool GPSInitFinished = false; // Init thread finished?
|
bool GPSInitFinished = false; // Init thread finished?
|
||||||
bool GPSInitStarted = 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;
|
uint8_t numSatellites = 0;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user