|
|
|
|
@@ -506,10 +506,9 @@ bool GPS::setup()
|
|
|
|
|
delay(1000);
|
|
|
|
|
#endif
|
|
|
|
|
if (probeTries < GPS_PROBETRIES) {
|
|
|
|
|
LOG_DEBUG("Probe for GPS at %d", serialSpeeds[speedSelect]);
|
|
|
|
|
gnssModel = probe(serialSpeeds[speedSelect]);
|
|
|
|
|
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
|
|
|
|
if (++speedSelect == array_count(serialSpeeds)) {
|
|
|
|
|
if (currentStep == 0 && ++speedSelect == array_count(serialSpeeds)) {
|
|
|
|
|
speedSelect = 0;
|
|
|
|
|
++probeTries;
|
|
|
|
|
}
|
|
|
|
|
@@ -518,10 +517,9 @@ bool GPS::setup()
|
|
|
|
|
// Rare Serial Speeds
|
|
|
|
|
#ifndef CONFIG_IDF_TARGET_ESP32C6
|
|
|
|
|
if (probeTries == GPS_PROBETRIES) {
|
|
|
|
|
LOG_DEBUG("Probe for GPS at %d", rareSerialSpeeds[speedSelect]);
|
|
|
|
|
gnssModel = probe(rareSerialSpeeds[speedSelect]);
|
|
|
|
|
if (gnssModel == GNSS_MODEL_UNKNOWN) {
|
|
|
|
|
if (++speedSelect == array_count(rareSerialSpeeds)) {
|
|
|
|
|
if (currentStep == 0 && ++speedSelect == array_count(rareSerialSpeeds)) {
|
|
|
|
|
LOG_WARN("Give up on GPS probe and set to %d", GPS_BAUDRATE);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@@ -1033,7 +1031,7 @@ void GPS::down()
|
|
|
|
|
LOG_DEBUG("%us until next search", sleepTime / 1000);
|
|
|
|
|
|
|
|
|
|
// If update interval less than 10 seconds, no attempt to sleep
|
|
|
|
|
if (updateInterval <= 10 * 1000UL || sleepTime == 0)
|
|
|
|
|
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS || sleepTime == 0)
|
|
|
|
|
setPowerState(GPS_IDLE);
|
|
|
|
|
|
|
|
|
|
else {
|
|
|
|
|
@@ -1094,7 +1092,7 @@ int32_t GPS::runOnce()
|
|
|
|
|
return disable();
|
|
|
|
|
}
|
|
|
|
|
if (!setup())
|
|
|
|
|
return 2000; // Setup failed, re-run in two seconds
|
|
|
|
|
return currentDelay; // Setup failed, re-run in two seconds
|
|
|
|
|
|
|
|
|
|
// We have now loaded our saved preferences from flash
|
|
|
|
|
if (config.position.gps_mode != meshtastic_Config_PositionConfig_GpsMode_ENABLED) {
|
|
|
|
|
@@ -1104,6 +1102,29 @@ int32_t GPS::runOnce()
|
|
|
|
|
publishUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ======================== GPS_ACTIVE state ========================
|
|
|
|
|
// In GPS_ACTIVE state, GPS is powered on and we're receiving NMEA messages.
|
|
|
|
|
// We use the following logic to determine when to update the local position
|
|
|
|
|
// or time by running GPS::publishUpdate.
|
|
|
|
|
// Note: Local position update is asynchronous to position broadcast. We
|
|
|
|
|
// generally run this state every gps_update_interval seconds, and in most cases
|
|
|
|
|
// gps_update_interval is faster than the position broadcast interval so there's a
|
|
|
|
|
// fresh position ready when the device wants to broadcast one on the mesh.
|
|
|
|
|
//
|
|
|
|
|
// 1. Got a time for the first time --> set the time, don't publish.
|
|
|
|
|
// 2. Got a lock for the first time
|
|
|
|
|
// --> If gps_update_interval is <= 10s --> publishUpdate
|
|
|
|
|
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
|
|
|
|
// 3. Got a lock after turning back on
|
|
|
|
|
// --> If gps_update_interval is <= 10s --> publishUpdate
|
|
|
|
|
// --> Otherwise, hold for MIN(gps_update_interval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS, 20s)
|
|
|
|
|
// 4. Hold has expired
|
|
|
|
|
// --> If we have a time and a location --> publishUpdate
|
|
|
|
|
// --> down()
|
|
|
|
|
// 5. Search time has expired
|
|
|
|
|
// --> If we have a time and a location --> publishUpdate
|
|
|
|
|
// --> If we had a location before but don't now --> publishUpdate
|
|
|
|
|
// --> down()
|
|
|
|
|
if (whileActive()) {
|
|
|
|
|
// if we have received valid NMEA claim we are connected
|
|
|
|
|
setConnected();
|
|
|
|
|
@@ -1113,55 +1134,81 @@ int32_t GPS::runOnce()
|
|
|
|
|
if (!config.position.fixed_position && powerState != GPS_ACTIVE && scheduling.isUpdateDue())
|
|
|
|
|
up();
|
|
|
|
|
|
|
|
|
|
// If we've already set time from the GPS, no need to ask the GPS
|
|
|
|
|
// quality of the previous fix. We set it to 0 when we go down, so it's a way
|
|
|
|
|
// to check if we're getting a lock after being GPS_OFF.
|
|
|
|
|
uint8_t prev_fixQual = fixQual;
|
|
|
|
|
|
|
|
|
|
if (powerState == GPS_ACTIVE) {
|
|
|
|
|
// if gps_update_interval is <=10s, GPS never goes off, so we treat that differently
|
|
|
|
|
uint32_t updateInterval = Default::getConfiguredOrDefaultMs(config.position.gps_update_interval);
|
|
|
|
|
|
|
|
|
|
// 1. Got a time for the first time
|
|
|
|
|
bool gotTime = (getRTCQuality() >= RTCQualityGPS);
|
|
|
|
|
if (!gotTime && lookForTime()) { // Note: we count on this && short-circuiting and not resetting the RTC time
|
|
|
|
|
gotTime = true;
|
|
|
|
|
shouldPublish = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uint8_t prev_fixQual = fixQual;
|
|
|
|
|
// 2. Got a lock for the first time, or 3. Got a lock after turning back on
|
|
|
|
|
bool gotLoc = lookForLocation();
|
|
|
|
|
if (gotLoc && !hasValidLocation) { // declare that we have location ASAP
|
|
|
|
|
if (gotLoc) {
|
|
|
|
|
#ifdef GPS_DEBUG
|
|
|
|
|
if (!hasValidLocation) { // declare that we have location ASAP
|
|
|
|
|
LOG_DEBUG("hasValidLocation RISING EDGE");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (updateInterval <= GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS) {
|
|
|
|
|
hasValidLocation = true;
|
|
|
|
|
shouldPublish = true;
|
|
|
|
|
// Hold for 20secs after getting a lock to download ephemeris etc
|
|
|
|
|
fixHoldEnds = millis() + 20000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gotLoc && prev_fixQual == 0) { // just got a lock after turning back on.
|
|
|
|
|
fixHoldEnds = millis() + 20000;
|
|
|
|
|
shouldPublish = true; // Publish immediately, since next publish is at end of hold
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool tooLong = scheduling.searchedTooLong();
|
|
|
|
|
if (tooLong)
|
|
|
|
|
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
|
|
|
|
|
|
|
|
|
// Once we get a location we no longer desperately want an update
|
|
|
|
|
if ((gotLoc && gotTime) || tooLong) {
|
|
|
|
|
if (tooLong && !gotLoc) {
|
|
|
|
|
// we didn't get a location during this ack window, therefore declare loss of lock
|
|
|
|
|
if (hasValidLocation) {
|
|
|
|
|
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
|
|
|
|
}
|
|
|
|
|
p = meshtastic_Position_init_default;
|
|
|
|
|
hasValidLocation = false;
|
|
|
|
|
}
|
|
|
|
|
if (millis() > fixHoldEnds) {
|
|
|
|
|
shouldPublish = true; // publish our update at the end of the lock hold
|
|
|
|
|
publishUpdate();
|
|
|
|
|
down();
|
|
|
|
|
} else if (!hasValidLocation || prev_fixQual == 0 || (fixHoldEnds + GPS_THREAD_INTERVAL) < millis()) {
|
|
|
|
|
hasValidLocation = true;
|
|
|
|
|
// Hold for up to 20secs after getting a lock to download ephemeris etc
|
|
|
|
|
uint32_t holdTime = updateInterval - GPS_UPDATE_ALWAYS_ON_THRESHOLD_MS;
|
|
|
|
|
if (holdTime > GPS_FIX_HOLD_MAX_MS)
|
|
|
|
|
holdTime = GPS_FIX_HOLD_MAX_MS;
|
|
|
|
|
fixHoldEnds = millis() + holdTime;
|
|
|
|
|
#ifdef GPS_DEBUG
|
|
|
|
|
} else {
|
|
|
|
|
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
|
|
|
|
LOG_DEBUG("Holding for %ums after lock", holdTime);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If state has changed do a publish
|
|
|
|
|
bool tooLong = scheduling.searchedTooLong();
|
|
|
|
|
if (tooLong && !gotLoc) {
|
|
|
|
|
LOG_WARN("Couldn't publish a valid location: didn't get a GPS lock in time");
|
|
|
|
|
// we didn't get a location during this ack window, therefore declare loss of lock
|
|
|
|
|
if (hasValidLocation) {
|
|
|
|
|
p = meshtastic_Position_init_default;
|
|
|
|
|
hasValidLocation = false;
|
|
|
|
|
shouldPublish = true;
|
|
|
|
|
#ifdef GPS_DEBUG
|
|
|
|
|
LOG_DEBUG("hasValidLocation FALLING EDGE");
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Hold has expired , Search time has expired, we got a time only, or we never needed to hold.
|
|
|
|
|
bool holdExpired = (fixHoldEnds != 0 && millis() > fixHoldEnds);
|
|
|
|
|
if (shouldPublish || tooLong || holdExpired) {
|
|
|
|
|
if (gotTime && hasValidLocation) {
|
|
|
|
|
shouldPublish = true;
|
|
|
|
|
}
|
|
|
|
|
if (shouldPublish) {
|
|
|
|
|
fixHoldEnds = 0;
|
|
|
|
|
publishUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There's a chance we just got a time, so keep going to see if we can get a location too
|
|
|
|
|
if (tooLong || holdExpired) {
|
|
|
|
|
down();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef GPS_DEBUG
|
|
|
|
|
} else if (fixHoldEnds != 0) {
|
|
|
|
|
LOG_DEBUG("Holding for GPS data download: %d ms (numSats=%d)", fixHoldEnds - millis(), p.sats_in_view);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// ===================== end GPS_ACTIVE state ========================
|
|
|
|
|
|
|
|
|
|
if (config.position.fixed_position == true && hasValidLocation)
|
|
|
|
|
return disable(); // This should trigger when we have a fixed position, and get that first position
|
|
|
|
|
@@ -1218,6 +1265,10 @@ static const char *DETECTED_MESSAGE = "%s detected";
|
|
|
|
|
|
|
|
|
|
GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
{
|
|
|
|
|
uint8_t buffer[768] = {0};
|
|
|
|
|
|
|
|
|
|
switch (currentStep) {
|
|
|
|
|
case 0: {
|
|
|
|
|
#if defined(ARCH_NRF52) || defined(ARCH_PORTDUINO) || defined(ARCH_STM32WL)
|
|
|
|
|
_serial_gps->end();
|
|
|
|
|
_serial_gps->begin(serialSpeed);
|
|
|
|
|
@@ -1227,13 +1278,12 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
_serial_gps->begin(serialSpeed);
|
|
|
|
|
#else
|
|
|
|
|
if (_serial_gps->baudRate() != serialSpeed) {
|
|
|
|
|
LOG_DEBUG("Set Baud to %i", serialSpeed);
|
|
|
|
|
LOG_DEBUG("Set GPS Baud to %i", serialSpeed);
|
|
|
|
|
_serial_gps->updateBaudRate(serialSpeed);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
memset(&ublox_info, 0, sizeof(ublox_info));
|
|
|
|
|
uint8_t buffer[768] = {0};
|
|
|
|
|
delay(100);
|
|
|
|
|
|
|
|
|
|
// Close all NMEA sentences, valid for L76K, ATGM336H (and likely other AT6558 devices)
|
|
|
|
|
@@ -1248,19 +1298,31 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
_serial_gps->write("$CFGMSG,0,1,0,1*1B\r\n");
|
|
|
|
|
_serial_gps->write("$CFGMSG,0,2,0,1*18\r\n");
|
|
|
|
|
_serial_gps->write("$CFGMSG,0,3,0,1*19\r\n");
|
|
|
|
|
delay(20);
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 1;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 1: {
|
|
|
|
|
|
|
|
|
|
// Unicore UFirebirdII Series: UC6580, UM620, UM621, UM670A, UM680A, or UM681A,or CM121
|
|
|
|
|
std::vector<ChipInfo> unicore = {
|
|
|
|
|
{"UC6580", "UC6580", GNSS_MODEL_UC6580}, {"UM600", "UM600", GNSS_MODEL_UC6580}, {"CM121", "CM121", GNSS_MODEL_CM121}};
|
|
|
|
|
PROBE_FAMILY("Unicore Family", "$PDTINFO", unicore, 500);
|
|
|
|
|
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 2;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 2: {
|
|
|
|
|
std::vector<ChipInfo> atgm = {
|
|
|
|
|
{"ATGM336H", "$GPTXT,01,01,02,HW=ATGM336H", GNSS_MODEL_ATGM336H},
|
|
|
|
|
/* ATGM332D series (-11(GPS), -21(BDS), -31(GPS+BDS), -51(GPS+GLONASS), -71-0(GPS+BDS+GLONASS)) based on AT6558 */
|
|
|
|
|
{"ATGM332D", "$GPTXT,01,01,02,HW=ATGM332D", GNSS_MODEL_ATGM336H}};
|
|
|
|
|
PROBE_FAMILY("ATGM33xx Family", "$PCAS06,1*1A", atgm, 500);
|
|
|
|
|
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 3;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 3: {
|
|
|
|
|
/* Airoha (Mediatek) AG3335A/M/S, A3352Q, Quectel L89 2.0, SimCom SIM65M */
|
|
|
|
|
_serial_gps->write("$PAIR062,2,0*3C\r\n"); // GSA OFF to reduce volume
|
|
|
|
|
_serial_gps->write("$PAIR062,3,0*3D\r\n"); // GSV OFF to reduce volume
|
|
|
|
|
@@ -1269,9 +1331,18 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
{"AG3352", "$PAIR021,AG3352", GNSS_MODEL_AG3352},
|
|
|
|
|
{"RYS3520", "$PAIR021,REYAX_RYS3520_V2", GNSS_MODEL_AG3352}};
|
|
|
|
|
PROBE_FAMILY("Airoha Family", "$PAIR021*39", airoha, 1000);
|
|
|
|
|
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 4;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 4: {
|
|
|
|
|
PROBE_SIMPLE("LC86", "$PQTMVERNO*58", "$PQTMVERNO,LC86", GNSS_MODEL_AG3352, 500);
|
|
|
|
|
PROBE_SIMPLE("L76K", "$PCAS06,0*1B", "$GPTXT,01,01,02,SW=", GNSS_MODEL_MTK, 500);
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 5;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 5: {
|
|
|
|
|
|
|
|
|
|
// Close all NMEA sentences, valid for MTK3333 and MTK3339 platforms
|
|
|
|
|
_serial_gps->write("$PMTK514,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0*2E\r\n");
|
|
|
|
|
@@ -1282,7 +1353,11 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
{"L80", "_3339_", GNSS_MODEL_MTK_L76B}};
|
|
|
|
|
|
|
|
|
|
PROBE_FAMILY("MTK Family", "$PMTK605*31", mtk, 500);
|
|
|
|
|
|
|
|
|
|
currentDelay = 20;
|
|
|
|
|
currentStep = 6;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
case 6: {
|
|
|
|
|
uint8_t cfg_rate[] = {0xB5, 0x62, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00};
|
|
|
|
|
UBXChecksum(cfg_rate, sizeof(cfg_rate));
|
|
|
|
|
clearBuffer();
|
|
|
|
|
@@ -1291,6 +1366,8 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
GPS_RESPONSE response = getACK(0x06, 0x08, 750);
|
|
|
|
|
if (response == GNSS_RESPONSE_NONE) {
|
|
|
|
|
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
|
|
|
|
currentDelay = 2000;
|
|
|
|
|
currentStep = 0;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
} else if (response == GNSS_RESPONSE_FRAME_ERRORS) {
|
|
|
|
|
LOG_INFO("UBlox Frame Errors (baudrate %d)", serialSpeed);
|
|
|
|
|
@@ -1374,7 +1451,11 @@ GnssModel_t GPS::probe(int serialSpeed)
|
|
|
|
|
return GNSS_MODEL_UBLOX10;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
LOG_WARN("No GNSS Module (baudrate %d)", serialSpeed);
|
|
|
|
|
currentDelay = 2000;
|
|
|
|
|
currentStep = 0;
|
|
|
|
|
return GNSS_MODEL_UNKNOWN;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|