diff --git a/src/OpenBikeSensorFirmware.cpp b/src/OpenBikeSensorFirmware.cpp index 39b26cd..bc32a01 100644 --- a/src/OpenBikeSensorFirmware.cpp +++ b/src/OpenBikeSensorFirmware.cpp @@ -399,9 +399,10 @@ void setup() { esp_bt_mem_release(ESP_BT_MODE_BTDM)); // no bluetooth at all here. delay(200); - startServer(&cfg); + obsDisplay->showTextOnGrid(2, obsDisplay->newLine(), "Start GPS..."); gps.begin(); gps.setStatisticsIntervalInSeconds(2); // ?? + startServer(&cfg); while (true) { yield(); serverLoop(); @@ -446,7 +447,6 @@ void setup() { gps.handle(); setupBluetooth(cfg, trackUniqueIdentifier); - obsDisplay->showTextOnGrid(2, obsDisplay->newLine(), "Wait for GPS"); obsDisplay->newLine(); gps.handle(); diff --git a/src/configServer.cpp b/src/configServer.cpp index 272fac2..7b1b4fb 100644 --- a/src/configServer.cpp +++ b/src/configServer.cpp @@ -230,6 +230,9 @@ static const char* const development = static const char* const rebootIndex = "

Device reboots now.

"; +static const char* const gpsColdIndex = + "

GPS cold start

"; + // ######################################### // Wifi // ######################################### @@ -456,6 +459,7 @@ static void handleNotFound(HTTPRequest * req, HTTPResponse * res); static void handleIndex(HTTPRequest * req, HTTPResponse * res); static void handleAbout(HTTPRequest * req, HTTPResponse * res); static void handleReboot(HTTPRequest * req, HTTPResponse * res); +static void handleColdStartGPS(HTTPRequest * req, HTTPResponse * res); static void handleBackup(HTTPRequest * req, HTTPResponse * res); static void handleBackupDownload(HTTPRequest * req, HTTPResponse * res); static void handleBackupRestore(HTTPRequest * req, HTTPResponse * res); @@ -546,6 +550,7 @@ void registerPages(HTTPServer * httpServer) { httpServer->registerNode(new ResourceNode("/", HTTP_GET, handleIndex)); httpServer->registerNode(new ResourceNode("/about", HTTP_GET, handleAbout)); httpServer->registerNode(new ResourceNode("/reboot", HTTP_GET, handleReboot)); + httpServer->registerNode(new ResourceNode("/cold", HTTP_GET, handleColdStartGPS)); httpServer->registerNode(new ResourceNode("/settings/backup", HTTP_GET, handleBackup)); httpServer->registerNode(new ResourceNode("/settings/backup.json", HTTP_GET, handleBackupDownload)); httpServer->registerNode(new ResourceNode("/settings/restore", HTTP_POST, handleBackupRestore)); @@ -724,7 +729,8 @@ static void wifiConnectedActions() { if (WiFiClass::status() == WL_CONNECTED) { TimeUtils::setClockByNtpAndWait(WiFi.gatewayIP().toString().c_str()); } - if (SD.begin() && WiFiClass::status() == WL_CONNECTED) { + if (SD.begin() && WiFiClass::status() == WL_CONNECTED + && (!gps.moduleIsAlive() || gps.is_neo6())) { AlpData::update(obsDisplay); } @@ -1065,6 +1071,8 @@ static void handleAbout(HTTPRequest *req, HTTPResponse * res) { page += keyValue("GPS satellites", gps.getValidSatellites()); page += keyValue("GPS uptime", gps.getUptime(), "ms"); page += keyValue("GPS noise level", gps.getLastNoiseLevel()); + page += keyValue("GPS Antenna Gain", gps.getLastAntennaGain()); + page += keyValue("GPS Jamming Level", gps.getLastJamInd()); page += keyValue("GPS baud rate", gps.getBaudRate()); page += keyValue("GPS ALP bytes", gps.getNumberOfAlpBytesSent()); page += keyValue("GPS messages", gps.getMessagesHtml()); @@ -1123,6 +1131,14 @@ static void handleReboot(HTTPRequest *, HTTPResponse * res) { ESP.restart(); } +static void handleColdStartGPS(HTTPRequest *, HTTPResponse * res) { + String html = createPage(gpsColdIndex); + html = replaceDefault(html, "Navigation"); + sendHtml(res, html); + gps.coldStartGps(); + res->finalize(); +} + static void handleBackup(HTTPRequest *, HTTPResponse * res) { String html = createPage(backupIndex, xhrUpload); diff --git a/src/gps.cpp b/src/gps.cpp index b1f4db7..a364059 100644 --- a/src/gps.cpp +++ b/src/gps.cpp @@ -31,14 +31,63 @@ const String Gps::INF_SEVERITY_STRING[] = { String("TST"), String("DBG") }; +bool Gps::is_neo6() const { + if (String(hwString).substring(0,4) == String("0004")) { + return true; + } + return false; +} + +bool Gps::is_neo8() const { + if (String(hwString).substring(0,4) == String("0008")) { + return true; + } + return false; +} + +bool Gps::is_neo10() const { + if (String(hwString).substring(0,4) == String("000A")) { + return true; + } + return false; +} + +String Gps::hw() const { + if (is_neo6()){ + return "Neo6"; + } + if (is_neo8()){ + return "Neo8"; + } + if (is_neo10()) { + return "NeoA"; + } + return "Neo" + String(hwString).substring(3,4); +} + void Gps::begin() { setBaud(); softResetGps(); if (mGpsNeedsConfigUpdate) { configureGpsModule(); } - enableAlpIfDataIsAvailable(); pollStatistics(); + if((!is_neo6()) || (!SD.exists(AID_INI_DATA_FILE_NAME))) { + // we're on a non-6 neo and avoid AID_INI because is deprecated + // or we're on a neo6 but last boot we didn't get far enough to receive fresh + // ALP_INI data after initializing + // so restart GPS for good measure. + if (is_neo6()) log_i("We found no AID_INI on with neo6 on boot - coldstart gps in case its in a state where it doesn't get fixes"); + if (!is_neo6()) log_i("Coldstart because we found that newer neos profit from that."); + + coldStartGps(); + } + pollStatistics(); + + if (is_neo6()) { + enableAlpIfDataIsAvailable(); + } + if (mLastTimeTimeSet == 0) { #ifdef UBX_M10 setMessageInterval(UBX_CFG_KEY_ID::CFG_MSGOUT_UBX_NAV_TIMEGPS_UART1, 1); @@ -293,7 +342,6 @@ void Gps::softResetGps() { log_i("Soft-RESET GPS!"); handle(); const uint8_t UBX_CFG_RST[] = {0x00, 0x00, 0x02, 0x00}; // WARM START -// const uint8_t UBX_CFG_RST[] = {0xFF, 0xFF, 0x02, 0x00}; // Cold START // we had the case where the reset took several seconds // see https://github.com/openbikesensor/OpenBikeSensorFirmware/issues/309 // Newer firmware (like M10 and likely also M8) will not ack this @@ -304,6 +352,20 @@ void Gps::softResetGps() { log_i("Soft-RESET GPS! Done"); } +void Gps::coldStartGps() { + log_i("Cold-Start GPS!"); + handle(); + const uint8_t UBX_CFG_RST[] = {0xFF, 0xFF, 0x00, 0x00}; + // we had the case where the reset took several seconds + // see https://github.com/openbikesensor/OpenBikeSensorFirmware/issues/309 + // Newer firmware (like M10 and likely also M8) will not ack this + // message so we do not wait for the ACK + sendUbx(UBX_MSG::CFG_RST, UBX_CFG_RST, 4); + waitForData(3000); + handle(); + log_i("Cold Start GPS! Done"); +} + /* There had been changes for the satellites used for SBAS * in europe since the firmware of our GPS module was built * we configure the module to use the 2 satellites that are @@ -344,11 +406,15 @@ void Gps::enableAlpIfDataIsAvailable() { /* Poll or refresh one time statistics, also spends some time * to collect the results. */ + void Gps::pollStatistics() { - handle(); - sendUbx(UBX_MSG::AID_ALP); handle(); sendUbx(UBX_MSG::MON_VER); + handle(20); + if (is_neo6()){ + // AID_ALP is a neo6-only thing + sendUbx(UBX_MSG::AID_ALP); + } handle(); sendUbx(UBX_MSG::MON_HW); handle(); @@ -777,21 +843,35 @@ int32_t Gps::getMessagesWithFailedCrcCount() const { } void Gps::showWaitStatus(DisplayDevice const * display) const { + static bool clear = false; + if (!is_neo6() && !clear) { + obsDisplay->clear(); + clear = true; + } String satellitesString[2]; if (mValidMessagesReceived == 0) { // could not get any valid char from GPS module satellitesString[0] = "OFF?"; } else if (mLastTimeTimeSet == 0) { - satellitesString[0] = String(mCurrentGpsRecord.mSatellitesUsed) + "sats SN:" + String(mLastNoiseLevel); + satellitesString[0] = "aGain:" + String(mLastGain); + satellitesString[1] = String(mCurrentGpsRecord.mSatellitesUsed) + "sats SN:" + String(mLastNoiseLevel); } else { - satellitesString[0] = "GPS " + TimeUtils::timeToString(); + satellitesString[0] = String(hw()).substring(1) + TimeUtils::timeToString(); satellitesString[1] = String(mCurrentGpsRecord.mSatellitesUsed) + "sats SN:" + String(mLastNoiseLevel); } + obsDisplay->showTextOnGrid(2, display->currentLine() - 1, satellitesString[0]); + obsDisplay->showTextOnGrid(2, display->currentLine(), satellitesString[1]); + if (!is_neo6()){ + obsDisplay->showTextOnGrid(0, 1, String(hw())+" GPS"); + obsDisplay->showTextOnGrid(2, 1, "HDOP: " + getHdopAsString() + "D"); - if (satellitesString[1].isEmpty()) { - obsDisplay->showTextOnGrid(2, display->currentLine(), satellitesString[0]); - } else { - obsDisplay->showTextOnGrid(2, display->currentLine() - 1, satellitesString[0]); - obsDisplay->showTextOnGrid(2, display->currentLine(), satellitesString[1]); + obsDisplay->showTextOnGrid(0, 2, "Jam: " + String(mLastJamInd)); + obsDisplay->showTextOnGrid(2, 2, "Msgs: " + String(mValidMessagesReceived)); + obsDisplay->showTextOnGrid(2, 3, "Fix: " + String(mCurrentGpsRecord.mFixStatus) + "D"); + obsDisplay->showTextOnGrid(0, 3, "lat,lon:"); + + + obsDisplay->showTextOnGrid(0, 4, String(mCurrentGpsRecord.mLatitude)); + obsDisplay->showTextOnGrid(0, 5, String(mCurrentGpsRecord.mLongitude)); } } @@ -1098,12 +1178,40 @@ void Gps::parseUbxMessage() { String(mGpsBuffer.monVer.swVersion).c_str(), String(mGpsBuffer.monVer.hwVersion).c_str(), mGpsBuffer.ubxHeader.length); + for (int i = 0; i < sizeof(hwString) && i < sizeof(mGpsBuffer.monVer.hwVersion) ; i++) { + hwString[i]=mGpsBuffer.monVer.hwVersion[i]; + } } break; case (uint16_t) UBX_MSG::MON_HW: { - log_v("MON-HW Antenna Status %d, noise level %d", mGpsBuffer.monHw.aStatus, - mGpsBuffer.monHw.noisePerMs); - mLastNoiseLevel = mGpsBuffer.monHw.noisePerMs; + const char* aStatus; + if (is_neo6()) { + switch (mGpsBuffer.monHw.aStatus) { + case mGpsBuffer.monHw.INIT: aStatus = "init"; break; + case mGpsBuffer.monHw.DONTKNOW: aStatus = "?"; break; + case mGpsBuffer.monHw.OK: aStatus = "ok"; break; + case mGpsBuffer.monHw.SHORT: aStatus = "short"; break; + case mGpsBuffer.monHw.OPEN: aStatus = "open"; break; + default: aStatus = "invalid"; + } + log_d("MON-HW Antenna Status %d %s, Antenna Power %d, Gain (0-8191) %d, noise level %d", mGpsBuffer.monHw.aStatus, aStatus, mGpsBuffer.monHw.aPower, mGpsBuffer.monHw.agcCnt, mGpsBuffer.monHw.noisePerMs); + mLastNoiseLevel = mGpsBuffer.monHw.noisePerMs; + mLastGain = mGpsBuffer.monHw.agcCnt; + mLastJamInd = mGpsBuffer.monHw.jamInd; + } else { + switch (mGpsBuffer.monHwNew.aStatus) { + case mGpsBuffer.monHwNew.INIT: aStatus = "init"; break; + case mGpsBuffer.monHwNew.DONTKNOW: aStatus = "?"; break; + case mGpsBuffer.monHwNew.OK: aStatus = "ok"; break; + case mGpsBuffer.monHwNew.SHORT: aStatus = "short"; break; + case mGpsBuffer.monHwNew.OPEN: aStatus = "open"; break; + default: aStatus = "invalid"; + } + log_d("MON-HW Antenna Status %d %s, Antenna Power %d, Gain (0-8191) %d, noise level %d", mGpsBuffer.monHwNew.aStatus, aStatus, mGpsBuffer.monHwNew.aPower, mGpsBuffer.monHwNew.agcCnt, mGpsBuffer.monHwNew.noisePerMs); + mLastNoiseLevel = mGpsBuffer.monHwNew.noisePerMs; + mLastGain = mGpsBuffer.monHwNew.agcCnt; + mLastJamInd = mGpsBuffer.monHwNew.jamInd; + } } break; case (uint16_t) UBX_MSG::NAV_STATUS: { @@ -1113,7 +1221,7 @@ void Gps::parseUbxMessage() { mGpsUptime = mGpsBuffer.navStatus.msss; if (mGpsBuffer.navStatus.ttff != 0) { addStatisticsMessage("TimeToFix: " + String(mGpsBuffer.navStatus.ttff) + "ms"); - } else if (!mAidIniSent) { + } else if (!mAidIniSent and is_neo6()) { mAidIniSent = true; aidIni(); } @@ -1130,7 +1238,7 @@ void Gps::parseUbxMessage() { } break; case (uint16_t) UBX_MSG::NAV_SOL: { - log_v("SOL: iTOW: %u, gpsFix: %d, flags: %02x, numSV: %d, pDop: %04d.", + log_d("SOL: iTOW: %u, gpsFix: %d, flags: %02x, numSV: %d, pDop: %04d.", mGpsBuffer.navSol.iTow, mGpsBuffer.navSol.gpsFix, mGpsBuffer.navSol.flags, mGpsBuffer.navSol.numSv, mGpsBuffer.navSol.pDop); if (mGpsBuffer.navSol.flags & 4) { // WKNSET @@ -1148,7 +1256,7 @@ void Gps::parseUbxMessage() { } break; case (uint16_t) UBX_MSG::NAV_PVT: { - log_v("PVT: iTOW: %u, fixType: %d, flags: %02x, numSV: %d, pDop: %04d.", + log_d("PVT: iTOW: %u, fixType: %d, flags: %02x, numSV: %d, pDop: %04d.", mGpsBuffer.navPvt.iTow, mGpsBuffer.navPvt.fixType, mGpsBuffer.navPvt.flags, mGpsBuffer.navPvt.numSV, mGpsBuffer.navPvt.pDOP); prepareGpsData(mGpsBuffer.navPvt.iTow, mMessageStarted); @@ -1156,7 +1264,7 @@ void Gps::parseUbxMessage() { } break; case (uint16_t) UBX_MSG::NAV_VELNED: { - log_v("VELNED: iTOW: %u, speed: %d cm/s, gSpeed: %d cm/s, heading: %d," + log_d("VELNED: iTOW: %u, speed: %d cm/s, gSpeed: %d cm/s, heading: %d," " speedAcc: %d, cAcc: %d", mGpsBuffer.navVelned.iTow, mGpsBuffer.navVelned.speed, mGpsBuffer.navVelned.gSpeed, mGpsBuffer.navVelned.heading, mGpsBuffer.navVelned.sAcc, mGpsBuffer.navVelned.cAcc); @@ -1289,7 +1397,7 @@ void Gps::parseUbxMessage() { log_d("CFG_GNSS"); break; default: - log_e("Got UBX_MESSAGE! Id: 0x%04x Len %d iTOW %d", mGpsBuffer.ubxHeader.ubxMsgId, + log_e("Got unparsed UBX_MESSAGE! Id: 0x%04x Len %d iTOW %d", mGpsBuffer.ubxHeader.ubxMsgId, mGpsBuffer.ubxHeader.length, mGpsBuffer.navStatus.iTow); } } @@ -1314,10 +1422,10 @@ void Gps::handleUbxNavTimeGps(const GpsBuffer::UbxNavTimeGps &message, const uin mIncomingGpsRecord.setWeek(mLastGpsWeek); } if ((message.valid & 0x03) == 0x03 // WEEK && TOW - && delayMs < 250 + && delayMs < 1000 && message.tAcc < (20 * 1000 * 1000 /* 20ms */) - && (mLastTimeTimeSet == 0 - || (mLastTimeTimeSet + (2 * 60 * 1000 /* 2 minutes */)) < receivedMs)) { + && ((mLastTimeTimeSet == 0) + || ((mLastTimeTimeSet + (2 * 60 * 1000 /* 2 minutes */)) < receivedMs))) { String oldTime = TimeUtils::dateTimeToString(); TimeUtils::setClockByGps(message.iTow, message.fTow, message.week); String newTime = TimeUtils::dateTimeToString(); @@ -1328,10 +1436,12 @@ void Gps::handleUbxNavTimeGps(const GpsBuffer::UbxNavTimeGps &message, const uin + "ms. tAcc:" + String(message.tAcc) + "ns"); } if (mLastTimeTimeSet == 0) { - mLastTimeTimeSet = receivedMs; - // This triggers another NAV-TIMEGPS message! + if (delayMs < 100) { // keep mLastTimeTimeSet at 0 unless reasonable delayMs + mLastTimeTimeSet = receivedMs; + } + // This triggers another NAV-TIMEGPS message! more often until good time is received #ifdef UBX_M6 - setMessageInterval(UBX_MSG::NAV_TIMEGPS, 240, false); // every 4 minutes + setMessageInterval(UBX_MSG::NAV_TIMEGPS, (delayMs>100) ? 5 : 240, false); // every 4 minutes #endif #ifdef UBX_M10 setMessageInterval(UBX_CFG_KEY_ID::CFG_MSGOUT_UBX_NAV_TIMEGPS_UART1, 240, false); // every 4 minutes @@ -1411,6 +1521,14 @@ uint16_t Gps::getLastNoiseLevel() const { return mLastNoiseLevel; } +uint16_t Gps::getLastAntennaGain() const { + return mLastGain; +} + +uint8_t Gps::getLastJamInd() const { + return mLastJamInd; +} + uint32_t Gps::getBaudRate() { return mSerial.baudRate(); } diff --git a/src/gps.h b/src/gps.h index e5fccab..a5cd341 100644 --- a/src/gps.h +++ b/src/gps.h @@ -70,6 +70,10 @@ class Gps { uint16_t getLastNoiseLevel() const; + uint16_t getLastAntennaGain() const; + + uint8_t getLastJamInd() const; + /* Collected informational messages as String. */ String getMessages() const; @@ -107,6 +111,12 @@ class Gps { uint32_t getNumberOfAlpBytesSent() const; uint32_t getUnexpectedCharReceivedCount() const; + void coldStartGps(); + + bool is_neo6() const; + bool is_neo8() const; + bool is_neo10() const; + private: /* ALP msgs up to 0x16A seen might be more? */ static const int MAX_MESSAGE_LENGTH = 128 * 3; @@ -288,6 +298,37 @@ class Gps { uint32_t pinDir; uint32_t pinVal; uint16_t noisePerMs; + uint16_t agcCnt; // AGC (Automatic Gain Control) Monitor, as percentage of maximum gain,range 0 to 8191 (100%) + enum ANT_STATUS : uint8_t { + INIT = 0, + DONTKNOW = 1, + OK = 2, + SHORT = 3, + OPEN = 4, + } aStatus; + enum ANT_POWER : uint8_t { + OFF = 0, + ON = 1, + POWER_DONTKNOW = 2, + } aPower; + uint8_t flags; + uint8_t reserved1; + uint32_t usedMask; + uint8_t vp[17]; //M6 25bytes, M8 only 17bytes? + uint8_t jamInd; //cwSuppression / CW interference suppression level, scaled (0 = no CW jamming, 255 = strong CW jamming) + uint16_t reserved3; + uint32_t pinIrq; + uint32_t pullH; + uint32_t pullL; + } monHwNew; + struct __attribute__((__packed__)) { + UBX_HEADER ubxHeader; + uint32_t pinSel; + uint32_t pinBank; + uint32_t pinDir; + uint32_t pinVal; + uint16_t noisePerMs; + uint16_t agcCnt; // AGC (Automatic Gain Control) Monitor, as percentage of maximum gain,range 0 to 8191 (100%) enum ANT_STATUS : uint8_t { INIT = 0, DONTKNOW = 1, @@ -550,6 +591,8 @@ class Gps { uint32_t mUnexpectedCharReceivedCount = 0; uint8_t mNmeaChk; uint16_t mLastNoiseLevel; + uint16_t mLastGain; + uint8_t mLastJamInd; AlpData mAlpData; bool mAidIniSent = false; /* record that was last received */ @@ -567,12 +610,16 @@ class Gps { bool mGpsNeedsConfigUpdate = false; static const String INF_SEVERITY_STRING[]; + char hwString[10]; + void configureGpsModule(); bool encode(uint8_t data); bool setBaud(); + String hw() const; + bool checkCommunication(); void addStatisticsMessage(String message); diff --git a/src/gpsrecord.h b/src/gpsrecord.h index 9a89af3..60c6dc8 100644 --- a/src/gpsrecord.h +++ b/src/gpsrecord.h @@ -102,7 +102,6 @@ class GpsRecord { uint32_t mCreatedAtMillisTicks; static const int32_t pow10[10]; static String toScaledString(int32_t value, uint16_t scale); - }; diff --git a/src/utils/alpdata.cpp b/src/utils/alpdata.cpp index baff084..fe9b549 100644 --- a/src/utils/alpdata.cpp +++ b/src/utils/alpdata.cpp @@ -172,6 +172,7 @@ size_t AlpData::loadMessage(uint8_t *data, size_t size) { result = f.read(data, size); f.close(); log_d("Read %d bytes", result); + SD.remove(AID_INI_DATA_FILE_NAME); } return result; }