From 91afc47c8aeecf21be71470f302d2202872af27d Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Fri, 27 Sep 2024 08:01:27 -1000 Subject: [PATCH 1/5] Create the GNSS virtual class --- Firmware/RTK_Everywhere/Begin.ino | 4 +- Firmware/RTK_Everywhere/Bluetooth.ino | 4 +- Firmware/RTK_Everywhere/Buttons.ino | 2 +- Firmware/RTK_Everywhere/Display.ino | 22 +- Firmware/RTK_Everywhere/ESPNOW.ino | 2 +- Firmware/RTK_Everywhere/Form.ino | 8 +- Firmware/RTK_Everywhere/GNSS.h | 367 ++++ Firmware/RTK_Everywhere/GNSS.ino | 1795 +---------------- Firmware/RTK_Everywhere/LoRa.ino | 6 +- Firmware/RTK_Everywhere/MQTT_Client.ino | 18 +- Firmware/RTK_Everywhere/NTP.ino | 73 +- Firmware/RTK_Everywhere/NtripClient.ino | 8 +- .../RTK_Everywhere/PointPerfectLibrary.ino | 12 +- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 33 +- Firmware/RTK_Everywhere/States.ino | 46 +- Firmware/RTK_Everywhere/System.ino | 44 +- Firmware/RTK_Everywhere/Tasks.ino | 6 +- Firmware/RTK_Everywhere/UM980.ino | 6 +- Firmware/RTK_Everywhere/ZED.ino | 15 +- Firmware/RTK_Everywhere/menuBase.ino | 2 +- Firmware/RTK_Everywhere/menuCommands.ino | 16 +- Firmware/RTK_Everywhere/menuFirmware.ino | 2 +- Firmware/RTK_Everywhere/menuGNSS.ino | 24 +- Firmware/RTK_Everywhere/menuMain.ino | 14 +- Firmware/RTK_Everywhere/menuMessages.ino | 26 +- Firmware/RTK_Everywhere/menuPP.ino | 38 +- Firmware/RTK_Everywhere/menuPorts.ino | 20 +- Firmware/RTK_Everywhere/menuSystem.ino | 30 +- Firmware/RTK_Everywhere/mosaic.ino | 10 +- Firmware/RTK_Everywhere/settings.h | 1 + Firmware/RTK_Everywhere/support.ino | 2 +- 31 files changed, 606 insertions(+), 2050 deletions(-) create mode 100644 Firmware/RTK_Everywhere/GNSS.h diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 579d6177e..35388729b 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -1474,9 +1474,9 @@ void tpISR() { if (millisNow < (timTpArrivalMillis + 999)) // Only sync if the GNSS time is not stale { - if (gnssIsFullyResolved()) // Only sync if GNSS time is fully resolved + if (gnss->isFullyResolved()) // Only sync if GNSS time is fully resolved { - if (gnssGetTimeAccuracy() < 5000) // Only sync if the tAcc is better than 5000ns + if (gnss->getTimeAccuracy() < 5000) // Only sync if the tAcc is better than 5000ns { // To perform the time zone adjustment correctly, it's easiest if we convert the GNSS // time and date into Unix epoch first and then apply the timeZone offset diff --git a/Firmware/RTK_Everywhere/Bluetooth.ino b/Firmware/RTK_Everywhere/Bluetooth.ino index 59db02a16..f7688f47a 100644 --- a/Firmware/RTK_Everywhere/Bluetooth.ino +++ b/Firmware/RTK_Everywhere/Bluetooth.ino @@ -483,7 +483,7 @@ void bluetoothTest(bool runTest) { tasksStopGnssUart(); // Stop absorbing serial via task from GNSS receiver - gnssSetBaudrate(115200 * 2); + gnss->setBaudrate(115200 * 2); serialGNSS->begin(115200 * 2, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Start UART on platform depedent pins for SPP. The GNSS will be @@ -498,7 +498,7 @@ void bluetoothTest(bool runTest) else bluetoothStatusText = "Offline"; - gnssSetBaudrate(settings.dataPortBaud); + gnss->setBaudrate(settings.dataPortBaud); serialGNSS->begin(settings.dataPortBaud, SERIAL_8N1, pin_GnssUart_RX, pin_GnssUart_TX); // Start UART on platform depedent pins for SPP. The GNSS will be diff --git a/Firmware/RTK_Everywhere/Buttons.ino b/Firmware/RTK_Everywhere/Buttons.ino index 577ad286d..84cfd2f1a 100644 --- a/Firmware/RTK_Everywhere/Buttons.ino +++ b/Firmware/RTK_Everywhere/Buttons.ino @@ -29,7 +29,7 @@ void powerDown(bool displayInfo) // Disable SD card use endSD(false, false); - gnssStandby(); // Put the GNSS into standby - if possible + gnss->standby(); // Put the GNSS into standby - if possible // Prevent other tasks from logging, even if access to the microSD card was denied online.logging = false; diff --git a/Firmware/RTK_Everywhere/Display.ino b/Firmware/RTK_Everywhere/Display.ino index 687f5ab75..2f0bfcdb1 100644 --- a/Firmware/RTK_Everywhere/Display.ino +++ b/Firmware/RTK_Everywhere/Display.ino @@ -1267,7 +1267,7 @@ void paintHorizontalAccuracy(displayCoords textCoords) oled->setCursor(textCoords.x, textCoords.y); // x, y oled->print(":"); - float hpa = gnssGetHorizontalAccuracy(); + float hpa = gnss->getHorizontalAccuracy(); if (online.gnss == false) { @@ -1322,7 +1322,7 @@ void paintClockAccuracy(displayCoords textCoords) oled->setCursor(textCoords.x, textCoords.y); // x, y oled->print(":"); - uint32_t timeAccuracy = gnssGetTimeAccuracy(); + uint32_t timeAccuracy = gnss->getTimeAccuracy(); if (online.gnss == false) { @@ -1530,7 +1530,7 @@ displayCoords paintSIVIcon(std::vector *iconList, const ic icon = &SIVIconProperties; // Determine if there is a fix - if (gnssIsFixed() == false) + if (gnss->isFixed() == false) { // override duty - blink satellite dish icon if we don't have a fix duty = 0b01010101; @@ -1561,10 +1561,10 @@ void paintSIVText(displayCoords textCoords) if (online.gnss) { - if (gnssIsFixed() == false) + if (gnss->isFixed() == false) oled->print("0"); else - oled->print(gnssGetSatellitesInView()); + oled->print(gnss->getSatellitesInView()); paintResets(); } // End gnss online @@ -1646,8 +1646,8 @@ void paintBaseTempSurveyStarted(std::vector *iconList) oled->setCursor(xPos + 29, yPos + 2); // x, y oled->setFont(QW_FONT_8X16); - if (gnssGetSurveyInMeanAccuracy() < 10.0) // Error check - oled->print(gnssGetSurveyInMeanAccuracy(), 2); + if (gnss->getSurveyInMeanAccuracy() < 10.0) // Error check + oled->print(gnss->getSurveyInMeanAccuracy(), 2); else oled->print(">10"); @@ -1680,8 +1680,8 @@ void paintBaseTempSurveyStarted(std::vector *iconList) oled->setCursor((uint8_t)((int)xPos + SIVTextStartXPosOffset[present.display_type]) + 30, yPos + 1); // x, y oled->setFont(QW_FONT_8X16); - if (gnssGetSurveyInObservationTime() < 1000) // Error check - oled->print(gnssGetSurveyInObservationTime()); + if (gnss->getSurveyInObservationTime() < 1000) // Error check + oled->print(gnss->getSurveyInObservationTime()); else oled->print("0"); } @@ -2364,9 +2364,9 @@ void paintSystemTest() oled->print("GNSS:"); if (online.gnss == true) { - gnssUpdate(); // Regularly poll to get latest data + gnss->update(); // Regularly poll to get latest data - int satsInView = gnssGetSatellitesInView(); + int satsInView = gnss->getSatellitesInView(); if (satsInView > 5) { oled->print("OK"); diff --git a/Firmware/RTK_Everywhere/ESPNOW.ino b/Firmware/RTK_Everywhere/ESPNOW.ino index fa5b76a65..267efd7ce 100644 --- a/Firmware/RTK_Everywhere/ESPNOW.ino +++ b/Firmware/RTK_Everywhere/ESPNOW.ino @@ -112,7 +112,7 @@ void espnowOnDataReceived(const esp_now_recv_info *mac, const uint8_t *incomingD if (correctionLastSeen(CORR_ESPNOW)) { // Pass RTCM bytes (presumably) from ESP NOW out ESP32-UART to GNSS - gnssPushRawData((uint8_t *)incomingData, len); + gnss->pushRawData((uint8_t *)incomingData, len); if ((settings.debugEspNow == true || settings.debugCorrections == true) && !inMainMenu) systemPrintf("ESPNOW received %d RTCM bytes, pushed to GNSS, RSSI: %d\r\n", len, espnowRSSI); diff --git a/Firmware/RTK_Everywhere/Form.ino b/Firmware/RTK_Everywhere/Form.ino index c91b4aabd..8dd84974c 100644 --- a/Firmware/RTK_Everywhere/Form.ino +++ b/Firmware/RTK_Everywhere/Form.ino @@ -873,15 +873,15 @@ void createDynamicDataString(char *settingsCSV) settingsCSV[0] = '\0'; // Erase current settings string // Current coordinates come from HPPOSLLH call back - stringRecord(settingsCSV, "geodeticLat", gnssGetLatitude(), haeNumberOfDecimals); - stringRecord(settingsCSV, "geodeticLon", gnssGetLongitude(), haeNumberOfDecimals); - stringRecord(settingsCSV, "geodeticAlt", gnssGetAltitude(), 3); + stringRecord(settingsCSV, "geodeticLat", gnss->getLatitude(), haeNumberOfDecimals); + stringRecord(settingsCSV, "geodeticLon", gnss->getLongitude(), haeNumberOfDecimals); + stringRecord(settingsCSV, "geodeticAlt", gnss->getAltitude(), 3); double ecefX = 0; double ecefY = 0; double ecefZ = 0; - geodeticToEcef(gnssGetLatitude(), gnssGetLongitude(), gnssGetAltitude(), &ecefX, &ecefY, &ecefZ); + geodeticToEcef(gnss->getLatitude(), gnss->getLongitude(), gnss->getAltitude(), &ecefX, &ecefY, &ecefZ); stringRecord(settingsCSV, "ecefX", ecefX, 3); stringRecord(settingsCSV, "ecefY", ecefY, 3); diff --git a/Firmware/RTK_Everywhere/GNSS.h b/Firmware/RTK_Everywhere/GNSS.h new file mode 100644 index 000000000..c1a54c2ca --- /dev/null +++ b/Firmware/RTK_Everywhere/GNSS.h @@ -0,0 +1,367 @@ +/*------------------------------------------------------------------------------ +GNSS.h + + Declarations and definitions for the GNSS layer +------------------------------------------------------------------------------*/ + +#ifndef __GNSS_H__ +#define __GNSS_H__ + +class GNSS +{ + protected: + float _altitude; // Altitude in meters + float _horizontalAccuracy; // Horizontal position accuracy in meters + double _latitude; // Latitude in degrees + double _longitude; // Longitude in degrees + + uint8_t _day; // Day number + uint8_t _month; // Month number + uint16_t _year; + uint8_t _hour; // Hours for 24 hour clock + uint8_t _minute; + uint8_t _second; + uint16_t _millisecond; // Limited to first two digits + uint32_t _nanosecond; + + uint8_t _satellitesInView; + uint8_t _fixType; + uint8_t _carrierSolution; + + bool _validDate; // True when date is valid + bool _validTime; // True when time is valid + bool _confirmedDate; + bool _confirmedTime; + bool _fullyResolved; + uint32_t _tAcc; + + unsigned long _pvtArrivalMillis; + bool _pvtUpdated; + + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configureRadio(); + + // Set the minimum satellite signal level for navigation. + virtual bool setMinCnoRadio (uint8_t cnoValue); + + public: + + // Temporarily make these public + unsigned long _autoBaseStartTimer; // Tracks how long the base auto / averaging mode has been running + uint8_t _leapSeconds; + + // Constructor + GNSS() : _leapSeconds(18), _pvtArrivalMillis(0), _pvtUpdated(0) + { + } + + // If we have decryption keys, configure module + // Note: don't check online.lband_neo here. We could be using ip corrections + virtual void applyPointPerfectKeys(); + + // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) + virtual void baseRtcmDefault(); + + // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) + virtual void baseRtcmLowDataRate(); + + // Connect to GNSS and identify particulars + virtual void begin(); + + // Setup TM2 time stamp input as need + // Outputs: + // Returns true when an external event occurs and false if no event + virtual bool beginExternalEvent(); + + // Setup the timepulse output on the PPS pin for external triggering + // Outputs + // Returns true if the pin was successfully setup and false upon + // failure + virtual bool beginPPS(); + + virtual bool checkNMEARates(); + + virtual bool checkPPPRates(); + + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + bool configure(); + + // Configure the Base + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configureBase(); + + // Configure specific aspects of the receiver for NTP mode + virtual bool configureNtpMode(); + + // Configure the Rover + // Outputs: + // Returns true if successfully configured and false upon failure + virtual bool configureRover(); + + virtual void debuggingDisable(); + + virtual void debuggingEnable(); + + virtual void enableGgaForNtrip(); + + // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted + // even if there is no GPS fix. We use it to test serial output. + // Outputs: + // Returns true if successfully started and false upon failure + virtual bool enableRTCMTest(); + + // Restore the GNSS to the factory settings + virtual void factoryReset(); + + virtual uint16_t fileBufferAvailable(); + + virtual uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); + + // Start the base using fixed coordinates + // Outputs: + // Returns true if successfully started and false upon failure + virtual bool fixedBaseStart(); + + // Return the number of active/enabled messages + virtual uint8_t getActiveMessageCount(); + + // Get the altitude + // Outputs: + // Returns the altitude in meters or zero if the GNSS is offline + virtual double getAltitude(); + + // Returns the carrier solution or zero if not online + virtual uint8_t getCarrierSolution(); + + virtual uint32_t getDataBaudRate(); + + // Returns the day number or zero if not online + virtual uint8_t getDay(); + + // Return the number of milliseconds since GNSS data was last updated + virtual uint16_t getFixAgeMilliseconds(); + + // Returns the fix type or zero if not online + virtual uint8_t getFixType(); + + // Returns the hours of 24 hour clock or zero if not online + virtual uint8_t getHour(); + + // Get the horizontal position accuracy + // Outputs: + // Returns the horizontal position accuracy or zero if offline + virtual float getHorizontalAccuracy(); + + virtual const char * getId(); + + // Get the latitude value + // Outputs: + // Returns the latitude value or zero if not online + virtual double getLatitude(); + + // Query GNSS for current leap seconds + virtual uint8_t getLeapSeconds(); + + // Get the longitude value + // Outputs: + // Returns the longitude value or zero if not online + virtual double getLongitude(); + + // Returns two digits of milliseconds or zero if not online + virtual uint8_t getMillisecond(); + + // Get the minimum satellite signal level for navigation. + uint8_t getMinCno(); + + // Returns minutes or zero if not online + virtual uint8_t getMinute(); + + // Returns month number or zero if not online + virtual uint8_t getMonth(); + + // Returns nanoseconds or zero if not online + virtual uint32_t getNanosecond(); + + virtual uint32_t getRadioBaudRate(); + + // Returns the seconds between solutions + virtual double getRateS(); + + virtual const char * getRtcmDefaultString(); + + virtual const char * getRtcmLowDataRateString(); + + // Returns the number of satellites in view or zero if offline + virtual uint8_t getSatellitesInView(); + + // Returns seconds or zero if not online + virtual uint8_t getSecond(); + + // Get the survey-in mean accuracy + // Outputs: + // Returns the mean accuracy or zero (0) + virtual float getSurveyInMeanAccuracy(); + + // Return the number of seconds the survey-in process has been running + virtual int getSurveyInObservationTime(); + + float getSurveyInStartingAccuracy(); + + // Returns timing accuracy or zero if not online + virtual uint32_t getTimeAccuracy(); + + // Returns full year, ie 2023, not 23. + virtual uint16_t getYear(); + + virtual bool isBlocking(); + + // Date is confirmed once we have GNSS fix + virtual bool isConfirmedDate(); + + // Date is confirmed once we have GNSS fix + virtual bool isConfirmedTime(); + + // Return true if GNSS receiver has a higher quality DGPS fix than 3D + virtual bool isDgpsFixed(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have a valid fix, not what type of fix + // This function checks to see if the given platform has reached + // sufficient fix type to be considered valid + virtual bool isFixed(); + + // Used in tpISR() for time pulse synchronization + virtual bool isFullyResolved(); + + virtual bool isPppConverged(); + + virtual bool isPppConverging(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Fix. This function checks to see if the + // given platform has reached sufficient fix type to be considered valid + virtual bool isRTKFix(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Float. This function checks to see if + // the given platform has reached sufficient fix type to be considered + // valid + virtual bool isRTKFloat(); + + // Determine if the survey-in operation is complete + // Outputs: + // Returns true if the survey-in operation is complete and false + // if the operation is still running + virtual bool isSurveyInComplete(); + + // Date will be valid if the RTC is reporting (regardless of GNSS fix) + virtual bool isValidDate(); + + // Time will be valid if the RTC is reporting (regardless of GNSS fix) + virtual bool isValidTime(); + + // Controls the constellations that are used to generate a fix and logged + virtual void menuConstellations(); + + virtual void menuMessageBaseRtcm(); + + // Control the messages that get broadcast over Bluetooth and logged (if enabled) + virtual void menuMessages(); + + // Print the module type and firmware version + virtual void printModuleInfo(); + + // Send correction data to the GNSS + // Inputs: + // dataToSend: Address of a buffer containing the data + // dataLength: The number of valid data bytes in the buffer + // Outputs: + // Returns the number of correction data bytes written + virtual int pushRawData(uint8_t *dataToSend, int dataLength); + + virtual uint16_t rtcmBufferAvailable(); + + // If LBand is being used, ignore any RTCM that may come in from the GNSS + virtual void rtcmOnGnssDisable(); + + // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver + virtual void rtcmOnGnssEnable(); + + virtual uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); + + // Save the current configuration + // Outputs: + // Returns true when the configuration was saved and false upon failure + virtual bool saveConfiguration(); + + // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS + // This just sets the GNSS side + // Used during Bluetooth testing + // Inputs: + // baudRate: The desired baudrate + virtual bool setBaudrate(uint32_t baudRate); + + // Enable all the valid constellations and bands for this platform + virtual bool setConstellations(); + + virtual bool setDataBaudRate(uint32_t baud); + + // Set the elevation in degrees + // Inputs: + // elevationDegrees: The elevation value in degrees + virtual bool setElevation(uint8_t elevationDegrees); + + // Enable all the valid messages for this platform + virtual bool setMessages(int maxRetries); + + // Enable all the valid messages for this platform over the USB port + virtual bool setMessagesUsb(int maxRetries); + + // Set the minimum satellite signal level for navigation. + bool setMinCno(uint8_t cnoValue); + + // Set the dynamic model to use for RTK + // Inputs: + // modelNumber: Number of the model to use, provided by radio library + virtual bool setModel(uint8_t modelNumber); + + virtual bool setRadioBaudRate(uint32_t baud); + + // Specify the interval between solutions + // Inputs: + // secondsBetweenSolutions: Number of seconds between solutions + // Outputs: + // Returns true if the rate was successfully set and false upon + // failure + virtual bool setRate(double secondsBetweenSolutions); + + virtual bool setTalkerGNGGA(); + + // Hotstart GNSS to try to get RTK lock + virtual bool softwareReset(); + + virtual bool standby(); + + // Reset the survey-in operation + // Outputs: + // Returns true if the survey-in operation was reset successfully + // and false upon failure + virtual bool surveyInReset(); + + // Start the survey-in operation + // Outputs: + // Return true if successful and false upon failure + virtual bool surveyInStart(); + + // Poll routine to update the GNSS state + virtual void update(); +}; + +#endif // __GNSS_H__ diff --git a/Firmware/RTK_Everywhere/GNSS.ino b/Firmware/RTK_Everywhere/GNSS.ino index 43687bd1e..15b21e2b6 100644 --- a/Firmware/RTK_Everywhere/GNSS.ino +++ b/Firmware/RTK_Everywhere/GNSS.ino @@ -1,17 +1,15 @@ -// Connect to GNSS and identify particulars -void gnssBegin() -{ - if (present.gnss_zedf9p) - zedBegin(); - else if (present.gnss_um980) - um980Begin(); - else if (present.gnss_mosaicX5) - mosaicX5Begin(); -} +/*------------------------------------------------------------------------------ +GNSS.ino + GNSS layer implementation +------------------------------------------------------------------------------*/ + +//---------------------------------------- // Setup the general configuration of the GNSS // Not Rover or Base specific (ie, baud rates) -bool gnssConfigure() +// Returns true if successfully configured and false otherwise +//---------------------------------------- +bool GNSS::configure() { if (online.gnss == false) return (false); @@ -19,1775 +17,32 @@ bool gnssConfigure() // Check various setting arrays (message rates, etc) to see if they need to be reset to defaults checkGNSSArrayDefaults(); - if (present.gnss_zedf9p) - { - // Configuration can take >1s so configure during splash - if (zedConfigure() == false) - return (false); - } - else if (present.gnss_um980) - { - if (um980Configure() == false) - return (false); - } - else if (present.gnss_mosaicX5) - { - if (mosaicX5Configure() == false) - return (false); - } - - return (true); -} - -bool gnssConfigureRover() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedConfigureRover()); - } - else if (present.gnss_um980) - { - return (um980ConfigureRover()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5ConfigureRover()); - } - } - return (false); -} - -bool gnssConfigureBase() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedConfigureBase()); - } - else if (present.gnss_um980) - { - return (um980ConfigureBase()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5ConfigureBase()); - } - } - return (false); -} - -void gnssUpdate() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->checkUblox(); // Regularly poll to get latest data and any RTCM - theGNSS->checkCallbacks(); // Process any callbacks: ie, eventTriggerReceived - } - else if (present.gnss_um980) - { - // We don't check serial data here; the gnssReadTask takes care of serial consumption - } - else if (present.gnss_mosaicX5) - { - // We don't check serial data here; the gnssReadTask takes care of serial consumption - mosaicX5Housekeeping(); // Housekeeping - update sdFreeSpace, logIncreasing etc. - } - } -} - -bool gnssSurveyInStart() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSurveyInStart()); - } - else if (present.gnss_um980) - { - return (um980BaseAverageStart()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5AutoBaseStart()); // setPVTMode,Static, ,auto - } - } - return (false); -} - -bool gnssIsSurveyComplete() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (theGNSS->getSurveyInValid(50)); - } - else if (present.gnss_um980) - { - // Return true once enough time, since the start of the base mode, has elapsed - int elapsedSeconds = (millis() - autoBaseStartTimer) / 1000; - - if (elapsedSeconds > settings.observationSeconds) - return (true); - return (false); - } - else if (present.gnss_mosaicX5) - { - // Bit 6: Set if the user has entered the command setPVTMode, Static, , auto - // and the receiver is still in the process of determining its fixed position. - return (mosaicX5AutoBaseComplete()); - } - } - return (false); -} - -bool gnssSurveyInReset() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSurveyInReset()); - } - else if (present.gnss_um980) - { - // Put UM980 into rover mode to cancel base averaging mode - return (um980SetModeRoverSurvey()); - } - else if (present.gnss_mosaicX5) - { - // Put mosaicX5 into rover mode to cancel auto base mode - return (mosaicX5SurveyReset()); - } - } - return (false); -} - -bool gnssFixedBaseStart() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedFixedBaseStart()); - } - else if (present.gnss_um980) - { - return (um980FixedBaseStart()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5FixedBaseStart()); - } - } - return (false); -} - -void gnssEnableRTCMTest() -{ - // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted - // even if there is no GPS fix. We use it to test serial output. - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->newCfgValset(); // Create a new Configuration Item VALSET message - theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_UART2, 1); // Enable message 1230 every second - theGNSS->sendCfgValset(); // Send the VALSET - } - else if (present.gnss_um980) - { - // There is no data port on devices with the UM980 - } - else if (present.gnss_mosaicX5) - { - // Enable RTCM1230 on COM2 (Radio connector) - mosaicX5EnableRTCMTest(); - } - } -} - -// If LBand is being used, ignore any RTCM that may come in from the GNSS -void gnssDisableRtcmOnGnss() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->setUART2Input(COM_TYPE_UBX); // Set ZED's UART2 to input UBX (no RTCM) - } - else if (present.gnss_um980) - { - // UM980 does not have a separate interface for RTCM - } - else if (present.gnss_mosaicX5) - { - // TODO: is this needed? - } - } -} - -// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver -// UNUSED. TODO: delete this? -void gnssEnableRtcmOnGnss() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->setUART2Input(COM_TYPE_RTCM3); // Set the ZED's UART2 to input RTCM - } - else if (present.gnss_um980) - { - // UM980 does not have separate interface for RTCM - } - else if (present.gnss_mosaicX5) - { - // TODO: is this needed? - } - } -} - -// Return the number of seconds the survey-in process has been running -int gnssGetSurveyInObservationTime() -{ - static uint16_t svinObservationTime = 0; - static unsigned long lastCheck = 0; - - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - // Use a local static so we don't have to request these values multiple times (ZED takes many ms to respond - // to this command) - if (millis() - lastCheck > 1000) - { - lastCheck = millis(); - svinObservationTime = theGNSS->getSurveyInObservationTime(50); - } - return (svinObservationTime); - } - else if (present.gnss_um980 || present.gnss_mosaicX5) - { - int elapsedSeconds = (millis() - autoBaseStartTimer) / 1000; - return (elapsedSeconds); - } - } - return (0); -} - -// TODO make sure we're not slowing down a ZED base -float gnssGetSurveyInMeanAccuracy() -{ - static float svinMeanAccuracy = 0; - static unsigned long lastCheck = 0; - - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - // Use a local static so we don't have to request these values multiple times (ZED takes many ms to respond - // to this command) - if (millis() - lastCheck > 1000) - { - lastCheck = millis(); - svinMeanAccuracy = theGNSS->getSurveyInMeanAccuracy(50); - } - return (svinMeanAccuracy); - } - else if (present.gnss_um980) - { - // Not supported on the UM980 - // Return the current HPA instead - return (um980GetHorizontalAccuracy()); - } - else if (present.gnss_mosaicX5) - { - // Not supported on the mosaicX5 - // Return the current HPA instead - return (mosaicX5GetHorizontalAccuracy()); - } - } - return (0); -} - -// Setup TM2 time stamp input as need -bool gnssBeginExternalEvent() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedBeginExternalEvent()); - } - else if (present.gnss_um980) - { - // UM980 Event signal not exposed - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5BeginExternalEvent()); - } - } - return (false); -} - -// Setup the timepulse output on the PPS pin for external triggering -bool gnssBeginPPS() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedBeginPPS()); - } - else if (present.gnss_um980) - { - // UM980 PPS signal not exposed - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5BeginPPS()); - // spps - } - } - return (false); -} - -// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS -// This just sets the GNSS side -// Used during Bluetooth testing -void gnssSetBaudrate(uint32_t baudRate) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->setVal32(UBLOX_CFG_UART1_BAUDRATE, - (115200 * 2)); // Defaults to 230400 to maximize message output support - } - else if (present.gnss_um980) - { - // Set the baud rate on COM3 of the UM980 - um980SetBaudRateCOM3(baudRate); - } - else if (present.gnss_mosaicX5) - { - // Set the baud rate on COM1 of the X5 - mosaicX5SetBaudRateCOM(1, baudRate); - } - } -} - -int gnssPushRawData(uint8_t *dataToSend, int dataLength) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (theGNSS->pushRawData((uint8_t *)dataToSend, dataLength)); - } - else if (present.gnss_um980) - { - // Send data directly from ESP GNSS UART to UM980 UART3 - return (um980PushRawData((uint8_t *)dataToSend, dataLength)); - } - else if (present.gnss_mosaicX5) - { - // Send data directly from ESP GNSS UART to mosaic-X5 COM1 - return (mosaicX5PushRawData((uint8_t *)dataToSend, dataLength)); - } - } - return (0); -} - -bool gnssSetRate(double secondsBetweenSolutions) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetRate(secondsBetweenSolutions)); - } - else if (present.gnss_um980) - { - return (um980SetRate(secondsBetweenSolutions)); - } - else if (present.gnss_mosaicX5) - { - // The X5 doesn't have a navigation rate. Instead, we set the - // message rate multiplier for sr3i, sno etc. - return (mosaicX5SetRate(secondsBetweenSolutions)); - } - } - return (false); -} - -// Returns the seconds between solutions -double gnssGetRateS(void) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetRateS()); - } - else if (present.gnss_um980) - { - return (um980GetRateS()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetRateS()); - } - } - return (0.0); -} - -bool gnssSaveConfiguration() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedSaveConfiguration(); - return (true); - } - else if (present.gnss_um980) - { - return (um980SaveConfiguration()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5SaveConfiguration()); - } - } - return (false); -} - -void gnssFactoryReset() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedFactoryReset(); - } - else if (present.gnss_um980) - { - um980FactoryReset(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5FactoryReset(); - } - } -} - -void gnssSetModel(uint8_t modelNumber) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - theGNSS->setVal8(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)modelNumber); // Set dynamic model - } - else if (present.gnss_um980) - { - um980SetModel(modelNumber); - } - else if (present.gnss_mosaicX5) - { - mosaicX5SetModel(modelNumber); - } - } -} - -void gnssSetElevation(uint8_t elevationDegrees) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedSetElevation(elevationDegrees); - } - else if (present.gnss_um980) - { - um980SetMinElevation(elevationDegrees); - } - else if (present.gnss_mosaicX5) - { - mosaicX5SetMinElevation(elevationDegrees); // sem - } - } + // Configure the radio + return configureRadio(); } -void gnssSetMinCno(uint8_t cnoValue) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedSetMinCno(cnoValue); - settings.minCNO = cnoValue; // Update the setting - } - else if (present.gnss_um980) - { - um980SetMinCNO(cnoValue); - settings.minCNO = cnoValue; // Update the setting - } - else if (present.gnss_mosaicX5) - { - mosaicX5SetMinCNO(cnoValue); // scm - settings.minCNO = cnoValue; // Update the setting - } - } -} - -uint8_t gnssGetMinCno() +//---------------------------------------- +// Get the minimum satellite signal level for navigation. +//---------------------------------------- +uint8_t GNSS::getMinCno() { return (settings.minCNO); } -double gnssGetLatitude() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetLatitude()); - } - else if (present.gnss_um980) - { - return (um980GetLatitude()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetLatitude()); - } - } - return (0); -} - -double gnssGetLongitude() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetLongitude()); - } - else if (present.gnss_um980) - { - return (um980GetLongitude()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetLongitude()); - } - } - return (0); -} - -double gnssGetAltitude() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetAltitude()); - } - else if (present.gnss_um980) - { - return (um980GetAltitude()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetAltitude()); - } - } - return (0); -} - -// Date and Time will be valid if ZED's RTC is reporting (regardless of GNSS fix) -bool gnssIsValidDate() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedIsValidDate()); - } - else if (present.gnss_um980) - { - return (um980IsValidDate()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5IsValidDate()); - } - } - return (false); -} -bool gnssIsValidTime() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedIsValidTime()); - } - else if (present.gnss_um980) - { - return (um980IsValidTime()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5IsValidTime()); - } - } - return (false); -} - -// Date and Time are confirmed once we have GNSS fix -bool gnssIsConfirmedDate() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedIsConfirmedDate()); - } - else if (present.gnss_um980) - { - // UM980 doesn't have this feature. Check for valid date. - return (um980IsValidDate()); - } - else if (present.gnss_mosaicX5) - { - // UM980 doesn't have this feature. Check for valid date. - return (mosaicX5IsValidDate()); - } - } - return (false); -} -bool gnssIsConfirmedTime() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedIsConfirmedTime()); - } - else if (present.gnss_um980) - { - // UM980 doesn't have this feature. Check for valid time. - return (um980IsValidTime()); - } - else if (present.gnss_mosaicX5) - { - // UM980 doesn't have this feature. Check for valid time. - return (mosaicX5IsValidTime()); - } - } - return (false); -} - -// Used in tpISR() for time pulse synchronization -bool gnssIsFullyResolved() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedIsFullyResolved()); - } - else if (present.gnss_um980) - { - return (um980IsFullyResolved()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5IsFullyResolved()); - } - } - return (false); -} - -// Used in tpISR() for time pulse synchronization -uint32_t gnssGetTimeAccuracy() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetTimeAccuracy()); // Returns nanoseconds - } - else if (present.gnss_um980) - { - return (um980GetTimeDeviation()); // Returns nanoseconds - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetTimeDeviation()); - } - } - return (0); -} - -uint8_t gnssGetSatellitesInView() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetSatellitesInView()); - } - else if (present.gnss_um980) - { - return (um980GetSatellitesInView()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetSatellitesInView()); - } - } - return (0); -} - -// ZED: 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead reckoning combined, 5 = time only -// fix -// UM980: 0 = None, 1 = FixedPos, 8 = DopplerVelocity, 16 = Single, ... -uint8_t gnssGetFixType() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetFixType()); // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead - // reckoning combined, 5 = time only fix - } - else if (present.gnss_um980) - { - return (um980GetPositionType()); // 0 = None, 1 = FixedPos, 8 = DopplerVelocity, 16 = Single, ... - } - else if (present.gnss_mosaicX5) - { - // Bits 0-3: type of PVT solution: - // 0: No GNSS PVT available - // 1: Stand-Alone PVT - // 2: Differential PVT - // 3: Fixed location - // 4: RTK with fixed ambiguities - // 5: RTK with float ambiguities - // 6: SBAS aided PVT - // 7: moving-base RTK with fixed ambiguities - // 8: moving-base RTK with float ambiguities - // 9: Reserved - // 10: Precise Point Positioning (PPP) - // 12: Reserved - return (mosaicX5GetPositionType()); - } - } - return (0); -} - -// Some functions (L-Band area frequency determination) merely need to know if we have a valid fix, not what type of fix -// This function checks to see if the given platform has reached sufficient fix type to be considered valid -bool gnssIsFixed() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - if (zedGetFixType() >= 3) // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead - // reckoning combined, 5 = time only fix - return (true); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() >= 16) // 16 = 3D Fix (Single) - return (true); - } - else if (present.gnss_mosaicX5) - { - // Bits 0-3: type of PVT solution: - // 0: No GNSS PVT available - // 1: Stand-Alone PVT - // 2: Differential PVT - // 3: Fixed location - // 4: RTK with fixed ambiguities - // 5: RTK with float ambiguities - // 6: SBAS aided PVT - // 7: moving-base RTK with fixed ambiguities - // 8: moving-base RTK with float ambiguities - // 9: Reserved - // 10: Precise Point Positioning (PPP) - // 12: Reserved - if (mosaicX5GetPositionType() > 0) - return (true); - } - } - return (false); -} - -// Return true if GNSS receiver has a higher quality DGPS fix than 3D -bool gnssIsDgpsFixed() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - // Not supported - return (false); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() == 17) // 17 = Pseudorange differential solution - return (true); - } - else if (present.gnss_mosaicX5) - { - // 2: Differential PVT - // 6: SBAS aided PVT - if ((mosaicX5GetPositionType() == 2) || (mosaicX5GetPositionType() == 6)) - return (true); - } - } - return (false); -} - -// ZED: 0 = No RTK, 1 = RTK Float, 2 = RTK Fix -// UM980: 0 = Solution computed, 1 = Insufficient observation, 3 = No convergence, 4 = Covariance trace -// mosaic-X5: 0 = No RTK, 1 = RTK Float (Mode = 5), 2 = RTK Fixed (Mode = 4) -uint8_t gnssGetCarrierSolution() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetCarrierSolution()); - } - else if (present.gnss_um980) - { - return (um980GetSolutionStatus()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetSolutionStatus()); - } - } - return (0); -} - -// Some functions (L-Band area frequency determination) merely need to know if we have an RTK Fix. -// This function checks to see if the given platform has reached sufficient fix type to be considered valid -bool gnssIsRTKFix() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - if (zedGetCarrierSolution() == 2) // 0 = No RTK, 1 = RTK Float, 2 = RTK Fix - return (true); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() == 50) // 50 = RTK Fixed (Narrow-lane fixed solution) - return (true); - } - else if (present.gnss_mosaicX5) - { - // 4: RTK with fixed ambiguities - if (mosaicX5GetPositionType() == 4) - return (true); - } - } - return (false); -} - -// Some functions (L-Band area frequency determination) merely need to know if we have an RTK Float. -// This function checks to see if the given platform has reached sufficient fix type to be considered valid -bool gnssIsRTKFloat() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - if (zedGetCarrierSolution() == 1) // 0 = No RTK, 1 = RTK Float, 2 = RTK Fix - return (true); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() == 49 || - um980GetPositionType() == 34) // 49 = Wide-lane fixed solution, 34 = Narrow-land float solution - return (true); - } - else if (present.gnss_mosaicX5) - { - // 5: RTK with float ambiguities - if (mosaicX5GetPositionType() == 5) - return (true); - } - } - return (false); -} - -bool gnssIsPppConverging() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (false); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() == 68) // 68 = PPP solution converging - return (true); - } - else if (present.gnss_mosaicX5) - { - // 10: Precise Point Positioning (PPP) ? Is this what we want? TODO - if (mosaicX5GetPositionType() == 10) - return (true); - } - } - return (false); -} - -bool gnssIsPppConverged() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (false); - } - else if (present.gnss_um980) - { - if (um980GetPositionType() == 69) // 69 = Precision Point Positioning - return (true); - } - else if (present.gnss_mosaicX5) - { - // 10: Precise Point Positioning (PPP) ? Is this what we want? TODO - if (mosaicX5GetPositionType() == 10) - return (true); - } - } - return (false); -} - -float gnssGetHorizontalAccuracy() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetHorizontalAccuracy()); - } - else if (present.gnss_um980) - { - return (um980GetHorizontalAccuracy()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetHorizontalAccuracy()); - } - } - return (0); -} - -// Return full year, ie 2023, not 23. -uint16_t gnssGetYear() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetYear()); - } - else if (present.gnss_um980) - { - return (um980GetYear()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetYear()); - } - } - return (0); -} -uint8_t gnssGetMonth() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetMonth()); - } - else if (present.gnss_um980) - { - return (um980GetMonth()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetMonth()); - } - } - return (0); -} -uint8_t gnssGetDay() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetDay()); - } - else if (present.gnss_um980) - { - return (um980GetDay()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetDay()); - } - } - return (0); -} -uint8_t gnssGetHour() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetHour()); - } - else if (present.gnss_um980) - { - return (um980GetHour()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetHour()); - } - } - return (0); -} -uint8_t gnssGetMinute() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetMinute()); - } - else if (present.gnss_um980) - { - return (um980GetMinute()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetMinute()); - } - } - return (0); -} -uint8_t gnssGetSecond() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetSecond()); - } - else if (present.gnss_um980) - { - return (um980GetSecond()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetSecond()); - } - } - return (0); -} - -// Limit to two digits -uint8_t gnssGetMillisecond() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetMillisecond()); - } - else if (present.gnss_um980) - { - return (um980GetMillisecond()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetMillisecond()); - } - } - return (0); -} - -// Used in convertGnssTimeToEpoch() -uint32_t gnssGetNanosecond() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetNanosecond()); // Return nanosecond fraction of a second of UTC - } - else if (present.gnss_um980) - { - // UM980 does not have nanosecond, but it does have millisecond - return (um980GetMillisecond() * 1000L); // Convert to ns - } - else if (present.gnss_mosaicX5) - { - // mosaicX5 does not have nanosecond, but it does have millisecond (from ToW) - return (mosaicX5GetMillisecond() * 1000L); // Convert to ns - } - } - return (0); -} - -// Return the number of milliseconds since GNSS data was last updated -uint16_t gnssGetFixAgeMilliseconds() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedFixAgeMilliseconds()); - } - else if (present.gnss_um980) - { - return (um980FixAgeMilliseconds()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5FixAgeMilliseconds()); - } - } - return (65000); -} - -void gnssPrintModuleInfo() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedPrintInfo(); - } - else if (present.gnss_um980) - { - um980PrintInfo(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5PrintInfo(); - } - } -} - -void gnssEnableDebugging() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedEnableDebugging(); - } - else if (present.gnss_um980) - { - um980EnableDebugging(); - } - else if (present.gnss_mosaicX5) - { - ; // TODO - } - } -} -void gnssDisableDebugging() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedDisableDebugging(); - } - else if (present.gnss_um980) - { - um980DisableDebugging(); - } - else if (present.gnss_mosaicX5) - { - ; // TODO - } - } -} - -void gnssSetTalkerGNGGA() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedSetTalkerGNGGA(); - } - else if (present.gnss_um980) - { - // TODO um980SetTalkerGNGGA(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5SetTalkerGNGGA(); - } - } -} -void gnssEnableGgaForNtrip() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - zedEnableGgaForNtrip(); - } - else if (present.gnss_um980) - { - // TODO um980EnableGgaForNtrip(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5EnableGgaForNtrip(); - } - } -} - -uint16_t gnssRtcmBufferAvailable() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedRtcmBufferAvailable()); - } - else if (present.gnss_um980) - { - // TODO return(um980RtcmBufferAvailable()); - return (0); - } - else if (present.gnss_mosaicX5) - { - // TODO return(mosaicX5RtcmBufferAvailable()); - return (0); - } - } - return (0); -} - -uint16_t gnssRtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedRtcmRead(rtcmBuffer, rtcmBytesToRead)); - } - else if (present.gnss_um980) - { - // TODO return(um980RtcmRead(rtcmBuffer, rtcmBytesToRead)); - return (0); - } - else if (present.gnss_mosaicX5) - { - // TODO return(mosaicX5RtcmRead(rtcmBuffer, rtcmBytesToRead)); - return (0); - } - } - return (0); -} - -// Enable all the valid messages for this platform -bool gnssSetMessages(int maxRetries) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetMessages(maxRetries)); - } - else if (present.gnss_um980) - { - // We probably don't need this for the UM980 - // TODO return(um980SetMessages(maxRetries)); - return (true); - } - else if (present.gnss_mosaicX5) - { - return(mosaicX5SetMessages(maxRetries)); - } - } - return (false); -} - -bool gnssSetMessagesUsb(int maxRetries) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetMessagesUsb(maxRetries)); - } - else if (present.gnss_um980) - { - // We probably don't need this for the UM980 - // TODO return(um980SetMessagesUsb(maxRetries)); - return (true); - } - else if (present.gnss_mosaicX5) - { - return(mosaicX5SetMessagesUsb(maxRetries)); - } - } - return (false); -} - -bool gnssSetConstellations() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetConstellations(true)); // Send fully formed setVal list - } - else if (present.gnss_um980) - { - return (um980SetConstellations()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5SetConstellations()); - } - } - return (false); -} - -uint16_t gnssFileBufferAvailable() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedFileBufferAvailable()); - } - else if (present.gnss_um980) - { - // TODO return(um980FileBufferAvailable()); - return (0); - } - else if (present.gnss_mosaicX5) - { - return (0); - } - } - - return (0); -} - -uint16_t gnssExtractFileBufferData(uint8_t *fileBuffer, int fileBytesToRead) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedExtractFileBufferData(fileBuffer, fileBytesToRead)); - } - else if (present.gnss_um980) - { - // TODO return(um980FileBufferAvailable()); - return (0); - } - else if (present.gnss_mosaicX5) - { - return (0); - } - } - - return (0); -} - -char *gnssGetId() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (gnssUniqueId); - } - else if (present.gnss_um980) - { - return (um980GetId()); - } - else if (present.gnss_mosaicX5) - { - return (gnssUniqueId); - } - } - - return ((char *)"\0"); -} - -// Query GNSS for current leap seconds -uint8_t gnssGetLeapSeconds() -{ - if (online.gnss == true) - { - if (leapSeconds == 0) // Check to see if we've already set it - { - if (present.gnss_zedf9p) - { - return (zedGetLeapSeconds()); - } - else if (present.gnss_um980) - { - return (um980GetLeapSeconds()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetLeapSeconds()); - } - } - } - return (18); // Default to 18 if GNSS is offline -} - -void gnssApplyPointPerfectKeys() -{ - if (present.gnss_zedf9p) - { - zedApplyPointPerfectKeys(); - } - else if (present.gnss_um980) - { - // Taken care of in beginPPL() - } - else if (present.gnss_mosaicX5) - { - // Taken care of in beginPPL() - } -} - -// Return the number of active/enabled messages -uint8_t gnssGetActiveMessageCount() -{ - if (present.gnss_zedf9p) - { - return (zedGetActiveMessageCount()); - } - else if (present.gnss_um980) - { - return (um980GetActiveMessageCount()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetActiveMessageCount()); - } - return (0); -} - -void gnssMenuMessages() -{ - if (present.gnss_zedf9p) - { - zedMenuMessages(); - } - else if (present.gnss_um980) - { - um980MenuMessages(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5MenuMessages(); - } -} - -void gnssMenuMessageBaseRtcm() -{ - if (present.gnss_zedf9p) - { - zedMenuMessagesSubtype(settings.ubxMessageRatesBase, "RTCM-Base"); - } - else if (present.gnss_um980) - { - um980MenuMessagesSubtype(settings.um980MessageRatesRTCMBase, "RTCMBase"); - } - else if (present.gnss_mosaicX5) - { - mosaicX5MenuMessagesRTCM(false); - } -} - -// Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) -void gnssBaseRtcmDefault() -{ - if (present.gnss_zedf9p) - { - zedBaseRtcmDefault(); - } - else if (present.gnss_um980) - { - um980BaseRtcmDefault(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5BaseRtcmDefault(); - } -} - -// Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) -void gnssBaseRtcmLowDataRate() -{ - if (present.gnss_zedf9p) - { - zedBaseRtcmLowDataRate(); - } - else if (present.gnss_um980) - { - um980BaseRtcmLowDataRate(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5BaseRtcmLowDataRate(); - } -} - -char *gnssGetRtcmDefaultString() -{ - if (present.gnss_zedf9p) - { - return (zedGetRtcmDefaultString()); - } - else if (present.gnss_um980) - { - return (um980GetRtcmDefaultString()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetRtcmDefaultString()); - } - return ((char *)"Error"); -} - -char *gnssGetRtcmLowDataRateString() -{ - if (present.gnss_zedf9p) - { - return (zedGetRtcmLowDataRateString()); - } - else if (present.gnss_um980) - { - return (um980GetRtcmLowDataRateString()); - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetRtcmLowDataRateString()); - } - return ((char *)"Error"); -} - -float gnssGetSurveyInStartingAccuracy() +//---------------------------------------- +float GNSS::getSurveyInStartingAccuracy() { return (settings.surveyInStartingAccuracy); } -void gnssMenuConstellations() -{ - if (present.gnss_zedf9p) - { - zedMenuConstellations(); - } - else if (present.gnss_um980) - { - um980MenuConstellations(); - } - else if (present.gnss_mosaicX5) - { - mosaicX5MenuConstellations(); - } -} - -bool gnssIsBlocking() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (false); - } - else if (present.gnss_um980) - { - return (um980IsBlocking()); - } - else if (present.gnss_mosaicX5) - { - return (false); - } - } - return (false); -} - -uint32_t gnssGetRadioBaudRate() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetRadioBaudRate()); - } - else if (present.gnss_um980) - { - return (0); // UM980 has no multiplexer - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetRadioBaudRate()); - } - } - return (0); -} - -bool gnssSetRadioBaudRate(uint32_t baud) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetRadioBaudRate(baud)); - } - else if (present.gnss_um980) - { - return false; // UM980 has no multiplexer - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5SetRadioBaudRate(baud)); - } - } - return false; -} - -uint32_t gnssGetDataBaudRate() +//---------------------------------------- +// Set the minimum satellite signal level for navigation. +//---------------------------------------- +bool GNSS::setMinCno(uint8_t cnoValue) { - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedGetDataBaudRate()); - } - else if (present.gnss_um980) - { - return (0); // UM980 has no multiplexer - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5GetDataBaudRate()); - } - } - return (0); -} - -bool gnssSetDataBaudRate(uint32_t baud) -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return (zedSetDataBaudRate(baud)); - } - else if (present.gnss_um980) - { - return false; // UM980 has no multiplexer - } - else if (present.gnss_mosaicX5) - { - return (mosaicX5SetDataBaudRate(baud)); - } - } - return false; -} + // Update the setting + settings.minCNO = cnoValue; -bool gnssStandby() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return true; // TODO - this would be a perfect place for Save-On-Shutdown - } - else if (present.gnss_um980) - { - return true; - } - else if (present.gnss_mosaicX5) - { - return mosaicX5Standby(); - } - } - return false; -} - -bool checkGnssNMEARates() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return zedCheckGnssNMEARates(); - } - else if (present.gnss_um980) - { - return false; - } - else if (present.gnss_mosaicX5) - { - return mosaicX5CheckGnssNMEARates(); - } - } - return false; -} - -bool checkGnssPPPRates() -{ - if (online.gnss == true) - { - if (present.gnss_zedf9p) - { - return zedCheckGnssPPPRates(); - } - else if (present.gnss_um980) - { - return false; - } - else if (present.gnss_mosaicX5) - { - return settings.enableLoggingRINEX; - } - } - return false; + // Pass the value to the GNSS receiver + return gnss->setMinCnoRadio(cnoValue); } diff --git a/Firmware/RTK_Everywhere/LoRa.ino b/Firmware/RTK_Everywhere/LoRa.ino index 80434caa4..6e6e4ab34 100644 --- a/Firmware/RTK_Everywhere/LoRa.ino +++ b/Firmware/RTK_Everywhere/LoRa.ino @@ -125,7 +125,7 @@ void updateLora() case (LORA_TX_SETTLING): // While the survey is running, avoid transmitting over LoRa to allow maximum GNSS reception - if (gnssIsSurveyComplete() == true) + if (gnss->isSurveyInComplete() == true) { if (settings.debugLora == true) systemPrintln("LoRa: Moving to TX"); @@ -173,7 +173,7 @@ void updateLora() if (correctionLastSeen(CORR_RADIO_LORA)) { // Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS - gnssPushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module + gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu) { @@ -252,7 +252,7 @@ void updateLora() if (correctionLastSeen(CORR_RADIO_LORA)) { // Pass RTCM bytes (presumably) from LoRa out ESP32-UART to GNSS - gnssPushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module + gnss->pushRawData(rtcmData, rtcmCount); // Push RTCM to GNSS module if (((settings.debugCorrections == true) || (settings.debugLora == true)) && !inMainMenu) { diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino index e62a8a703..3f6404f70 100644 --- a/Firmware/RTK_Everywhere/MQTT_Client.ino +++ b/Firmware/RTK_Everywhere/MQTT_Client.ino @@ -368,8 +368,8 @@ void mqttClientReceiveMessage(int messageSize) float minDist = 99999.0; // Minimum distance to tile center in centidegrees char *preservedTile; char *tile = strtok_r(nodes, ",", &preservedTile); - int latitude = int(gnssGetLatitude() * 100.0); // Centidegrees - int longitude = int(gnssGetLongitude() * 100.0); // Centidegrees + int latitude = int(gnss->getLatitude() * 100.0); // Centidegrees + int longitude = int(gnss->getLongitude() * 100.0); // Centidegrees while (tile != nullptr) { char ns, ew; @@ -454,8 +454,8 @@ void mqttClientReceiveMessage(int messageSize) WeekToWToUnixEpoch(&settings.pointPerfectNextKeyStart, nextWeek, nextToW); settings.pointPerfectCurrentKeyStart -= - gnssGetLeapSeconds(); // Remove GPS leap seconds to align with u-blox - settings.pointPerfectNextKeyStart -= gnssGetLeapSeconds(); + gnss->getLeapSeconds(); // Remove GPS leap seconds to align with u-blox + settings.pointPerfectNextKeyStart -= gnss->getLeapSeconds(); settings.pointPerfectCurrentKeyStart *= 1000; // Convert to ms settings.pointPerfectNextKeyStart *= 1000; @@ -499,7 +499,7 @@ void mqttClientReceiveMessage(int messageSize) updateZEDCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed - gnssPushRawData(mqttData, mqttCount); + gnss->pushRawData(mqttData, mqttCount); bytesPushed += mqttCount; } else @@ -517,7 +517,7 @@ void mqttClientReceiveMessage(int messageSize) if (((settings.debugMqttClientData == true) || (settings.debugCorrections == true)) && !inMainMenu) systemPrintf("Pushing %d bytes from %s topic to GNSS\r\n", mqttCount, topic); - gnssPushRawData(mqttData, mqttCount); + gnss->pushRawData(mqttData, mqttCount); bytesPushed += mqttCount; } } @@ -952,9 +952,9 @@ void mqttClientUpdate() if ((strlen(settings.regionalCorrectionTopics[settings.geographicRegion]) > 0) && (settings.useLocalizedDistribution)) { - uint8_t fixType = gnssGetFixType(); - double latitude = gnssGetLatitude(); // degrees - double longitude = gnssGetLongitude(); // degrees + uint8_t fixType = gnss->getFixType(); + double latitude = gnss->getLatitude(); // degrees + double longitude = gnss->getLongitude(); // degrees if (fixType >= 3) // If we have a 3D fix { // If both the dict and tile topics are empty, prepare to subscribe to the dict topic diff --git a/Firmware/RTK_Everywhere/NTP.ino b/Firmware/RTK_Everywhere/NTP.ino index fc3b1540d..b45938c46 100644 --- a/Firmware/RTK_Everywhere/NTP.ino +++ b/Firmware/RTK_Everywhere/NTP.ino @@ -705,79 +705,10 @@ bool configureUbloxModuleNTP() log_d("Skipping ZED NTP configuration"); return (true); } - firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - gnssUpdate(); // Regularly poll to get latest data - - theGNSS->setNMEAGPGGAcallbackPtr( - nullptr); // Disable GPGGA call back that may have been set during Rover NTRIP Client mode - - int tryNo = -1; - bool success = false; - - // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < MAX_SET_MESSAGES_RETRIES) && !success) - { - bool response = true; - - // In NTP mode we force 1Hz - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - - // Survey mode is only available on ZED-F9P modules - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - - // Set dynamic model to stationary - response &= theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, DYN_MODEL_STATIONARY); // Set dynamic model - - // Set time pulse to 1Hz (100:900) - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) - response &= theGNSS->addCfgValset( - UBLOX_CFG_TP_USE_LOCKED_TP1, - 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_TP1_ENA, 1); // Enable timepulse - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_POL_TP1, 1); // 1 = rising edge - - // While the module is _locking_ to GNSS time, turn off pulse - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us - - // When the module is _locked_ to GNSS time, make it generate 1Hz (100ms high, 900ms low) - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, 1000000); // Set the period between pulses is us - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, 100000); // Set the pulse length in us - - // Ensure pulse is aligned to top-of-second. This is the default. Set it here just to make sure. - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_ALIGN_TO_TOW_TP1, 1); - - // Set the time grid to UTC. This is the default. Set it here just to make sure. - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_TIMEGRID_TP1, 0); // 0=UTC; 1=GPS - - // Sync to GNSS. This is the default. Set it here just to make sure. - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_SYNC_GNSS_TP1, 1); - - response &= theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation - - // Ensure PVT, HPPOSLLH and TP messages are being output at 1Hz on the correct port - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_PVT_I2C, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSLLH_I2C, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_UBX_TIM_TP_I2C, 1); - - response &= theGNSS->sendCfgValset(); // Closing value - - if (response) - success = true; - } - - if (!success) - systemPrintln("NTP config fail"); - - return (success); + gnss->update(); // Regularly poll to get latest data + return gnss->configureNtpMode(); } //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= diff --git a/Firmware/RTK_Everywhere/NtripClient.ino b/Firmware/RTK_Everywhere/NtripClient.ino index 9a3717825..2c01c87db 100644 --- a/Firmware/RTK_Everywhere/NtripClient.ino +++ b/Firmware/RTK_Everywhere/NtripClient.ino @@ -511,7 +511,7 @@ void ntripClientStop(bool shutdown) ntripClientTimer = millis(); // Return the Main Talker ID to "GN". - gnssSetTalkerGNGGA(); + gnss->setTalkerGNGGA(); // Determine the next NTRIP client state online.ntripClient = false; @@ -594,7 +594,7 @@ void ntripClientUpdate() // If GGA transmission is enabled, wait for GNSS lock before connecting to NTRIP Caster // If GGA transmission is not enabled, start connecting to NTRIP Caster - else if ((settings.ntripClient_TransmitGGA == false) || (gnssIsFixed() == true)) + else if ((settings.ntripClient_TransmitGGA == false) || (gnss->isFixed() == true)) { // Delay before opening the NTRIP client connection if ((millis() - ntripClientTimer) >= ntripClientConnectionAttemptTimeout) @@ -707,7 +707,7 @@ void ntripClientUpdate() { // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA // Tell the module to output GGA every 5 seconds - gnssEnableGgaForNtrip(); + gnss->enableGgaForNtrip(); lastGGAPush = millis() - NTRIPCLIENT_MS_BETWEEN_GGA; // Force immediate transmission of GGA message @@ -821,7 +821,7 @@ void ntripClientUpdate() if (correctionLastSeen(CORR_TCP)) { // Push RTCM to GNSS module over I2C / SPI - gnssPushRawData(rtcmData, rtcmCount); + gnss->pushRawData(rtcmData, rtcmCount); if ((settings.debugCorrections || settings.debugNtripClientRtcm || PERIODIC_DISPLAY(PD_NTRIP_CLIENT_DATA)) && (!inMainMenu)) diff --git a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino index 5eefc8ed5..45dbffd60 100644 --- a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino +++ b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino @@ -44,7 +44,7 @@ void updatePplTask(void *e) // ZED can internally using SPARTN direct. updateZEDCorrectionsSource(1); - gnssPushRawData(pplRtcmBuffer, rtcmLength); + gnss->pushRawData(pplRtcmBuffer, rtcmLength); if (settings.debugCorrections == true && !inMainMenu) systemPrintf("Received %d RTCM bytes from PPL. Pushed to the GNSS.\r\n", rtcmLength); @@ -233,7 +233,7 @@ void updatePPL() static unsigned long pplTime3dFixStarted; - if ((online.ppl == false) && (settings.enablePointPerfectCorrections) && (gnssIsFixed())) + if ((online.ppl == false) && (settings.enablePointPerfectCorrections) && (gnss->isFixed())) { // Start PPL only after GNSS is outputting appropriate NMEA+RTCM, we have a key, and the MQTT broker is // connected or L-Band SPARTN is being received. Don't restart the PPL if we've already tried @@ -256,7 +256,7 @@ void updatePPL() { pplReport = millis(); - if (gnssIsRTKFloat() && pplTimeFloatStarted > 0) + if (gnss->isRTKFloat() && pplTimeFloatStarted > 0) { systemPrintf("GNSS restarts: %d Time remaining before Float lock forced restart: %ds\r\n", floatLockRestarts, @@ -278,7 +278,7 @@ void updatePPL() } } - if (gnssIsRTKFloat()) + if (gnss->isRTKFloat()) { if (pplTimeFloatStarted == 0) pplTimeFloatStarted = millis(); @@ -300,7 +300,7 @@ void updatePPL() } } } - else if (gnssIsRTKFix()) + else if (gnss->isRTKFix()) { if (pplTimeFloatStarted != 0) pplTimeFloatStarted = 0; // Reset pplTimeFloatStarted @@ -320,7 +320,7 @@ void updatePPL() { // We are not in RTK Float or RTK Fix - if (gnssIsFixed() == false) + if (gnss->isFixed() == false) pplTimeFloatStarted = 0; // Reset pplTimeFloatStarted if we loose a 3D fix entirely } } diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index aa3e829e6..121ecfbdd 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -339,6 +339,8 @@ int wifiOriginalMaxConnectionAttempts = wifiMaxConnectionAttempts; // Modified d //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 +GNSS * gnss; + char neoFirmwareVersion[20]; // Output to system status menu. // Use Michael's lock/unlock methods to prevent the GNSS UART task from calling checkUblox during a sendCommand and @@ -779,7 +781,6 @@ unsigned long startTime; // Used for checking longest-running functi bool lbandCorrectionsReceived; // Used to display L-Band SIV icon when corrections are successfully decrypted (NEO-D9S only) unsigned long lastLBandDecryption; // Timestamp of last successfully decrypted PMP message from NEO-D9S volatile bool mqttMessageReceived; // Goes true when the subscribed MQTT channel reports back -uint8_t leapSeconds; // Gets set if GNSS is online unsigned long systemTestDisplayTime; // Timestamp for swapping the graphic during testing uint8_t systemTestDisplayNumber; // Tracks which test screen we're looking at unsigned long rtcWaitTime; // At power on, we give the RTC a few seconds to update during PointPerfect Key checking @@ -821,8 +822,6 @@ volatile PeriodicDisplay_t periodicDisplay; unsigned long shutdownNoChargeTimer; -unsigned long autoBaseStartTimer; // Tracks how long the base auto / averaging mode has been running - RtkMode_t rtkMode; // Mode of operation unsigned long beepLengthMs; // Number of ms to make noise @@ -1100,8 +1099,8 @@ void setup() DMW_b("displaySplash"); displaySplash(); // Display the RTK product name and firmware version - DMW_b("gnssBegin"); - gnssBegin(); // Requires settings. Connect to GNSS to get module type + DMW_b("gnss->begin"); + gnss->begin(); // Requires settings. Connect to GNSS to get module type DMW_b("beginSD"); beginSD(); // Requires settings. Test if SD is present @@ -1129,17 +1128,17 @@ void setup() DMW_b("beginCharger"); beginCharger(); // Configure battery charger - DMW_b("gnssConfigure"); - gnssConfigure(); // Requires settings. Configure GNSS module + DMW_b("gnss->configure"); + gnss->configure(); // Requires settings. Configure GNSS module DMW_b("beginLBand"); beginLBand(); // Begin L-Band DMW_b("beginExternalEvent"); - gnssBeginExternalEvent(); // Configure the event input + gnss->beginExternalEvent(); // Configure the event input DMW_b("beginPPS"); - gnssBeginPPS(); // Configure the time pulse output + gnss->beginPPS(); // Configure the time pulse output DMW_b("beginInterrupts"); beginInterrupts(); // Begin the TP interrupts @@ -1228,8 +1227,8 @@ void loop() DMW_c("periodicDisplay"); updatePeriodicDisplay(); - DMW_c("gnssUpdate"); - gnssUpdate(); + DMW_c("gnss->update"); + gnss->update(); DMW_c("stateUpdate"); stateUpdate(); @@ -1467,21 +1466,21 @@ void rtcUpdate() { lastRTCAttempt = millis(); - // gnssUpdate() is called in loop() but rtcUpdate + // gnss->update() is called in loop() but rtcUpdate // can also be called during begin. To be safe, check for fresh PVT data here. - gnssUpdate(); + gnss->update(); bool timeValid = false; - if (gnssIsValidTime() == true && - gnssIsValidDate() == true) // Will pass if ZED's RTC is reporting (regardless of GNSS fix) + if (gnss->isValidTime() == true && + gnss->isValidDate() == true) // Will pass if ZED's RTC is reporting (regardless of GNSS fix) timeValid = true; - if (gnssIsConfirmedTime() == true && gnssIsConfirmedDate() == true) // Requires GNSS fix + if (gnss->isConfirmedTime() == true && gnss->isConfirmedDate() == true) // Requires GNSS fix timeValid = true; if (timeValid && - (gnssGetFixAgeMilliseconds() > 999)) // If the GNSS time is over a second old, don't use it + (gnss->getFixAgeMilliseconds() > 999)) // If the GNSS time is over a second old, don't use it timeValid = false; if (timeValid == true) diff --git a/Firmware/RTK_Everywhere/States.ino b/Firmware/RTK_Everywhere/States.ino index e7a6fa4c6..5d0d315d8 100644 --- a/Firmware/RTK_Everywhere/States.ino +++ b/Firmware/RTK_Everywhere/States.ino @@ -98,7 +98,7 @@ void stateUpdate() // Configure for rover mode displayRoverStart(0); - if (gnssConfigureRover() == false) + if (gnss->configureRover() == false) { systemPrintln("Rover config failed"); displayRoverFail(1000); @@ -129,31 +129,31 @@ void stateUpdate() break; case (STATE_ROVER_NO_FIX): { - if (gnssIsFixed()) // 3D, 3D+DR + if (gnss->isFixed()) // 3D, 3D+DR changeState(STATE_ROVER_FIX); } break; case (STATE_ROVER_FIX): { - if (gnssIsRTKFloat()) + if (gnss->isRTKFloat()) changeState(STATE_ROVER_RTK_FLOAT); - else if (gnssIsRTKFix()) + else if (gnss->isRTKFix()) changeState(STATE_ROVER_RTK_FIX); } break; case (STATE_ROVER_RTK_FLOAT): { - if (gnssIsRTKFix() == false && gnssIsRTKFloat() == false) // No RTK + if (gnss->isRTKFix() == false && gnss->isRTKFloat() == false) // No RTK changeState(STATE_ROVER_FIX); - if (gnssIsRTKFix() == true) + if (gnss->isRTKFix() == true) changeState(STATE_ROVER_RTK_FIX); } break; case (STATE_ROVER_RTK_FIX): { - if (gnssIsRTKFix() == false && gnssIsRTKFloat() == false) // No RTK + if (gnss->isRTKFix() == false && gnss->isRTKFloat() == false) // No RTK changeState(STATE_ROVER_FIX); - if (gnssIsRTKFloat()) + if (gnss->isRTKFloat()) changeState(STATE_ROVER_RTK_FLOAT); } break; @@ -215,7 +215,7 @@ void stateUpdate() bluetoothStart(); // Restart Bluetooth with 'Base' identifier // Start the UART connected to the GNSS receiver for NMEA and UBX data (enables logging) - if (tasksStartGnssUart() && gnssConfigureBase()) + if (tasksStartGnssUart() && gnss->configureBase()) { settings.updateGNSSSettings = false; // On the next boot, no need to update the GNSS on this profile settings.lastState = STATE_BASE_NOT_STARTED; // Record this state for next POR @@ -245,26 +245,26 @@ void stateUpdate() baseStatusLedBlink(); // Toggle the base/status LED } - int siv = gnssGetSatellitesInView(); - float hpa = gnssGetHorizontalAccuracy(); + int siv = gnss->getSatellitesInView(); + float hpa = gnss->getHorizontalAccuracy(); // Check for <1m horz accuracy before starting surveyIn char accuracy[20]; char temp[20]; const char *units = getHpaUnits(hpa, temp, sizeof(temp), 2, true); // gnssGetSurveyInStartingAccuracy is 10m max - const char *accUnits = getHpaUnits(gnssGetSurveyInStartingAccuracy(), accuracy, sizeof(accuracy), 2, false); + const char *accUnits = getHpaUnits(gnss->getSurveyInStartingAccuracy(), accuracy, sizeof(accuracy), 2, false); systemPrintf("Waiting for Horz Accuracy < %s (%s): %s%s%s%s, SIV: %d\r\n", accuracy, accUnits, temp, (accUnits != units) ? " (" : "", (accUnits != units) ? units : "", (accUnits != units) ? ")" : "", siv); // On the mosaic-X5, the HPA is undefined while the GNSS is determining its fixed position // We need to skip the HPA check... - if ((hpa > 0.0 && hpa < gnssGetSurveyInStartingAccuracy()) || present.gnss_mosaicX5) + if ((hpa > 0.0 && hpa < gnss->getSurveyInStartingAccuracy()) || present.gnss_mosaicX5) { displaySurveyStart(0); // Show 'Survey' - if (gnssSurveyInStart() == true) // Begin survey + if (gnss->surveyInStart() == true) // Begin survey { displaySurveyStarted(500); // Show 'Survey Started' @@ -285,11 +285,11 @@ void stateUpdate() } // Get the data once to avoid duplicate slow responses - int observationTime = gnssGetSurveyInObservationTime(); - float meanAccuracy = gnssGetSurveyInMeanAccuracy(); - int siv = gnssGetSatellitesInView(); + int observationTime = gnss->getSurveyInObservationTime(); + float meanAccuracy = gnss->getSurveyInMeanAccuracy(); + int siv = gnss->getSatellitesInView(); - if (gnssIsSurveyComplete() == true) // Survey in complete + if (gnss->isSurveyInComplete() == true) // Survey in complete { systemPrintf("Observation Time: %d\r\n", observationTime); systemPrintln("Base survey complete! RTCM now broadcasting."); @@ -315,13 +315,13 @@ void stateUpdate() systemPrintf("Survey-In took more than %d minutes. Returning to rover mode.\r\n", maxSurveyInWait_s / 60); - if (gnssSurveyInReset() == false) + if (gnss->surveyInReset() == false) { systemPrintln("Survey reset failed - attempt 1/3"); - if (gnssSurveyInReset() == false) + if (gnss->surveyInReset() == false) { systemPrintln("Survey reset failed - attempt 2/3"); - if (gnssSurveyInReset() == false) + if (gnss->surveyInReset() == false) { systemPrintln("Survey reset failed - attempt 3/3"); } @@ -361,7 +361,7 @@ void stateUpdate() // If fixed base fails, we'll handle it here case (STATE_BASE_FIXED_NOT_STARTED): { RTK_MODE(RTK_MODE_BASE_FIXED); - bool response = gnssFixedBaseStart(); + bool response = gnss->fixedBaseStart(); if (response == true) { baseStatusLedOn(); // Turn on the base/status LED @@ -476,7 +476,7 @@ void stateUpdate() tasksStopGnssUart(); // Stop absoring GNSS serial via task zedUartPassed = false; - gnssEnableRTCMTest(); + gnss->enableRTCMTest(); RTK_MODE(RTK_MODE_TESTING); changeState(STATE_TESTING); diff --git a/Firmware/RTK_Everywhere/System.ino b/Firmware/RTK_Everywhere/System.ino index d1d807813..d854bb2d1 100644 --- a/Firmware/RTK_Everywhere/System.ino +++ b/Firmware/RTK_Everywhere/System.ino @@ -354,31 +354,31 @@ void printReports() lastPrintRoverAccuracy = millis(); PERIODIC_CLEAR(PD_MQTT_CLIENT_DATA); - if (online.gnss == true) + if (online.gnss) { // If we are in rover mode, display HPA and SIV if (inRoverMode() == true) { - float hpa = gnssGetHorizontalAccuracy(); + float hpa = gnss->getHorizontalAccuracy(); char modifiedHpa[20]; const char *hpaUnits = getHpaUnits(hpa, modifiedHpa, sizeof(modifiedHpa), 3, true); // Returns string of the HPA units systemPrintf("Rover Accuracy (%s): %s, SIV: %d GNSS State: ", hpaUnits, modifiedHpa, - gnssGetSatellitesInView()); + gnss->getSatellitesInView()); - if (gnssIsRTKFix() == true) + if (gnss->isRTKFix() == true) systemPrint("RTK Fix"); - else if (gnssIsRTKFloat() == true) + else if (gnss->isRTKFloat() == true) systemPrint("RTK Float"); - else if (gnssIsPppConverged() == true) + else if (gnss->isPppConverged() == true) systemPrint("PPP Converged"); - else if (gnssIsPppConverging() == true) + else if (gnss->isPppConverging() == true) systemPrint("PPP Converging"); - else if (gnssIsDgpsFixed() == true) + else if (gnss->isDgpsFixed() == true) systemPrint("DGPS Fix"); - else if (gnssIsFixed() == true) + else if (gnss->isFixed() == true) systemPrint("3D Fix"); else systemPrint("No Fix"); @@ -389,7 +389,7 @@ void printReports() // If we are in base mode, display SIV only else if (inBaseMode() == true) { - systemPrintf("Base Mode - SIV: %d\r\n", gnssGetSatellitesInView()); + systemPrintf("Base Mode - SIV: %d\r\n", gnss->getSatellitesInView()); } } } @@ -759,18 +759,18 @@ const char *getHpaUnits(double hpa, char *buffer, int length, int decimals, bool void convertGnssTimeToEpoch(uint32_t *epochSecs, uint32_t *epochMicros) { uint32_t t = SFE_UBLOX_DAYS_FROM_1970_TO_2020; // Jan 1st 2020 as days from Jan 1st 1970 - t += (uint32_t)SFE_UBLOX_DAYS_SINCE_2020[gnssGetYear() - 2020]; // Add on the number of days since 2020 - t += (uint32_t)SFE_UBLOX_DAYS_SINCE_MONTH[gnssGetYear() % 4 == 0 ? 0 : 1] - [gnssGetMonth() - 1]; // Add on the number of days since Jan 1st - t += (uint32_t)gnssGetDay() - 1; // Add on the number of days since the 1st of the month - t *= 24; // Convert to hours - t += (uint32_t)gnssGetHour(); // Add on the hour - t *= 60; // Convert to minutes - t += (uint32_t)gnssGetMinute(); // Add on the minute - t *= 60; // Convert to seconds - t += (uint32_t)gnssGetSecond(); // Add on the second - - int32_t us = gnssGetNanosecond() / 1000; // Convert nanos to micros + t += (uint32_t)SFE_UBLOX_DAYS_SINCE_2020[gnss->getYear() - 2020]; // Add on the number of days since 2020 + t += (uint32_t)SFE_UBLOX_DAYS_SINCE_MONTH[gnss->getYear() % 4 == 0 ? 0 : 1] + [gnss->getMonth() - 1]; // Add on the number of days since Jan 1st + t += (uint32_t)gnss->getDay() - 1; // Add on the number of days since the 1st of the month + t *= 24; // Convert to hours + t += (uint32_t)gnss->getHour(); // Add on the hour + t *= 60; // Convert to minutes + t += (uint32_t)gnss->getMinute(); // Add on the minute + t *= 60; // Convert to seconds + t += (uint32_t)gnss->getSecond(); // Add on the second + + int32_t us = gnss->getNanosecond() / 1000; // Convert nanos to micros uint32_t micro; // Adjust t if nano is negative if (us < 0) diff --git a/Firmware/RTK_Everywhere/Tasks.ino b/Firmware/RTK_Everywhere/Tasks.ino index a8ac2eecd..e32fd6990 100644 --- a/Firmware/RTK_Everywhere/Tasks.ino +++ b/Firmware/RTK_Everywhere/Tasks.ino @@ -291,7 +291,7 @@ void sendGnssBuffer() { if (correctionLastSeen(CORR_BLUETOOTH)) { - if (gnssPushRawData(bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead)) + if (gnss->pushRawData(bluetoothOutgoingToGnss, bluetoothOutgoingToGnssHead)) { if ((settings.debugCorrections || PERIODIC_DISPLAY(PD_ZED_DATA_TX)) && !inMainMenu) { @@ -450,7 +450,7 @@ void gnssReadTask(void *e) // to add extra checks, above and beyond the invalidDataCallback, to make sure that doesn't happen. // Here we check that the SBF ID and length are expected / valid too. - if (gnssIsBlocking() == false) + if (gnss->isBlocking() == false) { // Determine if serial data is available while (serialGNSS->available()) @@ -1372,7 +1372,7 @@ void tickerGnssLedUpdate() // Update the GNSS LED according to our state // Solid once RTK Fix is achieved, or PPP converges - if (gnssIsRTKFix() == true || gnssIsPppConverged()) + if (gnss->isRTKFix() == true || gnss->isPppConverged()) { ledcWrite(pin_gnssStatusLED, 255); } diff --git a/Firmware/RTK_Everywhere/UM980.ino b/Firmware/RTK_Everywhere/UM980.ino index 2bcf8f532..4cc6b2b16 100644 --- a/Firmware/RTK_Everywhere/UM980.ino +++ b/Firmware/RTK_Everywhere/UM980.ino @@ -307,7 +307,7 @@ bool um980BaseAverageStart() response &= um980->setModeBaseAverage(settings.observationSeconds); // Average for a number of seconds (default is 60) - autoBaseStartTimer = millis(); // Stamp when averaging began + gnss->_autoBaseStartTimer = millis(); // Stamp when averaging began if (response == false) { @@ -983,7 +983,7 @@ void um980MenuMessages() systemPrintln(); systemPrintln("Menu: GNSS Messages"); - systemPrintf("Active messages: %d\r\n", gnssGetActiveMessageCount()); + systemPrintf("Active messages: %d\r\n", gnss->getActiveMessageCount()); systemPrintln("1) Set NMEA Messages"); systemPrintln("2) Set Rover RTCM Messages"); @@ -1261,7 +1261,7 @@ void um980MenuConstellations() } // Apply current settings to module - gnssSetConstellations(); + gnss->setConstellations(); clearBuffer(); // Empty buffer of any newline chars } diff --git a/Firmware/RTK_Everywhere/ZED.ino b/Firmware/RTK_Everywhere/ZED.ino index 4149a8b35..9c5d0306e 100644 --- a/Firmware/RTK_Everywhere/ZED.ino +++ b/Firmware/RTK_Everywhere/ZED.ino @@ -258,7 +258,7 @@ bool zedConfigure() startTime = millis(); while (pvtUpdated == false) { - gnssUpdate(); // Regularly poll to get latest data + gnss->update(); // Regularly poll to get latest data delay(10); if ((millis() - startTime) > maxWait) @@ -463,7 +463,7 @@ bool zedConfigureRover() firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - gnssUpdate(); // Regularly poll to get latest data + gnss->update(); // Regularly poll to get latest data bool success = false; int tryNo = -1; @@ -556,7 +556,7 @@ bool zedConfigureBase() firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - gnssUpdate(); // Regularly poll to get latest data + gnss->update(); // Regularly poll to get latest data theGNSS->setNMEAGPGGAcallbackPtr( nullptr); // Disable GPGGA call back that may have been set during Rover NTRIP Client mode @@ -1433,8 +1433,11 @@ uint16_t zedExtractFileBufferData(uint8_t *fileBuffer, int fileBytesToRead) // Query GNSS for current leap seconds uint8_t zedGetLeapSeconds() { + uint8_t leapSeconds; + sfe_ublox_ls_src_e leapSecSource; leapSeconds = theGNSS->getCurrentLeapSeconds(leapSecSource); + gnss->_leapSeconds = leapSeconds; return (leapSeconds); } @@ -1554,7 +1557,7 @@ void zedMenuMessages() systemPrintln(); systemPrintln("Menu: GNSS Messages"); - systemPrintf("Active messages: %d\r\n", gnssGetActiveMessageCount()); + systemPrintf("Active messages: %d\r\n", gnss->getActiveMessageCount()); systemPrintln("1) Set NMEA Messages"); systemPrintln("2) Set RTCM Messages"); @@ -1650,7 +1653,7 @@ void zedMenuMessages() clearBuffer(); // Empty buffer of any newline chars // Make sure the appropriate messages are enabled - bool response = gnssSetMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set + bool response = gnss->setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set if (response == false) systemPrintf("menuMessages: Failed to enable messages - after %d tries", MAX_SET_MESSAGES_RETRIES); else @@ -1769,7 +1772,7 @@ void zedMenuConstellations() } // Apply current settings to module - gnssSetConstellations(); + gnss->setConstellations(); clearBuffer(); // Empty buffer of any newline chars } diff --git a/Firmware/RTK_Everywhere/menuBase.ino b/Firmware/RTK_Everywhere/menuBase.ino index 13f186b30..31391828b 100644 --- a/Firmware/RTK_Everywhere/menuBase.ino +++ b/Firmware/RTK_Everywhere/menuBase.ino @@ -100,7 +100,7 @@ void menuBase() } systemPrintf("4) Set required initial positional accuracy before Survey-In: %0.2f meters\r\n", - gnssGetSurveyInStartingAccuracy()); + gnss->getSurveyInStartingAccuracy()); } } diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index e9bfad0f7..edb755c17 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -1014,7 +1014,7 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting else if (strcmp(settingName, "measurementRateHz") == 0) { - gnssSetRate(1.0 / settingValue); + gnss->setRate(1.0 / settingValue); // This is one of the first settings to be received. If seen, remove the station files. removeFile(stationCoordinateECEFFileName); @@ -1047,7 +1047,7 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting else if (strcmp(settingName, "minCNO") == 0) { // Note: this sends the Min CNO to the GNSS, as well as saving it in settings... Is this what we want? TODO - gnssSetMinCno(settingValue); + gnss->setMinCno(settingValue); knownSetting = true; } else if (strcmp(settingName, "fixedHAEAPC") == 0) @@ -1721,7 +1721,7 @@ void createSettingsString(char *newSettings) stringRecord(newSettings, "fixedBaseCoordinateTypeGeo", true); } - stringRecord(newSettings, "measurementRateHz", 1.0 / gnssGetRateS(), 2); // 2 = decimals to print + stringRecord(newSettings, "measurementRateHz", 1.0 / gnss->getRateS(), 2); // 2 = decimals to print // System state at power on. Convert various system states to either Rover or Base or NTP. int lastState; // 0 = Rover, 1 = Base, 2 = NTP @@ -1759,7 +1759,7 @@ void createSettingsString(char *newSettings) stringRecord(newSettings, "wifiConfigOverAP", 0); // 1 = AP mode, 0 = WiFi // Single variables needed on Config page - stringRecord(newSettings, "minCNO", gnssGetMinCno()); + stringRecord(newSettings, "minCNO", gnss->getMinCno()); stringRecord(newSettings, "enableRCFirmware", enableRCFirmware); // Add SD Characteristics @@ -1795,15 +1795,15 @@ void createSettingsString(char *newSettings) stringRecord(newSettings, "daysRemaining", apDaysRemaining); // Current coordinates come from HPPOSLLH call back - stringRecord(newSettings, "geodeticLat", gnssGetLatitude(), haeNumberOfDecimals); - stringRecord(newSettings, "geodeticLon", gnssGetLongitude(), haeNumberOfDecimals); - stringRecord(newSettings, "geodeticAlt", gnssGetAltitude(), 3); + stringRecord(newSettings, "geodeticLat", gnss->getLatitude(), haeNumberOfDecimals); + stringRecord(newSettings, "geodeticLon", gnss->getLongitude(), haeNumberOfDecimals); + stringRecord(newSettings, "geodeticAlt", gnss->getAltitude(), 3); double ecefX = 0; double ecefY = 0; double ecefZ = 0; - geodeticToEcef(gnssGetLatitude(), gnssGetLongitude(), gnssGetAltitude(), &ecefX, &ecefY, &ecefZ); + geodeticToEcef(gnss->getLatitude(), gnss->getLongitude(), gnss->getAltitude(), &ecefX, &ecefY, &ecefZ); stringRecord(newSettings, "ecefX", ecefX, 3); stringRecord(newSettings, "ecefY", ecefY, 3); diff --git a/Firmware/RTK_Everywhere/menuFirmware.ino b/Firmware/RTK_Everywhere/menuFirmware.ino index e8aeecbc4..2264465ca 100644 --- a/Firmware/RTK_Everywhere/menuFirmware.ino +++ b/Firmware/RTK_Everywhere/menuFirmware.ino @@ -398,7 +398,7 @@ void updateFromSD(const char *firmwareFileName) firmwareFile.close(); sd->remove(firmwareFileName); - gnssFactoryReset(); + gnss->factoryReset(); } delay(1000); diff --git a/Firmware/RTK_Everywhere/menuGNSS.ino b/Firmware/RTK_Everywhere/menuGNSS.ino index 64ab49511..a1523854f 100644 --- a/Firmware/RTK_Everywhere/menuGNSS.ino +++ b/Firmware/RTK_Everywhere/menuGNSS.ino @@ -14,10 +14,10 @@ void menuGNSS() if (!present.gnss_mosaicX5) { systemPrint("1) Set measurement rate in Hz: "); - systemPrintln(1.0 / gnssGetRateS(), 5); + systemPrintln(1.0 / gnss->getRateS(), 5); systemPrint("2) Set measurement rate in seconds between measurements: "); - systemPrintln(gnssGetRateS(), 5); + systemPrintln(gnss->getRateS(), 5); systemPrintln(" Note: The measurement rate is overridden to 1Hz when in Base mode."); } @@ -115,7 +115,7 @@ void menuGNSS() systemPrintf("5) Minimum elevation for a GNSS satellite to be used in fix (degrees): %d\r\n", settings.minElev); - systemPrintf("6) Minimum satellite signal level for navigation (dBHz): %d\r\n", gnssGetMinCno()); + systemPrintf("6) Minimum satellite signal level for navigation (dBHz): %d\r\n", gnss->getMinCno()); systemPrint("7) Toggle NTRIP Client: "); if (settings.enableNtripClient == true) @@ -166,8 +166,8 @@ void menuGNSS() if (getNewSetting("Enter GNSS measurement rate in Hz", 0.00012, 20.0, &rate) == INPUT_RESPONSE_VALID) // 20Hz limit with all constellations enabled { - gnssSetRate(1.0 / rate); // Convert Hz to seconds. This will set settings.measurementRateMs, - // settings.navigationRate, and GSV message + gnss->setRate(1.0 / rate); // Convert Hz to seconds. This will set settings.measurementRateMs, + // settings.navigationRate, and GSV message } } else if ((incoming == 2) && (!present.gnss_mosaicX5)) @@ -190,7 +190,7 @@ void menuGNSS() if (getNewSetting("Enter GNSS measurement rate in seconds between measurements", minRate, maxRate, &rate) == INPUT_RESPONSE_VALID) { - gnssSetRate(rate); // This will set settings.measurementRateMs, settings.navigationRate, and GSV message + gnss->setRate(rate); // This will set settings.measurementRateMs, settings.navigationRate, and GSV message } } else if (incoming == 3) @@ -238,7 +238,7 @@ void menuGNSS() else settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnssSetModel(settings.dynamicModel); + gnss->setModel(settings.dynamicModel); } } else if (present.gnss_um980) @@ -250,7 +250,7 @@ void menuGNSS() dynamicModel -= 1; // Align to 0 to 2 settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnssSetModel(settings.dynamicModel); + gnss->setModel(settings.dynamicModel); } } else if (present.gnss_mosaicX5) @@ -262,14 +262,14 @@ void menuGNSS() dynamicModel -= 1; // Align to 0 to MAX_MOSAIC_RX_DYNAMICS - 1 settings.dynamicModel = dynamicModel; // Recorded to NVM and file at main menu exit - gnssSetModel(settings.dynamicModel); + gnss->setModel(settings.dynamicModel); } } } } else if (incoming == 4) { - gnssMenuConstellations(); + gnss->menuConstellations(); } else if (incoming == 5) @@ -277,7 +277,7 @@ void menuGNSS() // Arbitrary 90 degree max if (getNewSetting("Enter minimum elevation in degrees", 0, 90, &settings.minElev) == INPUT_RESPONSE_VALID) { - gnssSetElevation(settings.minElev); + gnss->setElevation(settings.minElev); } } else if (incoming == 6) @@ -288,7 +288,7 @@ void menuGNSS() present.gnss_mosaicX5 ? 60 : 90, &minCNO) == INPUT_RESPONSE_VALID) { - gnssSetMinCno(minCNO); // Set the setting and configure the GNSS receiver + gnss->setMinCno(minCNO); // Set the setting and configure the GNSS receiver } } diff --git a/Firmware/RTK_Everywhere/menuMain.ino b/Firmware/RTK_Everywhere/menuMain.ino index ce13d5f89..4fd2488db 100644 --- a/Firmware/RTK_Everywhere/menuMain.ino +++ b/Firmware/RTK_Everywhere/menuMain.ino @@ -39,7 +39,7 @@ void terminalUpdate() // Push RTCM to GNSS module over I2C / SPI if (correctionLastSeen(CORR_USB)) - gnssPushRawData((uint8_t *)buffer, length); + gnss->pushRawData((uint8_t *)buffer, length); } // Does incoming data consist of RTCM correction messages @@ -58,7 +58,7 @@ void terminalUpdate() // Push RTCM to GNSS module over I2C / SPI if (correctionLastSeen(CORR_USB)) - gnssPushRawData((uint8_t *)buffer, length); + gnss->pushRawData((uint8_t *)buffer, length); } else { @@ -117,7 +117,7 @@ void menuMain() if (settings.debugGnss == true) { // Turn off GNSS debug while in config menus - gnssDisableDebugging(); + gnss->debuggingDisable(); } // Check for remote app config entry into command mode @@ -255,7 +255,7 @@ void menuMain() if (incoming == 1) menuGNSS(); else if (incoming == 2) - gnssMenuMessages(); + gnss->menuMessages(); else if (incoming == 3) menuBase(); else if (incoming == 4) @@ -322,7 +322,7 @@ void menuMain() requestChangeState(STATE_ROVER_NOT_STARTED); // Restart rover upon exit for latest changes to take effect } - gnssSaveConfiguration(); + gnss->saveConfiguration(); recordSystemSettings(); // Once all menus have exited, record the new settings to LittleFS and config file } @@ -330,7 +330,7 @@ void menuMain() if (settings.debugGnss == true) { // Re-enable GNSS debug once we exit config menus - gnssEnableDebugging(); + gnss->debuggingEnable(); } clearBuffer(); // Empty buffer of any newline chars @@ -549,7 +549,7 @@ void factoryReset(bool alreadyHasSemaphore) LittleFS.format(); if (online.gnss == true) - gnssFactoryReset(); + gnss->factoryReset(); systemPrintln("Settings erased successfully. Rebooting. Goodbye!"); delay(2000); diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino index 649a39bc7..d471a9308 100644 --- a/Firmware/RTK_Everywhere/menuMessages.ino +++ b/Firmware/RTK_Everywhere/menuMessages.ino @@ -153,8 +153,8 @@ void menuMessagesBaseRTCM() systemPrintln("1) Set RXM Messages for Base Mode"); - systemPrintf("2) Reset to Defaults (%s)\r\n", gnssGetRtcmDefaultString()); - systemPrintf("3) Reset to Low Bandwidth Link (%s)\r\n", gnssGetRtcmLowDataRateString()); + systemPrintf("2) Reset to Defaults (%s)\r\n", gnss->getRtcmDefaultString()); + systemPrintf("3) Reset to Low Bandwidth Link (%s)\r\n", gnss->getRtcmLowDataRateString()); systemPrintln("x) Exit"); @@ -162,21 +162,21 @@ void menuMessagesBaseRTCM() if (incoming == 1) { - gnssMenuMessageBaseRtcm(); + gnss->menuMessageBaseRtcm(); restartBase = true; } else if (incoming == 2) { - gnssBaseRtcmDefault(); + gnss->baseRtcmDefault(); - systemPrintf("Reset to Defaults (%s)\r\n", gnssGetRtcmDefaultString()); + systemPrintf("Reset to Defaults (%s)\r\n", gnss->getRtcmDefaultString()); restartBase = true; } else if (incoming == 3) { - gnssBaseRtcmLowDataRate(); + gnss->baseRtcmLowDataRate(); - systemPrintf("Reset to Low Bandwidth Link (%s)\r\n", gnssGetRtcmLowDataRateString()); + systemPrintf("Reset to Low Bandwidth Link (%s)\r\n", gnss->getRtcmLowDataRateString()); restartBase = true; } else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) @@ -756,14 +756,14 @@ void setLoggingType() { loggingType = LOGGING_CUSTOM; - int messageCount = gnssGetActiveMessageCount(); + int messageCount = gnss->getActiveMessageCount(); if (messageCount == 5 || messageCount == 7) { - if (checkGnssNMEARates()) + if (gnss->checkNMEARates()) { loggingType = LOGGING_STANDARD; - if (checkGnssPPPRates()) + if (gnss->checkPPPRates()) loggingType = LOGGING_PPP; } } @@ -773,7 +773,7 @@ void setLoggingType() void setLogTestFrequencyMessages(int rate, int messages) { // Set measurement frequency - gnssSetRate(1.0 / (double)rate); // Convert Hz to seconds. This will set settings.measurementRateMs, + gnss->setRate(1.0 / (double)rate); // Convert Hz to seconds. This will set settings.measurementRateMs, // settings.navigationRate, and GSV message // Set messages @@ -804,8 +804,8 @@ void setLogTestFrequencyMessages(int rate, int messages) log_d("Unknown message amount"); // Apply these message rates to both UART1 / SPI and USB - gnssSetMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set - gnssSetMessagesUsb(MAX_SET_MESSAGES_RETRIES); + gnss->setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set + gnss->setMessagesUsb(MAX_SET_MESSAGES_RETRIES); } // The log test allows us to record a series of different system configurations into diff --git a/Firmware/RTK_Everywhere/menuPP.ino b/Firmware/RTK_Everywhere/menuPP.ino index cd803fec7..b2fe68143 100644 --- a/Firmware/RTK_Everywhere/menuPP.ino +++ b/Firmware/RTK_Everywhere/menuPP.ino @@ -581,7 +581,7 @@ long long thingstreamEpochToGPSEpoch(long long startEpoch) epoch /= 1000; // Convert PointPerfect ms Epoch to s // Convert Unix Epoch time from PointPerfect to GPS Time Of Week needed for UBX message - long long gpsEpoch = epoch - 315964800 + gnssGetLeapSeconds(); // Shift to GPS Epoch. + long long gpsEpoch = epoch - 315964800 + gnss->getLeapSeconds(); // Shift to GPS Epoch. return (gpsEpoch); } @@ -652,7 +652,7 @@ void dateToKeyStart(uint8_t expDay, uint8_t expMonth, uint16_t expYear, uint64_t long long startUnixEpoch = expireUnixEpoch - (27 * 24 * 60 * 60); // Move back 27 days // Additionally, Thingstream seems to be reporting Epochs that do not have leap seconds - startUnixEpoch -= gnssGetLeapSeconds(); // Modify our Epoch to match Point Perfect + startUnixEpoch -= gnss->getLeapSeconds(); // Modify our Epoch to match Point Perfect // PointPerfect uses/reports unix epochs in milliseconds *settingsKeyStart = startUnixEpoch * 1000L; // Convert to ms @@ -732,8 +732,8 @@ void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData) if (settings.debugCorrections == true && !inMainMenu) systemPrintf("Pushing %d bytes of RXM-PMP data to GNSS\r\n", payloadLen); - gnssPushRawData(&pmpData->sync1, (size_t)payloadLen + 6); // Push the sync chars, class, ID, length and payload - gnssPushRawData(&pmpData->checksumA, (size_t)2); // Push the checksum bytes + gnss->pushRawData(&pmpData->sync1, (size_t)payloadLen + 6); // Push the sync chars, class, ID, length and payload + gnss->pushRawData(&pmpData->checksumA, (size_t)2); // Push the checksum bytes } else { @@ -800,12 +800,12 @@ void beginLBand() printNEOInfo(); // Print module firmware version } - gnssUpdate(); + gnss->update(); uint32_t LBandFreq; - uint8_t fixType = gnssGetFixType(); - double latitude = gnssGetLatitude(); - double longitude = gnssGetLongitude(); + uint8_t fixType = gnss->getFixType(); + double latitude = gnss->getLatitude(); + double longitude = gnss->getLongitude(); // If we have a fix, check which frequency to use if (fixType >= 2 && fixType <= 5) // 2D, 3D, 3D+DR, or Time { @@ -864,7 +864,7 @@ void beginLBand() if (settings.debugCorrections == true) systemPrintln("L-Band online"); - gnssApplyPointPerfectKeys(); // Apply keys now, if we have them. This sets online.lbandCorrections + gnss->applyPointPerfectKeys(); // Apply keys now, if we have them. This sets online.lbandCorrections online.lband_neo = true; } @@ -873,9 +873,9 @@ void beginLBand() if (present.gnss_mosaicX5 && settings.enablePointPerfectCorrections) { uint32_t LBandFreq; - uint8_t fixType = gnssGetFixType(); - double latitude = gnssGetLatitude(); - double longitude = gnssGetLongitude(); + uint8_t fixType = gnss->getFixType(); + double latitude = gnss->getLatitude(); + double longitude = gnss->getLongitude(); // If we have a fix, check which frequency to use if (fixType >= 1) // Stand-Alone PVT or better { @@ -1122,7 +1122,7 @@ void menuPointPerfect() if (strlen(settings.pointPerfectClientID) > 0) { - gnssApplyPointPerfectKeys(); + gnss->applyPointPerfectKeys(); } clearBuffer(); // Empty buffer of any newline chars @@ -1158,7 +1158,7 @@ void updateLBand() lbandCorrectionsReceived = false; // If we don't get an L-Band fix within Timeout, hot-start ZED-F9x - if (gnssIsRTKFloat()) + if (gnss->isRTKFloat()) { if (lbandTimeFloatStarted == 0) lbandTimeFloatStarted = millis(); @@ -1182,15 +1182,15 @@ void updateLBand() lbandTimeFloatStarted = millis(); // Restart timer for L-Band. Don't immediately reset ZED to achieve fix. - // Hotstart ZED to try to get RTK lock - theGNSS->softwareResetGNSSOnly(); + // Hotstart GNSS to try to get RTK lock + gnss->softwareReset(); if (settings.debugCorrections == true) systemPrintf("Restarting ZED. Number of Float lock restarts: %d\r\n", floatLockRestarts); } } } - else if (gnssIsRTKFix() && rtkTimeToFixMs == 0) + else if (gnss->isRTKFix() && rtkTimeToFixMs == 0) { lbandTimeFloatStarted = 0; // Restart timer in case we drop from RTK Fix @@ -1494,9 +1494,9 @@ void updateProvisioning() paintLBandConfigure(); // Be sure we ignore any external RTCM sources - gnssDisableRtcmOnGnss(); + gnss->rtcmOnGnssDisable(); - gnssApplyPointPerfectKeys(); // Send current keys, if available, to GNSS + gnss->applyPointPerfectKeys(); // Send current keys, if available, to GNSS settings.requestKeyUpdate = false; // However we got here, clear requestKeyUpdate recordSystemSettings(); // Record these settings to unit diff --git a/Firmware/RTK_Everywhere/menuPorts.ino b/Firmware/RTK_Everywhere/menuPorts.ino index 59cf46671..79377c0c5 100644 --- a/Firmware/RTK_Everywhere/menuPorts.ino +++ b/Firmware/RTK_Everywhere/menuPorts.ino @@ -57,11 +57,11 @@ void menuPortsNoMux() systemPrintln("Menu: Ports"); systemPrint("1) Set serial baud rate for Radio Port: "); - systemPrint(gnssGetRadioBaudRate()); + systemPrint(gnss->getRadioBaudRate()); systemPrintln(" bps"); systemPrint("2) Set serial baud rate for Data Port: "); - systemPrint(gnssGetDataBaudRate()); + systemPrint(gnss->getDataBaudRate()); systemPrintln(" bps"); systemPrint("3) GNSS UART2 UBX Protocol In: "); @@ -87,7 +87,7 @@ void menuPortsNoMux() { settings.radioPortBaud = newBaud; if (online.gnss == true) - gnssSetRadioBaudRate(newBaud); + gnss->setRadioBaudRate(newBaud); } else { @@ -106,7 +106,7 @@ void menuPortsNoMux() { settings.dataPortBaud = newBaud; if (online.gnss == true) - gnssSetDataBaudRate(newBaud); + gnss->setDataBaudRate(newBaud); } else { @@ -147,7 +147,7 @@ void menuPortsMultiplexed() systemPrintln("Menu: Ports"); systemPrint("1) Set Radio port serial baud rate: "); - systemPrint(gnssGetRadioBaudRate()); + systemPrint(gnss->getRadioBaudRate()); systemPrintln(" bps"); systemPrint("2) Set Data port connections: "); @@ -163,7 +163,7 @@ void menuPortsMultiplexed() if (settings.dataPortChannel == MUX_GNSS_UART) { systemPrint("3) Set Data port serial baud rate: "); - systemPrint(gnssGetDataBaudRate()); + systemPrint(gnss->getDataBaudRate()); systemPrintln(" bps"); } else if (settings.dataPortChannel == MUX_PPS_EVENTTRIGGER) @@ -199,7 +199,7 @@ void menuPortsMultiplexed() { settings.radioPortBaud = newBaud; if (online.gnss == true) - gnssSetRadioBaudRate(newBaud); + gnss->setRadioBaudRate(newBaud); } else { @@ -237,7 +237,7 @@ void menuPortsMultiplexed() { settings.dataPortBaud = newBaud; if (online.gnss == true) - gnssSetDataBaudRate(newBaud); + gnss->setDataBaudRate(newBaud); } else { @@ -430,8 +430,8 @@ void menuPortHardwareTriggers() if (updateSettings) { settings.updateGNSSSettings = true; // Force update - gnssBeginExternalEvent(); // Update with new settings - gnssBeginPPS(); + gnss->beginExternalEvent(); // Update with new settings + gnss->beginPPS(); } } diff --git a/Firmware/RTK_Everywhere/menuSystem.ino b/Firmware/RTK_Everywhere/menuSystem.ino index 2227ffe6b..d09b76615 100644 --- a/Firmware/RTK_Everywhere/menuSystem.ino +++ b/Firmware/RTK_Everywhere/menuSystem.ino @@ -15,9 +15,9 @@ void menuSystem() { systemPrint("Online - "); - gnssPrintModuleInfo(); + gnss->printModuleInfo(); - systemPrintf("Module ID: %s\r\n", gnssGetId()); + systemPrintf("Module ID: %s\r\n", gnss->getId()); printCurrentConditions(); } @@ -493,9 +493,9 @@ void menuDebugHardware() settings.debugGnss ^= 1; if (settings.debugGnss) - gnssEnableDebugging(); + gnss->debuggingEnable(); else - gnssDisableDebugging(); + gnss->debuggingDisable(); } else if (incoming == 10) { @@ -1037,7 +1037,7 @@ void menuOperation() } else if (incoming == 10 && present.gnss_zedf9p) { - bool response = gnssSetMessagesUsb(MAX_SET_MESSAGES_RETRIES); + bool response = gnss->setMessagesUsb(MAX_SET_MESSAGES_RETRIES); if (response == false) systemPrintln(F("Failed to enable USB messages")); @@ -1427,19 +1427,19 @@ void printCurrentConditions() if (online.gnss == true) { systemPrint("SIV: "); - systemPrint(gnssGetSatellitesInView()); + systemPrint(gnss->getSatellitesInView()); - float hpa = gnssGetHorizontalAccuracy(); + float hpa = gnss->getHorizontalAccuracy(); char temp[20]; const char *units = getHpaUnits(hpa, temp, sizeof(temp), 3, true); systemPrintf(", HPA (%s): %s", units, temp); systemPrint(", Lat: "); - systemPrint(gnssGetLatitude(), haeNumberOfDecimals); + systemPrint(gnss->getLatitude(), haeNumberOfDecimals); systemPrint(", Lon: "); - systemPrint(gnssGetLongitude(), haeNumberOfDecimals); + systemPrint(gnss->getLongitude(), haeNumberOfDecimals); systemPrint(", Altitude (m): "); - systemPrint(gnssGetAltitude(), 1); + systemPrint(gnss->getAltitude(), 1); systemPrintln(); } @@ -1451,11 +1451,11 @@ void printCurrentConditionsNMEA() { char systemStatus[100]; snprintf(systemStatus, sizeof(systemStatus), - "%02d%02d%02d.%02d,%02d%02d%02d,%0.3f,%d,%0.9f,%0.9f,%0.2f,%d,%d,%d", gnssGetHour(), gnssGetMinute(), - gnssGetSecond(), gnssGetMillisecond(), gnssGetDay(), gnssGetMonth(), - gnssGetYear() % 2000, // Limit to 2 digits - gnssGetHorizontalAccuracy(), gnssGetSatellitesInView(), gnssGetLatitude(), gnssGetLongitude(), - gnssGetAltitude(), gnssGetFixType(), gnssGetCarrierSolution(), batteryLevelPercent); + "%02d%02d%02d.%02d,%02d%02d%02d,%0.3f,%d,%0.9f,%0.9f,%0.2f,%d,%d,%d", gnss->getHour(), gnss->getMinute(), + gnss->getSecond(), gnss->getMillisecond(), gnss->getDay(), gnss->getMonth(), + gnss->getYear() % 2000, // Limit to 2 digits + gnss->getHorizontalAccuracy(), gnss->getSatellitesInView(), gnss->getLatitude(), gnss->getLongitude(), + gnss->getAltitude(), gnss->getFixType(), gnss->getCarrierSolution(), batteryLevelPercent); char nmeaMessage[100]; // Max NMEA sentence length is 82 createNMEASentence(CUSTOM_NMEA_TYPE_STATUS, nmeaMessage, sizeof(nmeaMessage), diff --git a/Firmware/RTK_Everywhere/mosaic.ino b/Firmware/RTK_Everywhere/mosaic.ino index 663bee97d..9c2a1ac65 100644 --- a/Firmware/RTK_Everywhere/mosaic.ino +++ b/Firmware/RTK_Everywhere/mosaic.ino @@ -442,8 +442,8 @@ bool mosaicX5Begin() } // Set COM2 (Radio) and COM3 (Data) baud rates - gnssSetRadioBaudRate(settings.radioPortBaud); - gnssSetDataBaudRate(settings.dataPortBaud); + gnss->setRadioBaudRate(settings.radioPortBaud); + gnss->setDataBaudRate(settings.dataPortBaud); mosaicX5UpdateSD(); // Check card size and free space @@ -743,7 +743,7 @@ bool mosaicX5AutoBaseStart() mosaicDeterminingFixedPosition = true; // Ensure flag is set initially - autoBaseStartTimer = millis(); // Stamp when averaging began + gnss->_autoBaseStartTimer = millis(); // Stamp when averaging began if (response == false) { @@ -1403,7 +1403,7 @@ void mosaicX5MenuMessages() systemPrintln(); systemPrintln("Menu: GNSS Messages"); - systemPrintf("Active messages: %d\r\n", gnssGetActiveMessageCount()); + systemPrintf("Active messages: %d\r\n", gnss->getActiveMessageCount()); systemPrintln("1) Set NMEA Messages"); systemPrintln("2) Set Rover RTCM Messages"); @@ -1733,7 +1733,7 @@ void mosaicX5MenuConstellations() } // Apply current settings to module - gnssSetConstellations(); + gnss->setConstellations(); clearBuffer(); // Empty buffer of any newline chars } diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 8002f4cda..25c9a97aa 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -1,6 +1,7 @@ #ifndef __SETTINGS_H__ #define __SETTINGS_H__ +#include "GNSS.h" #include "UM980.h" //Structs of UM980 messages, needed for settings.h #include "mosaic.h" //Structs of mosaic messages, needed for settings.h #include diff --git a/Firmware/RTK_Everywhere/support.ino b/Firmware/RTK_Everywhere/support.ino index 3c7e82a85..12a06cb07 100644 --- a/Firmware/RTK_Everywhere/support.ino +++ b/Firmware/RTK_Everywhere/support.ino @@ -275,7 +275,7 @@ InputResponse getUserInputString(char *userString, uint16_t stringSize, bool loc //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Keep doing these important things while waiting for the user to enter data - gnssUpdate(); // Regularly poll to get latest data + gnss->update(); // Regularly poll to get latest data // Keep processing NTP requests if (online.ethernetNTPServer) From 2184fbf6067ddbce63560ad6e18d816363e95b06 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 29 Sep 2024 07:50:11 -1000 Subject: [PATCH 2/5] Implement the ZED class --- Firmware/RTK_Everywhere/Begin.ino | 3 + Firmware/RTK_Everywhere/Form.ino | 3 +- Firmware/RTK_Everywhere/MQTT_Client.ino | 3 +- Firmware/RTK_Everywhere/NVM.ino | 6 +- .../RTK_Everywhere/PointPerfectLibrary.ino | 3 +- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 58 +- Firmware/RTK_Everywhere/ZED.h | 373 ++ Firmware/RTK_Everywhere/ZED.ino | 3258 ++++++++++------- Firmware/RTK_Everywhere/menuCommands.ino | 12 +- Firmware/RTK_Everywhere/menuMessages.ino | 35 +- Firmware/RTK_Everywhere/menuPP.ino | 6 +- 11 files changed, 2329 insertions(+), 1431 deletions(-) create mode 100644 Firmware/RTK_Everywhere/ZED.h diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 35388729b..7ae0b5ccd 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -283,6 +283,9 @@ void beginBoard() else if (productVariant == RTK_EVK) { + // Specify the GNSS radio + gnss = (GNSS *) new ZED(); + // Pin defs etc. for EVK v1.1 present.psram_4mb = true; present.gnss_zedf9p = true; diff --git a/Firmware/RTK_Everywhere/Form.ino b/Firmware/RTK_Everywhere/Form.ino index 8dd84974c..a8829f9bd 100644 --- a/Firmware/RTK_Everywhere/Form.ino +++ b/Firmware/RTK_Everywhere/Form.ino @@ -1108,7 +1108,8 @@ void createMessageListBase(String &returnText) if (present.gnss_zedf9p) { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int messageNumber = 0; messageNumber < MAX_UBX_MSG_RTCM; messageNumber++) { diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino index 3f6404f70..9ebe46753 100644 --- a/Firmware/RTK_Everywhere/MQTT_Client.ino +++ b/Firmware/RTK_Everywhere/MQTT_Client.ino @@ -497,7 +497,8 @@ void mqttClientReceiveMessage(int messageSize) !inMainMenu) systemPrintf("Pushing %d bytes from %s topic to GNSS\r\n", mqttCount, topic); - updateZEDCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed + ZED * zed = (ZED *)gnss; + zed->updateCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed gnss->pushRawData(mqttData, mqttCount); bytesPushed += mqttCount; diff --git a/Firmware/RTK_Everywhere/NVM.ino b/Firmware/RTK_Everywhere/NVM.ino index 00ac54b5f..fd13adb2a 100644 --- a/Firmware/RTK_Everywhere/NVM.ino +++ b/Firmware/RTK_Everywhere/NVM.ino @@ -369,7 +369,8 @@ void recordSystemSettingsToFile(File *settingsFile) case tUbMsgRtb: { // Record message settings - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { @@ -1124,7 +1125,8 @@ bool parseLine(char *str) } break; case tUbMsgRtb: { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) { diff --git a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino index 45dbffd60..e98642765 100644 --- a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino +++ b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino @@ -42,7 +42,8 @@ void updatePplTask(void *e) // Note: this is almost certainly redundant. It would only be used if we // believe the PPL can do a better job generating corrections than the // ZED can internally using SPARTN direct. - updateZEDCorrectionsSource(1); + ZED * zed = (ZED *)gnss; + zed->updateCorrectionsSource(1); gnss->pushRawData(pplRtcmBuffer, rtcmLength); diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 121ecfbdd..96986f372 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -338,68 +338,12 @@ int wifiOriginalMaxConnectionAttempts = wifiMaxConnectionAttempts; // Modified d // GNSS configuration - ZED-F9x //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 +#include "ZED.h" GNSS * gnss; char neoFirmwareVersion[20]; // Output to system status menu. -// Use Michael's lock/unlock methods to prevent the GNSS UART task from calling checkUblox during a sendCommand and -// waitForResponse. Also prevents pushRawData from being called. -class SFE_UBLOX_GNSS_SUPER_DERIVED : public SFE_UBLOX_GNSS_SUPER -{ - public: - // SemaphoreHandle_t gnssSemaphore = nullptr; - - // Revert to a simple bool lock. The Mutex was causing occasional panics caused by - // vTaskPriorityDisinheritAfterTimeout in lock() (I think possibly / probably caused by the GNSS not being pinned to - // one core? - bool iAmLocked = false; - - bool createLock(void) - { - // if (gnssSemaphore == nullptr) - // gnssSemaphore = xSemaphoreCreateMutex(); - // return gnssSemaphore; - - return true; - } - bool lock(void) - { - // return (xSemaphoreTake(gnssSemaphore, 2100) == pdPASS); - - if (!iAmLocked) - { - iAmLocked = true; - return true; - } - - unsigned long startTime = millis(); - while (((millis() - startTime) < 2100) && (iAmLocked)) - delay(1); // Yield - - if (!iAmLocked) - { - iAmLocked = true; - return true; - } - - return false; - } - void unlock(void) - { - // xSemaphoreGive(gnssSemaphore); - - iAmLocked = false; - } - void deleteLock(void) - { - // vSemaphoreDelete(gnssSemaphore); - // gnssSemaphore = nullptr; - } -}; - -SFE_UBLOX_GNSS_SUPER_DERIVED *theGNSS = nullptr; // Don't instantiate until we know what gnssPlatform we're on - #ifdef COMPILE_L_BAND static SFE_UBLOX_GNSS_SUPER i2cLBand; // NEO-D9S diff --git a/Firmware/RTK_Everywhere/ZED.h b/Firmware/RTK_Everywhere/ZED.h new file mode 100644 index 000000000..85d77c124 --- /dev/null +++ b/Firmware/RTK_Everywhere/ZED.h @@ -0,0 +1,373 @@ +/*------------------------------------------------------------------------------ +ZED.h + + Declarations and definitions for the ZED implementation +------------------------------------------------------------------------------*/ + +#ifndef __ZED_H__ +#define __ZED_H__ + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 + +class ZED : GNSS +{ + private: + + // Use Michael's lock/unlock methods to prevent the GNSS UART task from + // calling checkUblox during a sendCommand and waitForResponse. + // Also prevents pushRawData from being called. + // + // Revert to a simple bool lock. The Mutex was causing occasional panics caused by + // vTaskPriorityDisinheritAfterTimeout in lock() (I think possibly / probably caused by the GNSS not being pinned to + // one core? + bool iAmLocked = false; + + SFE_UBLOX_GNSS_SUPER * _zed = nullptr; // Don't instantiate until we know what gnssPlatform we're on + + public: + + // If we have decryption keys, configure module + // Note: don't check online.lband_neo here. We could be using ip corrections + void applyPointPerfectKeys(); + + // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) + void baseRtcmDefault(); + + // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) + virtual void baseRtcmLowDataRate(); + + // Connect to GNSS and identify particulars + void begin(); + + // Setup TM2 time stamp input as need + // Outputs: + // Returns true when an external event occurs and false if no event + bool beginExternalEvent(); + + // Setup the timepulse output on the PPS pin for external triggering + // Outputs + // Returns true if the pin was successfully setup and false upon + // failure + bool beginPPS(); + + bool checkNMEARates(); + + bool checkPPPRates(); + + // Configure the Base + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureBase(); + + // Configure specific aspects of the receiver for NTP mode + bool configureNtpMode(); + + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureRadio(); + + // Configure the Rover + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureRover(); + + void debuggingDisable(); + + void debuggingEnable(); + + void enableGgaForNtrip(); + + // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted + // even if there is no GPS fix. We use it to test serial output. + // Outputs: + // Returns true if successfully started and false upon failure + bool enableRTCMTest(); + + // Restore the GNSS to the factory settings + void factoryReset(); + + uint16_t fileBufferAvailable(); + + uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); + + // Start the base using fixed coordinates + // Outputs: + // Returns true if successfully started and false upon failure + bool fixedBaseStart(); + + // Return the number of active/enabled messages + uint8_t getActiveMessageCount(); + + // Get the altitude + // Outputs: + // Returns the altitude in meters or zero if the GNSS is offline + double getAltitude(); + + // Returns the carrier solution or zero if not online + uint8_t getCarrierSolution(); + + virtual uint32_t getDataBaudRate(); + + // Returns the day number or zero if not online + uint8_t getDay(); + + // Return the number of milliseconds since GNSS data was last updated + uint16_t getFixAgeMilliseconds(); + + // Returns the fix type or zero if not online + uint8_t getFixType(); + + // Returns the hours of 24 hour clock or zero if not online + uint8_t getHour(); + + // Get the horizontal position accuracy + // Outputs: + // Returns the horizontal position accuracy or zero if offline + float getHorizontalAccuracy(); + + const char * getId(); + + // Get the latitude value + // Outputs: + // Returns the latitude value or zero if not online + double getLatitude(); + + // Query GNSS for current leap seconds + uint8_t getLeapSeconds(); + + // Get the longitude value + // Outputs: + // Returns the longitude value or zero if not online + double getLongitude(); + + // Given the name of a message, return the array number + uint8_t getMessageNumberByName(const char *msgName); + + // Given the name of a message, find it, and return the rate + uint8_t getMessageRateByName(const char *msgName); + + // Returns two digits of milliseconds or zero if not online + uint8_t getMillisecond(); + + // Returns minutes or zero if not online + uint8_t getMinute(); + + // Returns month number or zero if not online + uint8_t getMonth(); + + // Returns nanoseconds or zero if not online + uint32_t getNanosecond(); + + // Count the number of NAV2 messages with rates more than 0. Used for determining if we need the enable + // the global NAV2 feature. + uint8_t getNAV2MessageCount(); + + virtual uint32_t getRadioBaudRate(); + + // Returns the seconds between solutions + double getRateS(); + + const char * getRtcmDefaultString(); + + const char * getRtcmLowDataRateString(); + + // Returns the number of satellites in view or zero if offline + uint8_t getSatellitesInView(); + + // Returns seconds or zero if not online + uint8_t getSecond(); + + // Get the survey-in mean accuracy + // Outputs: + // Returns the mean accuracy or zero (0) + float getSurveyInMeanAccuracy(); + + // Return the number of seconds the survey-in process has been running + int getSurveyInObservationTime(); + + // Returns timing accuracy or zero if not online + uint32_t getTimeAccuracy(); + + // Returns full year, ie 2023, not 23. + uint16_t getYear(); + + bool isBlocking(); + + // Date is confirmed once we have GNSS fix + bool isConfirmedDate(); + + // Date is confirmed once we have GNSS fix + bool isConfirmedTime(); + + // Return true if GNSS receiver has a higher quality DGPS fix than 3D + bool isDgpsFixed(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have a valid fix, not what type of fix + // This function checks to see if the given platform has reached + // sufficient fix type to be considered valid + bool isFixed(); + + // Used in tpISR() for time pulse synchronization + bool isFullyResolved(); + + bool isPppConverged(); + + bool isPppConverging(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Fix. This function checks to see if the + // given platform has reached sufficient fix type to be considered valid + bool isRTKFix(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Float. This function checks to see if + // the given platform has reached sufficient fix type to be considered + // valid + bool isRTKFloat(); + + // Determine if the survey-in operation is complete + // Outputs: + // Returns true if the survey-in operation is complete and false + // if the operation is still running + bool isSurveyInComplete(); + + // Date will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidDate(); + + // Time will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidTime(); + + // Disable data output from the NEO + bool lBandCommunicationDisable(); + + // Enable data output from the NEO + bool lBandCommunicationEnable(); + + bool lock(void); + + bool lockCreate(void); + + void lockDelete(void); + + // Controls the constellations that are used to generate a fix and logged + void menuConstellations(); + + void menuMessageBaseRtcm(); + + // Control the messages that get broadcast over Bluetooth and logged (if enabled) + void menuMessages(); + + // Print the module type and firmware version + void printModuleInfo(); + + // Send correction data to the GNSS + // Inputs: + // dataToSend: Address of a buffer containing the data + // dataLength: The number of valid data bytes in the buffer + // Outputs: + // Returns the number of correction data bytes written + int pushRawData(uint8_t *dataToSend, int dataLength); + + uint16_t rtcmBufferAvailable(); + + // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver + void rtcmOnGnssDisable(); + + // If LBand is being used, ignore any RTCM that may come in from the GNSS + void rtcmOnGnssEnable(); + + uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); + + // Save the current configuration + // Outputs: + // Returns true when the configuration was saved and false upon failure + bool saveConfiguration(); + + // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS + // This just sets the GNSS side + // Used during Bluetooth testing + // Inputs: + // baudRate: The desired baudrate + bool setBaudrate(uint32_t baudRate); + + // Enable all the valid constellations and bands for this platform + bool setConstellations(); + + bool setDataBaudRate(uint32_t baud); + + // Set the elevation in degrees + // Inputs: + // elevationDegrees: The elevation value in degrees + bool setElevation(uint8_t elevationDegrees); + + // Given a unique string, find first and last records containing that string in message array + void setMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock); + + // Given the name of a message, find it, and set the rate + bool setMessageRateByName(const char *msgName, uint8_t msgRate); + + // Enable all the valid messages for this platform + bool setMessages(int maxRetries); + + // Enable all the valid messages for this platform over the USB port + bool setMessagesUsb(int maxRetries); + + // Set the minimum satellite signal level for navigation. + bool setMinCnoRadio (uint8_t cnoValue); + + // Set the dynamic model to use for RTK + // Inputs: + // modelNumber: Number of the model to use, provided by radio library + bool setModel(uint8_t modelNumber); + + bool setRadioBaudRate(uint32_t baud); + + // Specify the interval between solutions + // Inputs: + // secondsBetweenSolutions: Number of seconds between solutions + // Outputs: + // Returns true if the rate was successfully set and false upon + // failure + bool setRate(double secondsBetweenSolutions); + + bool setTalkerGNGGA(); + + // Hotstart GNSS to try to get RTK lock + bool softwareReset(); + + bool standby(); + + // Callback to save the high precision data + // Inputs: + // ubxDataStruct: Address of an UBX_NAV_HPPOSLLH_data_t structure + // containing the high precision position data + void storeHPdataRadio(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct); + + // Callback to save the PVT data + void storePVTdataRadio(UBX_NAV_PVT_data_t *ubxDataStruct); + + // Reset the survey-in operation + // Outputs: + // Returns true if the survey-in operation was reset successfully + // and false upon failure + bool surveyInReset(); + + // Start the survey-in operation + // Outputs: + // Return true if successful and false upon failure + bool surveyInStart(); + + int ubxConstellationIDToIndex(int id); + + void unlock(void); + + // Poll routine to update the GNSS state + void update(); + + void updateCorrectionsSource(uint8_t source); +}; + +#endif // __ZED_H__ diff --git a/Firmware/RTK_Everywhere/ZED.ino b/Firmware/RTK_Everywhere/ZED.ino index 9c5d0306e..423e112c5 100644 --- a/Firmware/RTK_Everywhere/ZED.ino +++ b/Firmware/RTK_Everywhere/ZED.ino @@ -1,159 +1,167 @@ -// These globals are updated regularly via the storePVTdata callback -double zedLatitude; -double zedLongitude; -float zedAltitude; -float zedHorizontalAccuracy; - -uint8_t zedDay; -uint8_t zedMonth; -uint16_t zedYear; -uint8_t zedHour; -uint8_t zedMinute; -uint8_t zedSecond; -int32_t zedNanosecond; -uint16_t zedMillisecond; // Limited to first two digits - -uint8_t zedSatellitesInView; -uint8_t zedFixType; -uint8_t zedCarrierSolution; - -bool zedValidDate; -bool zedValidTime; -bool zedConfirmedDate; -bool zedConfirmedTime; -bool zedFullyResolved; -uint32_t zedTAcc; - -unsigned long zedPvtArrivalMillis = 0; -bool pvtUpdated = false; - -// Below are the callbacks specific to the ZED-F9x -// Once called, they update global variables that are then accessed via zedGetSatellitesInView() and the likes -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -// These are the callbacks that get regularly called, globals are updated -void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct) -{ - zedAltitude = ubxDataStruct->height / 1000.0; +/*------------------------------------------------------------------------------ +ZED.ino - zedDay = ubxDataStruct->day; - zedMonth = ubxDataStruct->month; - zedYear = ubxDataStruct->year; + Implementation of the ZED class +------------------------------------------------------------------------------*/ - zedHour = ubxDataStruct->hour; - zedMinute = ubxDataStruct->min; - zedSecond = ubxDataStruct->sec; - zedNanosecond = ubxDataStruct->nano; - zedMillisecond = ceil((ubxDataStruct->iTOW % 1000) / 10.0); // Limit to first two digits - - zedSatellitesInView = ubxDataStruct->numSV; - zedFixType = ubxDataStruct->fixType; // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead - // reckoning combined, 5 = time only fix - zedCarrierSolution = ubxDataStruct->flags.bits.carrSoln; +//---------------------------------------- +// If we have decryption keys, configure module +// Note: don't check online.lband_neo here. We could be using ip corrections +//---------------------------------------- +void ZED::applyPointPerfectKeys() +{ + if (online.gnss == false) + { + if (settings.debugCorrections) + systemPrintln("ZED-F9P not available"); + return; + } - zedValidDate = ubxDataStruct->valid.bits.validDate; - zedValidTime = ubxDataStruct->valid.bits.validTime; - zedConfirmedDate = ubxDataStruct->flags2.bits.confirmedDate; - zedConfirmedTime = ubxDataStruct->flags2.bits.confirmedTime; - zedFullyResolved = ubxDataStruct->valid.bits.fullyResolved; - zedTAcc = ubxDataStruct->tAcc; // Nanoseconds + // NEO-D9S encrypted PMP messages are only supported on ZED-F9P firmware v1.30 and above + if (gnssFirmwareVersionInt < 130) + { + systemPrintln("Error: PointPerfect corrections currently supported by ZED-F9P firmware v1.30 and above. " + "Please upgrade your ZED firmware: " + "https://learn.sparkfun.com/tutorials/how-to-upgrade-firmware-of-a-u-blox-gnss-receiver"); + return; + } - zedPvtArrivalMillis = millis(); - pvtUpdated = true; -} + if (strlen(settings.pointPerfectNextKey) > 0) + { + const uint8_t currentKeyLengthBytes = 16; + const uint8_t nextKeyLengthBytes = 16; -void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) -{ - zedHorizontalAccuracy = ((float)ubxDataStruct->hAcc) / 10000.0; // Convert hAcc from mm*0.1 to m + uint16_t currentKeyGPSWeek; + uint32_t currentKeyGPSToW; + long long epoch = thingstreamEpochToGPSEpoch(settings.pointPerfectCurrentKeyStart); + epochToWeekToW(epoch, ¤tKeyGPSWeek, ¤tKeyGPSToW); - zedLatitude = ((double)ubxDataStruct->lat) / 10000000.0; - zedLatitude += ((double)ubxDataStruct->latHp) / 1000000000.0; - zedLongitude = ((double)ubxDataStruct->lon) / 10000000.0; - zedLongitude += ((double)ubxDataStruct->lonHp) / 1000000000.0; -} + uint16_t nextKeyGPSWeek; + uint32_t nextKeyGPSToW; + epoch = thingstreamEpochToGPSEpoch(settings.pointPerfectNextKeyStart); + epochToWeekToW(epoch, &nextKeyGPSWeek, &nextKeyGPSToW); -void storeTIMTPdata(UBX_TIM_TP_data_t *ubxDataStruct) -{ - uint32_t tow = ubxDataStruct->week - SFE_UBLOX_JAN_1ST_2020_WEEK; // Calculate the number of weeks since Jan 1st - // 2020 - tow *= SFE_UBLOX_SECS_PER_WEEK; // Convert weeks to seconds - tow += SFE_UBLOX_EPOCH_WEEK_2086; // Add the TOW for Jan 1st 2020 - tow += ubxDataStruct->towMS / 1000; // Add the TOW for the next TP + // If we are on a L-Band-only or L-Band+IP, set the SOURCE to 1 (L-Band) + // Else set the SOURCE to 0 (IP) + // If we are on L-Band+IP and IP corrections start to arrive, the corrections + // priority code will change SOURCE to match + if (strstr(settings.pointPerfectKeyDistributionTopic, "/Lb") != nullptr) + { + updateCorrectionsSource(1); // Set SOURCE to 1 (L-Band) if needed + } + else + { + updateCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed + } - uint32_t us = ubxDataStruct->towMS % 1000; // Extract the milliseconds - us *= 1000; // Convert to microseconds + _zed->setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C - double subMS = ubxDataStruct->towSubMS; // Get towSubMS (ms * 2^-32) - subMS *= pow(2.0, -32.0); // Convert to milliseconds - subMS *= 1000; // Convert to microseconds + _zed->setVal8(UBLOX_CFG_NAVHPG_DGNSSMODE, + 3); // Set the differential mode - ambiguities are fixed whenever possible - us += (uint32_t)subMS; // Add subMS + bool response = _zed->setDynamicSPARTNKeys(currentKeyLengthBytes, currentKeyGPSWeek, currentKeyGPSToW, + settings.pointPerfectCurrentKey, nextKeyLengthBytes, + nextKeyGPSWeek, nextKeyGPSToW, settings.pointPerfectNextKey); - timTpEpoch = tow; - timTpMicros = us; - timTpArrivalMillis = millis(); - timTpUpdated = true; + if (response == false) + systemPrintln("setDynamicSPARTNKeys failed"); + else + { + if (settings.debugCorrections) + systemPrintln("PointPerfect keys applied"); + online.lbandCorrections = true; + } + } + else + { + if (settings.debugCorrections) + systemPrintln("No PointPerfect keys available"); + } } -void storeMONHWdata(UBX_MON_HW_data_t *ubxDataStruct) +//---------------------------------------- +// Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) +//---------------------------------------- +void ZED::baseRtcmDefault() { - aStatus = ubxDataStruct->aStatus; -} + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 1; // 1105 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1074") - firstRTCMRecord] = 1; // 1074 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1077") - firstRTCMRecord] = 0; // 1077 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1084") - firstRTCMRecord] = 1; // 1084 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1087") - firstRTCMRecord] = 0; // 1087 -void storeRTCM1005data(RTCM_1005_data_t *rtcmData1005) -{ - ARPECEFX = rtcmData1005->AntennaReferencePointECEFX; - ARPECEFY = rtcmData1005->AntennaReferencePointECEFY; - ARPECEFZ = rtcmData1005->AntennaReferencePointECEFZ; - ARPECEFH = 0; - newARPAvailable = true; + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1094") - firstRTCMRecord] = 1; // 1094 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1097") - firstRTCMRecord] = 0; // 1097 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1124") - firstRTCMRecord] = 1; // 1124 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1127") - firstRTCMRecord] = 0; // 1127 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1230") - firstRTCMRecord] = 10; // 1230 + + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_4072_0") - firstRTCMRecord] = 0; // 4072_0 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_4072_1") - firstRTCMRecord] = 0; // 4072_1 } -void storeRTCM1006data(RTCM_1006_data_t *rtcmData1006) +//---------------------------------------- +// Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) +//---------------------------------------- +void ZED::baseRtcmLowDataRate() { - ARPECEFX = rtcmData1006->AntennaReferencePointECEFX; - ARPECEFY = rtcmData1006->AntennaReferencePointECEFY; - ARPECEFZ = rtcmData1006->AntennaReferencePointECEFZ; - ARPECEFH = rtcmData1006->AntennaHeight; - newARPAvailable = true; + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 10; // 1105 0.1Hz + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1074") - firstRTCMRecord] = 2; // 1074 0.5Hz + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1077") - firstRTCMRecord] = 0; // 1077 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1084") - firstRTCMRecord] = 2; // 1084 0.5Hz + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1087") - firstRTCMRecord] = 0; // 1087 + + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1094") - firstRTCMRecord] = 2; // 1094 0.5Hz + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1097") - firstRTCMRecord] = 0; // 1097 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1124") - firstRTCMRecord] = 2; // 1124 0.5Hz + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1127") - firstRTCMRecord] = 0; // 1127 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1230") - firstRTCMRecord] = 10; // 1230 0.1Hz + + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_4072_0") - firstRTCMRecord] = 0; // 4072_0 + settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_4072_1") - firstRTCMRecord] = 0; // 4072_1 } -//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -void zedBegin() +//---------------------------------------- +// Connect to GNSS and identify particulars +//---------------------------------------- +void ZED::begin() { // Instantiate the library - if (theGNSS == nullptr) - theGNSS = new SFE_UBLOX_GNSS_SUPER_DERIVED(); + if (_zed == nullptr) + _zed = new SFE_UBLOX_GNSS_SUPER(); // Note: we don't need to skip this for configureViaEthernet because the ZED is on I2C only - not SPI - if (theGNSS->begin(*i2c_0) == false) + if (_zed->begin(*i2c_0) == false) { log_d("GNSS Failed to begin. Trying again."); // Try again with power on delay delay(1000); // Wait for ZED-F9P to power up before it can respond to ACK - if (theGNSS->begin(*i2c_0) == false) + if (_zed->begin(*i2c_0) == false) { - log_d("GNSS offline"); + systemPrintln("GNSS ZED offline"); displayGNSSFail(1000); return; } } // Increase transactions to reduce transfer time - theGNSS->i2cTransactionSize = 128; + _zed->i2cTransactionSize = 128; // Auto-send Valset messages before the buffer is completely full - theGNSS->autoSendCfgValsetAtSpaceRemaining(16); + _zed->autoSendCfgValsetAtSpaceRemaining(16); // Check the firmware version of the ZED-F9P. Based on Example21_ModuleInfo. - if (theGNSS->getModuleInfo(1100) == true) // Try to get the module info + if (_zed->getModuleInfo(1100)) // Try to get the module info { // Reconstruct the firmware version - snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s %d.%02d", theGNSS->getFirmwareType(), - theGNSS->getFirmwareVersionHigh(), theGNSS->getFirmwareVersionLow()); + snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s %d.%02d", _zed->getFirmwareType(), + _zed->getFirmwareVersionHigh(), _zed->getFirmwareVersionLow()); - gnssFirmwareVersionInt = (theGNSS->getFirmwareVersionHigh() * 100) + theGNSS->getFirmwareVersionLow(); + gnssFirmwareVersionInt = (_zed->getFirmwareVersionHigh() * 100) + _zed->getFirmwareVersionLow(); // Check if this is known firmware //"1.20" - Mostly for F9R HPS 1.20, but also F9P HPG v1.20 @@ -177,11 +185,11 @@ void zedBegin() } // Determine if we have a ZED-F9P or an ZED-F9R - if (strstr(theGNSS->getModuleName(), "ZED-F9P") == nullptr) - systemPrintf("Unknown ZED module: %s\r\n", theGNSS->getModuleName()); + if (strstr(_zed->getModuleName(), "ZED-F9P") == nullptr) + systemPrintf("Unknown ZED module: %s\r\n", _zed->getModuleName()); - if (strcmp(theGNSS->getFirmwareType(), "HPG") == 0) - if ((theGNSS->getFirmwareVersionHigh() == 1) && (theGNSS->getFirmwareVersionLow() < 30)) + if (strcmp(_zed->getFirmwareType(), "HPG") == 0) + if ((_zed->getFirmwareVersionHigh() == 1) && (_zed->getFirmwareVersionLow() < 30)) { systemPrintln( "ZED-F9P module is running old firmware which does not support SPARTN. Please upgrade: " @@ -189,8 +197,8 @@ void zedBegin() displayUpdateZEDF9P(3000); } - if (strcmp(theGNSS->getFirmwareType(), "HPS") == 0) - if ((theGNSS->getFirmwareVersionHigh() == 1) && (theGNSS->getFirmwareVersionLow() < 21)) + if (strcmp(_zed->getFirmwareType(), "HPS") == 0) + if ((_zed->getFirmwareVersionHigh() == 1) && (_zed->getFirmwareVersionLow() < 21)) { systemPrintln( "ZED-F9R module is running old firmware which does not support SPARTN. Please upgrade: " @@ -198,13 +206,13 @@ void zedBegin() displayUpdateZEDF9R(3000); } - zedPrintInfo(); // Print module type and firmware version + printModuleInfo(); // Print module type and firmware version } UBX_SEC_UNIQID_data_t chipID; - if (theGNSS->getUniqueChipId(&chipID)) + if (_zed->getUniqueChipId(&chipID)) { - snprintf(gnssUniqueId, sizeof(gnssUniqueId), "%s", theGNSS->getUniqueChipIdStr(&chipID)); + snprintf(gnssUniqueId, sizeof(gnssUniqueId), "%s", _zed->getUniqueChipIdStr(&chipID)); } systemPrintln("GNSS ZED online"); @@ -212,258 +220,125 @@ void zedBegin() online.gnss = true; } -// Setup the u-blox module for any setup (base or rover) -// In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare -// occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. -bool zedConfigure() +//---------------------------------------- +// Setup the timepulse output on the PPS pin for external triggering +// Setup TM2 time stamp input as need +//---------------------------------------- +bool ZED::beginExternalEvent() { if (online.gnss == false) return (false); - bool response = true; - - // Turn on/off debug messages - if (settings.debugGnss) - theGNSS->enableDebugging(Serial, true); // Enable only the critical debug messages over Serial - else - theGNSS->disableDebugging(); - - // Check if the ubxMessageRates or ubxMessageRatesBase need to be defaulted - // Redundant - also done by gnssConfigure - // checkGNSSArrayDefaults(); - - theGNSS->setAutoPVTcallbackPtr(&storePVTdata); // Enable automatic NAV PVT messages with callback to storePVTdata - theGNSS->setAutoHPPOSLLHcallbackPtr( - &storeHPdata); // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata - theGNSS->setRTCM1005InputcallbackPtr( - &storeRTCM1005data); // Configure a callback for RTCM 1005 - parsed from pushRawData - theGNSS->setRTCM1006InputcallbackPtr( - &storeRTCM1006data); // Configure a callback for RTCM 1006 - parsed from pushRawData - - if (present.timePulseInterrupt == true) - theGNSS->setAutoTIMTPcallbackPtr( - &storeTIMTPdata); // Enable automatic TIM TP messages with callback to storeTIMTPdata - - // Configuring the ZED can take more than 2000ms. We save configuration to - // ZED so there is no need to update settings unless user has modified - // the settings file or internal settings. + // If our settings haven't changed, trust ZED's settings if (settings.updateGNSSSettings == false) { - systemPrintln("ZED-F9x configuration maintained"); + log_d("Skipping ZED Trigger configuration"); return (true); } - // Wait for initial report from module - int maxWait = 2000; - startTime = millis(); - while (pvtUpdated == false) - { - gnss->update(); // Regularly poll to get latest data - - delay(10); - if ((millis() - startTime) > maxWait) - { - log_d("PVT Update failed"); - break; - } - } - - // The first thing we do is go to 1Hz to lighten any I2C traffic from a previous configuration - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - - if (commandSupported(UBLOX_CFG_TMODE_MODE) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - - // UART1 will primarily be used to pass NMEA and UBX from ZED to ESP32 (eventually to cell phone) - // but the phone can also provide RTCM data and a user may want to configure the ZED over Bluetooth. - // So let's be sure to enable UBX+NMEA+RTCM on the input - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1OUTPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1OUTPROT_NMEA, 1); - if (commandSupported(UBLOX_CFG_UART1OUTPROT_RTCM3X) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1OUTPROT_RTCM3X, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1INPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1INPROT_NMEA, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1INPROT_RTCM3X, 1); - if (commandSupported(UBLOX_CFG_UART1INPROT_SPARTN) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1INPROT_SPARTN, 0); - - response &= theGNSS->addCfgValset(UBLOX_CFG_UART1_BAUDRATE, - settings.dataPortBaud); // Defaults to 230400 to maximize message output support - response &= theGNSS->addCfgValset( - UBLOX_CFG_UART2_BAUDRATE, - settings.radioPortBaud); // Defaults to 57600 to match SiK telemetry radio firmware default - - // Disable SPI port - This is just to remove some overhead by ZED - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIOUTPROT_UBX, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIOUTPROT_NMEA, 0); - if (commandSupported(UBLOX_CFG_SPIOUTPROT_RTCM3X) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIOUTPROT_RTCM3X, 0); - - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIINPROT_UBX, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIINPROT_NMEA, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIINPROT_RTCM3X, 0); - if (commandSupported(UBLOX_CFG_SPIINPROT_SPARTN) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_SPIINPROT_SPARTN, 0); - - // Set the UART2 to only do RTCM (in case this device goes into base mode) - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2OUTPROT_NMEA, 0); - if (commandSupported(UBLOX_CFG_UART2OUTPROT_RTCM3X) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2OUTPROT_RTCM3X, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2INPROT_UBX, settings.enableUART2UBXIn); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2INPROT_NMEA, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2INPROT_RTCM3X, 1); - if (commandSupported(UBLOX_CFG_UART2INPROT_SPARTN) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_UART2INPROT_SPARTN, 0); - - // We don't want NMEA over I2C, but we will want to deliver RTCM, and UBX+RTCM is not an option - response &= theGNSS->addCfgValset(UBLOX_CFG_I2COUTPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_I2COUTPROT_NMEA, 1); - if (commandSupported(UBLOX_CFG_I2COUTPROT_RTCM3X) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_I2COUTPROT_RTCM3X, 1); + if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) + return (true); // No need to configure PPS if port is not selected - response &= theGNSS->addCfgValset(UBLOX_CFG_I2CINPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_I2CINPROT_NMEA, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_I2CINPROT_RTCM3X, 1); + bool response = true; - if (commandSupported(UBLOX_CFG_I2CINPROT_SPARTN) == true) + if (settings.enableExternalHardwareEventLogging) { - if (present.lband_neo == true) - response &= - theGNSS->addCfgValset(UBLOX_CFG_I2CINPROT_SPARTN, - 1); // We push NEO-D9S correction data (SPARTN) to ZED-F9P over the I2C interface - else - response &= theGNSS->addCfgValset(UBLOX_CFG_I2CINPROT_SPARTN, 0); + _zed->setAutoTIMTM2callbackPtr( + &eventTriggerReceived); // Enable automatic TIM TM2 messages with callback to eventTriggerReceived } + else + _zed->setAutoTIMTM2callbackPtr(nullptr); - // The USB port on the ZED may be used for RTCM to/from the computer (as an NTRIP caster or client) - // So let's be sure all protocols are on for the USB port - response &= theGNSS->addCfgValset(UBLOX_CFG_USBOUTPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_USBOUTPROT_NMEA, 1); - if (commandSupported(UBLOX_CFG_USBOUTPROT_RTCM3X) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_USBOUTPROT_RTCM3X, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_USBINPROT_UBX, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_USBINPROT_NMEA, 1); - response &= theGNSS->addCfgValset(UBLOX_CFG_USBINPROT_RTCM3X, 1); - if (commandSupported(UBLOX_CFG_USBINPROT_SPARTN) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_USBINPROT_SPARTN, 0); - - if (commandSupported(UBLOX_CFG_NAVSPG_INFIL_MINCNO) == true) - { - response &= - theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINCNO, - settings.minCNO); // Set minimum satellite signal level for navigation - default 6 - } + return (response); +} + +//---------------------------------------- +// Setup the timepulse output on the PPS pin for external triggering +//---------------------------------------- +bool ZED::beginPPS() +{ + if (online.gnss == false) + return (false); - if (commandSupported(UBLOX_CFG_NAV2_OUT_ENABLED) == true) + // If our settings haven't changed, trust ZED's settings + if (settings.updateGNSSSettings == false) { - // Count NAV2 messages and enable NAV2 as needed. - if (zedGetNAV2MessageCount() > 0) - { - response &= theGNSS->addCfgValset( - UBLOX_CFG_NAV2_OUT_ENABLED, - 1); // Enable NAV2 messages. This has the side effect of causing RTCM to generate twice as fast. - } - else - response &= theGNSS->addCfgValset(UBLOX_CFG_NAV2_OUT_ENABLED, 0); // Disable NAV2 messages + systemPrintln("Skipping ZED Trigger configuration"); + return (true); } - response &= theGNSS->sendCfgValset(); + if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) + return (true); // No need to configure PPS if port is not selected - if (response == false) - systemPrintln("Module failed config block 0"); - response = true; // Reset + bool response = true; - // Enable the constellations the user has set - response &= zedSetConstellations(true); // 19 messages. Send newCfg or sendCfg with value set - if (response == false) - systemPrintln("Module failed config block 1"); - response = true; // Reset + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) + response &= + _zed->addCfgValset(UBLOX_CFG_TP_USE_LOCKED_TP1, + 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid + response &= _zed->addCfgValset(UBLOX_CFG_TP_TP1_ENA, settings.enableExternalPulse); // Enable/disable timepulse + response &= + _zed->addCfgValset(UBLOX_CFG_TP_POL_TP1, settings.externalPulsePolarity); // 0 = falling, 1 = rising edge - // Make sure the appropriate messages are enabled - response &= zedSetMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set - if (response == false) - systemPrintln("Module failed config block 2"); - response = true; // Reset + // While the module is _locking_ to GNSS time, turn off pulse + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us - // Disable NMEA messages on all but UART1 - response &= theGNSS->newCfgValset(); - - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_I2C, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_I2C, 0); - - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_UART2, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_UART2, 0); - - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_SPI, 0); - response &= theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_SPI, 0); - - response &= theGNSS->sendCfgValset(); + // When the module is _locked_ to GNSS time, make it generate 1Hz (Default is 100ms high, 900ms low) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, + settings.externalPulseTimeBetweenPulse_us); // Set the period between pulses is us + response &= + _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, settings.externalPulseLength_us); // Set the pulse length in us + response &= _zed->sendCfgValset(); if (response == false) - systemPrintln("Module failed config block 3"); - - if (present.antennaShortOpen) - { - theGNSS->newCfgValset(); - - theGNSS->addCfgValset(UBLOX_CFG_HW_ANT_CFG_SHORTDET, 1); // Enable antenna short detection - theGNSS->addCfgValset(UBLOX_CFG_HW_ANT_CFG_OPENDET, 1); // Enable antenna open detection + systemPrintln("beginExternalTriggers config failed"); - if (theGNSS->sendCfgValset()) - { - theGNSS->setAutoMONHWcallbackPtr( - &storeMONHWdata); // Enable automatic MON HW messages with callback to storeMONHWdata - } - else - { - systemPrintln("Failed to configure GNSS antenna detection"); - } - } + return (response); +} - if (response == true) - systemPrintln("ZED-F9x configuration update"); +//---------------------------------------- +bool ZED::checkNMEARates() +{ + if (online.gnss) + return (getMessageRateByName("NMEA_GGA") > 0 && getMessageRateByName("NMEA_GSA") > 0 && + getMessageRateByName("NMEA_GST") > 0 && getMessageRateByName("NMEA_GSV") > 0 && + getMessageRateByName("NMEA_RMC") > 0); + return false; +} - return (response); +//---------------------------------------- +bool ZED::checkPPPRates() +{ + if (online.gnss) + return (getMessageRateByName("RXM_RAWX") > 0 && getMessageRateByName("RXM_SFRBX") > 0); + return false; } -// Configure specific aspects of the receiver for rover mode -bool zedConfigureRover() +//---------------------------------------- +// Configure specific aspects of the receiver for base mode +//---------------------------------------- +bool ZED::configureBase() { if (online.gnss == false) - { - log_d("GNSS not online"); return (false); - } - // If our settings haven't changed, and this is first config since power on, trust GNSS's settings - if (settings.updateGNSSSettings == false && firstPowerOn == true) + // If our settings haven't changed, and this is first config since power on, trust ZED's settings + if (settings.updateGNSSSettings == false && firstPowerOn) { firstPowerOn = false; // Next time user switches modes, new settings will be applied - log_d("Skipping ZED Rover configuration"); + log_d("Skipping ZED Base configuration"); return (true); } firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - gnss->update(); // Regularly poll to get latest data + update(); // Regularly poll to get latest data + + _zed->setNMEAGPGGAcallbackPtr( + nullptr); // Disable GPGGA call back that may have been set during Rover NTRIP Client mode bool success = false; int tryNo = -1; @@ -476,92 +351,91 @@ bool zedConfigureRover() { bool response = true; - // Set output rate - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_MEAS, settings.measurementRateMs); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_NAV, settings.navigationRate); + // In Base mode we force 1Hz + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - // Survey mode is only available on ZED-F9P modules - if (commandSupported(UBLOX_CFG_TMODE_MODE) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + // Since we are at 1Hz, allow GSV NMEA to be reported at whatever the user has chosen + response &= _zed->addCfgValset(ubxMessages[8].msgConfigKey, + settings.ubxMessageRates[8]); // Update rate on module response &= - theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); // Set dynamic model + _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, + 0); // Disable NMEA message that may have been set during Rover NTRIP Client mode - // RTCM is only available on ZED-F9P modules - // - // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages - // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code - // using serialGNSS-> So in Rover mode, we want to disable any RTCM messages on I2C (and USB and UART2). - // - // But, on the Reference Station, the GNSS is interfaced via SPI. It has no access to I2C and UART1. So for that - // product - in Rover mode - we want to leave any RTCM messages enabled on SPI so they can be logged if desired. + // Survey mode is only available on ZED-F9P modules + if (commandSupported(UBLOX_CFG_TMODE_MODE)) + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - // Find first RTCM record in ubxMessage array - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + // Note that using UBX-CFG-TMODE3 to set the receiver mode to Survey In or to Fixed Mode, will set + // automatically the dynamic platform model (CFG-NAVSPG-DYNMODEL) to Stationary. + // response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); //Not needed - // Set RTCM messages to user's settings - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - response &= - theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 - 1 = I2C + // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages + // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code + // using serialGNSS-> In base mode the RTK device should output RTCM over all ports: (Primary) UART2 in case the + // RTK device is connected via radio to rover (Optional) I2C in case user wants base to connect to WiFi and + // NTRIP Caster (Seconday) USB in case the RTK device is used as an NTRIP caster connected to SBC or other + // (Tertiary) UART1 in case RTK device is sending RTCM to a phone that is then NTRIP Caster + + // Find first RTCM record in ubxMessage array + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); + + // ubxMessageRatesBase is an array of ~12 uint8_ts + // ubxMessage is an array of ~80 messages + // We use firstRTCMRecord as an offset for the keys, but use x as the rate - // Set RTCM messages to user's settings for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) { - response &= - theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 1 = UART2 - response &= - theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, - settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 2 = USB - } + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - 1 = I2C + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - response &= theGNSS->addCfgValset(UBLOX_CFG_NMEA_MAINTALKERID, - 3); // Return talker ID to GNGGA after NTRIP Client set to GPGGA + // Disable messages on SPI + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 3, + 0); // UBLOX_CFG UART1 + 3 = SPI + } - response &= theGNSS->addCfgValset(UBLOX_CFG_NMEA_HIGHPREC, 1); // Enable high precision NMEA - response &= theGNSS->addCfgValset(UBLOX_CFG_NMEA_SVNUMBERING, 1); // Enable extended satellite numbering + // Update message rates for UART2 and USB + for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) + { + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 1 = UART2 + response &= _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, + settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 2 = USB + } - response &= theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation + response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation - response &= theGNSS->sendCfgValset(); // Closing + response &= _zed->sendCfgValset(); // Closing value if (response) success = true; } if (!success) - log_d("Rover config failed 1"); - - if (!success) - systemPrintln("Rover config fail"); + systemPrintln("Base config fail"); return (success); } -// Configure specific aspects of the receiver for base mode -bool zedConfigureBase() +//---------------------------------------- +// Configure specific aspects of the receiver for NTP mode +//---------------------------------------- +bool ZED::configureNtpMode() { + bool success = false; + if (online.gnss == false) return (false); - // If our settings haven't changed, and this is first config since power on, trust ZED's settings - if (settings.updateGNSSSettings == false && firstPowerOn == true) - { - firstPowerOn = false; // Next time user switches modes, new settings will be applied - log_d("Skipping ZED Base configuration"); - return (true); - } - - firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - gnss->update(); // Regularly poll to get latest data - theGNSS->setNMEAGPGGAcallbackPtr( - nullptr); // Disable GPGGA call back that may have been set during Rover NTRIP Client mode + // Disable GPGGA call back that may have been set during Rover NTRIP Client mode + _zed->setNMEAGPGGAcallbackPtr(nullptr); - bool success = false; int tryNo = -1; // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS @@ -572,516 +446,785 @@ bool zedConfigureBase() { bool response = true; - // In Base mode we force 1Hz - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - - // Since we are at 1Hz, allow GSV NMEA to be reported at whatever the user has chosen - response &= theGNSS->addCfgValset(ubxMessages[8].msgConfigKey, - settings.ubxMessageRates[8]); // Update rate on module - - response &= - theGNSS->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, - 0); // Disable NMEA message that may have been set during Rover NTRIP Client mode + // In NTP mode we force 1Hz + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); // Survey mode is only available on ZED-F9P modules - if (commandSupported(UBLOX_CFG_TMODE_MODE) == true) - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - // Note that using UBX-CFG-TMODE3 to set the receiver mode to Survey In or to Fixed Mode, will set - // automatically the dynamic platform model (CFG-NAVSPG-DYNMODEL) to Stationary. - // response &= theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); //Not needed + // Set dynamic model to stationary + response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, DYN_MODEL_STATIONARY); // Set dynamic model - // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages - // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code - // using serialGNSS-> In base mode the RTK device should output RTCM over all ports: (Primary) UART2 in case the - // RTK device is connected via radio to rover (Optional) I2C in case user wants base to connect to WiFi and - // NTRIP Caster (Seconday) USB in case the RTK device is used as an NTRIP caster connected to SBC or other - // (Tertiary) UART1 in case RTK device is sending RTCM to a phone that is then NTRIP Caster + // Set time pulse to 1Hz (100:900) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) + response &= _zed->addCfgValset( + UBLOX_CFG_TP_USE_LOCKED_TP1, + 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid + response &= _zed->addCfgValset(UBLOX_CFG_TP_TP1_ENA, 1); // Enable timepulse + response &= _zed->addCfgValset(UBLOX_CFG_TP_POL_TP1, 1); // 1 = rising edge - // Find first RTCM record in ubxMessage array - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + // While the module is _locking_ to GNSS time, turn off pulse + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us - // ubxMessageRatesBase is an array of ~12 uint8_ts - // ubxMessage is an array of ~80 messages - // We use firstRTCMRecord as an offset for the keys, but use x as the rate + // When the module is _locked_ to GNSS time, make it generate 1Hz (100ms high, 900ms low) + response &= _zed->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, 1000000); // Set the period between pulses is us + response &= _zed->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, 100000); // Set the pulse length in us - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - { - response &= theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 - 1 = I2C - response &= theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + // Ensure pulse is aligned to top-of-second. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_ALIGN_TO_TOW_TP1, 1); - // Disable messages on SPI - response &= theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 3, - 0); // UBLOX_CFG UART1 + 3 = SPI - } + // Set the time grid to UTC. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_TIMEGRID_TP1, 0); // 0=UTC; 1=GPS - // Update message rates for UART2 and USB - for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) - { - response &= theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 1 = UART2 - response &= theGNSS->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, - settings.ubxMessageRatesBase[x]); // UBLOX_CFG UART1 + 2 = USB - } + // Sync to GNSS. This is the default. Set it here just to make sure. + response &= _zed->addCfgValset(UBLOX_CFG_TP_SYNC_GNSS_TP1, 1); + + response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation - response &= theGNSS->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation + // Ensure PVT, HPPOSLLH and TP messages are being output at 1Hz on the correct port + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_PVT_I2C, 1); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_NAV_HPPOSLLH_I2C, 1); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_UBX_TIM_TP_I2C, 1); - response &= theGNSS->sendCfgValset(); // Closing value + response &= _zed->sendCfgValset(); // Closing value if (response) success = true; } if (!success) - systemPrintln("Base config fail"); + systemPrintln("NTP config fail"); return (success); } -// Slightly modified method for restarting survey-in from: -// https://portal.u-blox.com/s/question/0D52p00009IsVoMCAV/restarting-surveyin-on-an-f9p -bool zedSurveyInReset() +//---------------------------------------- +// Setup the u-blox module for any setup (base or rover) +// In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare +// occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. +//---------------------------------------- +bool ZED::configureRadio() { + if (online.gnss == false) + return (false); + bool response = true; - // Disable survey-in mode - response &= theGNSS->setVal8(UBLOX_CFG_TMODE_MODE, 0); - delay(1000); + // Turn on/off debug messages + if (settings.debugGnss) + _zed->enableDebugging(Serial, true); // Enable only the critical debug messages over Serial + else + _zed->disableDebugging(); - // Enable Survey in with bogus values - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 1); // Survey-in enable - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, 40 * 10000); // 40.0m - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_SVIN_MIN_DUR, 1000); // 1000s - response &= theGNSS->sendCfgValset(); - delay(1000); + // Check if the ubxMessageRates or ubxMessageRatesBase need to be defaulted + // Redundant - also done by gnssConfigure + // checkGNSSArrayDefaults(); - // Disable survey-in mode - response &= theGNSS->setVal8(UBLOX_CFG_TMODE_MODE, 0); + _zed->setAutoPVTcallbackPtr(&storePVTdata); // Enable automatic NAV PVT messages with callback to storePVTdata + _zed->setAutoHPPOSLLHcallbackPtr( + &storeHPdata); // Enable automatic NAV HPPOSLLH messages with callback to storeHPdata + _zed->setRTCM1005InputcallbackPtr( + &storeRTCM1005data); // Configure a callback for RTCM 1005 - parsed from pushRawData + _zed->setRTCM1006InputcallbackPtr( + &storeRTCM1006data); // Configure a callback for RTCM 1006 - parsed from pushRawData - if (response == false) - return (response); + if (present.timePulseInterrupt) + _zed->setAutoTIMTPcallbackPtr( + &storeTIMTPdata); // Enable automatic TIM TP messages with callback to storeTIMTPdata - // Wait until active and valid becomes false - long maxTime = 5000; - long startTime = millis(); - while (theGNSS->getSurveyInActive(100) == true || theGNSS->getSurveyInValid(100) == true) + // Configuring the ZED can take more than 2000ms. We save configuration to + // ZED so there is no need to update settings unless user has modified + // the settings file or internal settings. + if (settings.updateGNSSSettings == false) { - delay(100); - if (millis() - startTime > maxTime) - return (false); // Reset of survey failed + systemPrintln("ZED-F9x configuration maintained"); + return (true); } - return (true); -} - -// Start survey -// The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. -bool zedSurveyInStart() -{ - theGNSS->setVal8(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - delay(100); - - bool needSurveyReset = false; - if (theGNSS->getSurveyInActive(100) == true) - needSurveyReset = true; - if (theGNSS->getSurveyInValid(100) == true) - needSurveyReset = true; - - if (needSurveyReset == true) + // Wait for initial report from module + int maxWait = 2000; + startTime = millis(); + while (_pvtUpdated == false) { - systemPrintln("Resetting survey"); + update(); // Regularly poll to get latest data - if (zedSurveyInReset() == false) + delay(10); + if ((millis() - startTime) > maxWait) { - systemPrintln("Survey reset failed - attempt 1/3"); - if (zedSurveyInReset() == false) - { - systemPrintln("Survey reset failed - attempt 2/3"); - if (zedSurveyInReset() == false) - { - systemPrintln("Survey reset failed - attempt 3/3"); - } - } + log_d("PVT Update failed"); + break; } } - bool response = true; - response &= theGNSS->setVal8(UBLOX_CFG_TMODE_MODE, 1); // Survey-in enable - response &= theGNSS->setVal32(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, settings.observationPositionAccuracy * 10000); - response &= theGNSS->setVal32(UBLOX_CFG_TMODE_SVIN_MIN_DUR, settings.observationSeconds); + // The first thing we do is go to 1Hz to lighten any I2C traffic from a previous configuration + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, 1000); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, 1); - if (response == false) + if (commandSupported(UBLOX_CFG_TMODE_MODE)) + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + + // UART1 will primarily be used to pass NMEA and UBX from ZED to ESP32 (eventually to cell phone) + // but the phone can also provide RTCM data and a user may want to configure the ZED over Bluetooth. + // So let's be sure to enable UBX+NMEA+RTCM on the input + response &= _zed->addCfgValset(UBLOX_CFG_UART1OUTPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_UART1OUTPROT_NMEA, 1); + if (commandSupported(UBLOX_CFG_UART1OUTPROT_RTCM3X)) + response &= _zed->addCfgValset(UBLOX_CFG_UART1OUTPROT_RTCM3X, 1); + response &= _zed->addCfgValset(UBLOX_CFG_UART1INPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_UART1INPROT_NMEA, 1); + response &= _zed->addCfgValset(UBLOX_CFG_UART1INPROT_RTCM3X, 1); + if (commandSupported(UBLOX_CFG_UART1INPROT_SPARTN)) + response &= _zed->addCfgValset(UBLOX_CFG_UART1INPROT_SPARTN, 0); + + response &= _zed->addCfgValset(UBLOX_CFG_UART1_BAUDRATE, + settings.dataPortBaud); // Defaults to 230400 to maximize message output support + response &= _zed->addCfgValset( + UBLOX_CFG_UART2_BAUDRATE, + settings.radioPortBaud); // Defaults to 57600 to match SiK telemetry radio firmware default + + // Disable SPI port - This is just to remove some overhead by ZED + response &= _zed->addCfgValset(UBLOX_CFG_SPIOUTPROT_UBX, 0); + response &= _zed->addCfgValset(UBLOX_CFG_SPIOUTPROT_NMEA, 0); + if (commandSupported(UBLOX_CFG_SPIOUTPROT_RTCM3X)) + response &= _zed->addCfgValset(UBLOX_CFG_SPIOUTPROT_RTCM3X, 0); + + response &= _zed->addCfgValset(UBLOX_CFG_SPIINPROT_UBX, 0); + response &= _zed->addCfgValset(UBLOX_CFG_SPIINPROT_NMEA, 0); + response &= _zed->addCfgValset(UBLOX_CFG_SPIINPROT_RTCM3X, 0); + if (commandSupported(UBLOX_CFG_SPIINPROT_SPARTN)) + response &= _zed->addCfgValset(UBLOX_CFG_SPIINPROT_SPARTN, 0); + + // Set the UART2 to only do RTCM (in case this device goes into base mode) + response &= _zed->addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); + response &= _zed->addCfgValset(UBLOX_CFG_UART2OUTPROT_NMEA, 0); + if (commandSupported(UBLOX_CFG_UART2OUTPROT_RTCM3X)) + response &= _zed->addCfgValset(UBLOX_CFG_UART2OUTPROT_RTCM3X, 1); + response &= _zed->addCfgValset(UBLOX_CFG_UART2INPROT_UBX, settings.enableUART2UBXIn); + response &= _zed->addCfgValset(UBLOX_CFG_UART2INPROT_NMEA, 0); + response &= _zed->addCfgValset(UBLOX_CFG_UART2INPROT_RTCM3X, 1); + if (commandSupported(UBLOX_CFG_UART2INPROT_SPARTN)) + response &= _zed->addCfgValset(UBLOX_CFG_UART2INPROT_SPARTN, 0); + + // We don't want NMEA over I2C, but we will want to deliver RTCM, and UBX+RTCM is not an option + response &= _zed->addCfgValset(UBLOX_CFG_I2COUTPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_I2COUTPROT_NMEA, 1); + if (commandSupported(UBLOX_CFG_I2COUTPROT_RTCM3X)) + response &= _zed->addCfgValset(UBLOX_CFG_I2COUTPROT_RTCM3X, 1); + + response &= _zed->addCfgValset(UBLOX_CFG_I2CINPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_I2CINPROT_NMEA, 1); + response &= _zed->addCfgValset(UBLOX_CFG_I2CINPROT_RTCM3X, 1); + + if (commandSupported(UBLOX_CFG_I2CINPROT_SPARTN)) { - systemPrintln("Survey start failed"); - return (false); + if (present.lband_neo) + response &= + _zed->addCfgValset(UBLOX_CFG_I2CINPROT_SPARTN, + 1); // We push NEO-D9S correction data (SPARTN) to ZED-F9P over the I2C interface + else + response &= _zed->addCfgValset(UBLOX_CFG_I2CINPROT_SPARTN, 0); } - systemPrintf("Survey started. This will run until %d seconds have passed and less than %0.03f meter accuracy is " - "achieved.\r\n", - settings.observationSeconds, settings.observationPositionAccuracy); + // The USB port on the ZED may be used for RTCM to/from the computer (as an NTRIP caster or client) + // So let's be sure all protocols are on for the USB port + response &= _zed->addCfgValset(UBLOX_CFG_USBOUTPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_USBOUTPROT_NMEA, 1); + if (commandSupported(UBLOX_CFG_USBOUTPROT_RTCM3X)) + response &= _zed->addCfgValset(UBLOX_CFG_USBOUTPROT_RTCM3X, 1); + response &= _zed->addCfgValset(UBLOX_CFG_USBINPROT_UBX, 1); + response &= _zed->addCfgValset(UBLOX_CFG_USBINPROT_NMEA, 1); + response &= _zed->addCfgValset(UBLOX_CFG_USBINPROT_RTCM3X, 1); + if (commandSupported(UBLOX_CFG_USBINPROT_SPARTN)) + response &= _zed->addCfgValset(UBLOX_CFG_USBINPROT_SPARTN, 0); + + if (commandSupported(UBLOX_CFG_NAVSPG_INFIL_MINCNO)) + { + response &= + _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINCNO, + settings.minCNO); // Set minimum satellite signal level for navigation - default 6 + } - // Wait until active becomes true - long maxTime = 5000; - long startTime = millis(); - while (theGNSS->getSurveyInActive(100) == false) + if (commandSupported(UBLOX_CFG_NAV2_OUT_ENABLED)) { - delay(100); - if (millis() - startTime > maxTime) - return (false); // Reset of survey failed + // Count NAV2 messages and enable NAV2 as needed. + if (getNAV2MessageCount() > 0) + { + response &= _zed->addCfgValset( + UBLOX_CFG_NAV2_OUT_ENABLED, + 1); // Enable NAV2 messages. This has the side effect of causing RTCM to generate twice as fast. + } + else + response &= _zed->addCfgValset(UBLOX_CFG_NAV2_OUT_ENABLED, 0); // Disable NAV2 messages } - return (true); -} + response &= _zed->sendCfgValset(); -// Start the base using fixed coordinates -bool zedFixedBaseStart() -{ - bool response = true; + if (response == false) + systemPrintln("Module failed config block 0"); + response = true; // Reset - if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) - { - // Break ECEF into main and high precision parts - // The type casting should not effect rounding of original double cast coordinate - long majorEcefX = floor((settings.fixedEcefX * 100.0) + 0.5); - long minorEcefX = floor((((settings.fixedEcefX * 100.0) - majorEcefX) * 100.0) + 0.5); - long majorEcefY = floor((settings.fixedEcefY * 100) + 0.5); - long minorEcefY = floor((((settings.fixedEcefY * 100.0) - majorEcefY) * 100.0) + 0.5); - long majorEcefZ = floor((settings.fixedEcefZ * 100) + 0.5); - long minorEcefZ = floor((((settings.fixedEcefZ * 100.0) - majorEcefZ) * 100.0) + 0.5); + // Enable the constellations the user has set + response &= setConstellations(); // 19 messages. Send newCfg or sendCfg with value set + if (response == false) + systemPrintln("Module failed config block 1"); + response = true; // Reset - // systemPrintf("fixedEcefY (should be -4716808.5807): %0.04f\r\n", settings.fixedEcefY); - // systemPrintf("major (should be -471680858): %ld\r\n", majorEcefY); - // systemPrintf("minor (should be -7): %ld\r\n", minorEcefY); + // Make sure the appropriate messages are enabled + response &= setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set + if (response == false) + systemPrintln("Module failed config block 2"); + response = true; // Reset - // Units are cm with a high precision extension so -1234.5678 should be called: (-123456, -78) - //-1280208.308,-4716803.847,4086665.811 is SparkFun HQ so... + // Disable NMEA messages on all but UART1 + response &= _zed->newCfgValset(); + + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_I2C, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_I2C, 0); + + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_UART2, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_UART2, 0); + + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSA_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GSV_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_RMC_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GST_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_GLL_SPI, 0); + response &= _zed->addCfgValset(UBLOX_CFG_MSGOUT_NMEA_ID_VTG_SPI, 0); + + response &= _zed->sendCfgValset(); - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 0); // Position in ECEF - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_X, majorEcefX); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_X_HP, minorEcefX); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_Y, majorEcefY); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_Y_HP, minorEcefY); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_Z, majorEcefZ); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_ECEF_Z_HP, minorEcefZ); - response &= theGNSS->sendCfgValset(); - } - else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) - { - // Add height of instrument (HI) to fixed altitude - // https://www.e-education.psu.edu/geog862/node/1853 - // For example, if HAE is at 100.0m, + 2m stick + 73mm ARP = 102.073 - float totalFixedAltitude = - settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); + if (response == false) + systemPrintln("Module failed config block 3"); - // Break coordinates into main and high precision parts - // The type casting should not effect rounding of original double cast coordinate - int64_t majorLat = settings.fixedLat * 10000000; - int64_t minorLat = ((settings.fixedLat * 10000000) - majorLat) * 100; - int64_t majorLong = settings.fixedLong * 10000000; - int64_t minorLong = ((settings.fixedLong * 10000000) - majorLong) * 100; - int32_t majorAlt = totalFixedAltitude * 100; - int32_t minorAlt = ((totalFixedAltitude * 100) - majorAlt) * 100; + if (present.antennaShortOpen) + { + _zed->newCfgValset(); - // systemPrintf("fixedLong (should be -105.184774720): %0.09f\r\n", settings.fixedLong); - // systemPrintf("major (should be -1051847747): %lld\r\n", majorLat); - // systemPrintf("minor (should be -20): %lld\r\n", minorLat); - // - // systemPrintf("fixedLat (should be 40.090335429): %0.09f\r\n", settings.fixedLat); - // systemPrintf("major (should be 400903354): %lld\r\n", majorLong); - // systemPrintf("minor (should be 29): %lld\r\n", minorLong); - // - // systemPrintf("fixedAlt (should be 1560.2284): %0.04f\r\n", settings.fixedAltitude); - // systemPrintf("major (should be 156022): %ld\r\n", majorAlt); - // systemPrintf("minor (should be 84): %ld\r\n", minorAlt); + _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_SHORTDET, 1); // Enable antenna short detection + _zed->addCfgValset(UBLOX_CFG_HW_ANT_CFG_OPENDET, 1); // Enable antenna open detection - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 1); // Position in LLH - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_LAT, majorLat); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_LAT_HP, minorLat); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_LON, majorLong); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_LON_HP, minorLong); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_HEIGHT, majorAlt); - response &= theGNSS->addCfgValset(UBLOX_CFG_TMODE_HEIGHT_HP, minorAlt); - response &= theGNSS->sendCfgValset(); + if (_zed->sendCfgValset()) + { + _zed->setAutoMONHWcallbackPtr( + &storeMONHWdata); // Enable automatic MON HW messages with callback to storeMONHWdata + } + else + { + systemPrintln("Failed to configure GNSS antenna detection"); + } } + if (response) + systemPrintln("ZED-F9x configuration update"); + return (response); } -// Setup the timepulse output on the PPS pin for external triggering -// Setup TM2 time stamp input as need -bool zedBeginExternalEvent() +//---------------------------------------- +// Configure specific aspects of the receiver for rover mode +//---------------------------------------- +bool ZED::configureRover() { if (online.gnss == false) + { + log_d("GNSS not online"); return (false); + } - // If our settings haven't changed, trust ZED's settings - if (settings.updateGNSSSettings == false) + // If our settings haven't changed, and this is first config since power on, trust GNSS's settings + if (settings.updateGNSSSettings == false && firstPowerOn) { - log_d("Skipping ZED Trigger configuration"); + firstPowerOn = false; // Next time user switches modes, new settings will be applied + log_d("Skipping ZED Rover configuration"); return (true); } - if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) - return (true); // No need to configure PPS if port is not selected + firstPowerOn = false; // If we switch between rover/base in the future, force config of module. - bool response = true; + update(); // Regularly poll to get latest data + + bool success = false; + int tryNo = -1; - if (settings.enableExternalHardwareEventLogging == true) + // Try up to MAX_SET_MESSAGES_RETRIES times to configure the GNSS + // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI + // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being + // processed. + while ((++tryNo < MAX_SET_MESSAGES_RETRIES) && !success) { - theGNSS->setAutoTIMTM2callbackPtr( - &eventTriggerReceived); // Enable automatic TIM TM2 messages with callback to eventTriggerReceived - } - else - theGNSS->setAutoTIMTM2callbackPtr(nullptr); + bool response = true; - return (response); -} + // Set output rate + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, settings.measurementRateMs); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, settings.navigationRate); -// Setup the timepulse output on the PPS pin for external triggering -bool zedBeginPPS() -{ - if (online.gnss == false) - return (false); + // Survey mode is only available on ZED-F9P modules + if (commandSupported(UBLOX_CFG_TMODE_MODE)) + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode - // If our settings haven't changed, trust ZED's settings - if (settings.updateGNSSSettings == false) - { - systemPrintln("Skipping ZED Trigger configuration"); - return (true); - } + response &= + _zed->addCfgValset(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)settings.dynamicModel); // Set dynamic model - if (settings.dataPortChannel != MUX_PPS_EVENTTRIGGER) - return (true); // No need to configure PPS if port is not selected + // RTCM is only available on ZED-F9P modules + // + // For most RTK products, the GNSS is interfaced via both I2C and UART1. Configuration and PVT/HPPOS messages + // are configured over I2C. Any messages that need to be logged are output on UART1, and received by this code + // using serialGNSS-> So in Rover mode, we want to disable any RTCM messages on I2C (and USB and UART2). + // + // But, on the Reference Station, the GNSS is interfaced via SPI. It has no access to I2C and UART1. So for that + // product - in Rover mode - we want to leave any RTCM messages enabled on SPI so they can be logged if desired. - bool response = true; + // Find first RTCM record in ubxMessage array + int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PULSE_DEF, 0); // Time pulse definition is a period (in us) - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PULSE_LENGTH_DEF, 1); // Define timepulse by length (not ratio) - response &= - theGNSS->addCfgValset(UBLOX_CFG_TP_USE_LOCKED_TP1, - 1); // Use CFG-TP-PERIOD_LOCK_TP1 and CFG-TP-LEN_LOCK_TP1 as soon as GNSS time is valid - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_TP1_ENA, settings.enableExternalPulse); // Enable/disable timepulse - response &= - theGNSS->addCfgValset(UBLOX_CFG_TP_POL_TP1, settings.externalPulsePolarity); // 0 = falling, 1 = rising edge + // Set RTCM messages to user's settings + for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) + response &= + _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey - 1, + settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 - 1 = I2C - // While the module is _locking_ to GNSS time, turn off pulse - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PERIOD_TP1, 1000000); // Set the period between pulses in us - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_LEN_TP1, 0); // Set the pulse length in us + // Set RTCM messages to user's settings + for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) + { + response &= + _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 1, + settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 1 = UART2 + response &= + _zed->addCfgValset(ubxMessages[firstRTCMRecord + x].msgConfigKey + 2, + settings.ubxMessageRates[firstRTCMRecord + x]); // UBLOX_CFG UART1 + 2 = USB + } - // When the module is _locked_ to GNSS time, make it generate 1Hz (Default is 100ms high, 900ms low) - response &= theGNSS->addCfgValset(UBLOX_CFG_TP_PERIOD_LOCK_TP1, - settings.externalPulseTimeBetweenPulse_us); // Set the period between pulses is us - response &= - theGNSS->addCfgValset(UBLOX_CFG_TP_LEN_LOCK_TP1, settings.externalPulseLength_us); // Set the pulse length in us - response &= theGNSS->sendCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_NMEA_MAINTALKERID, + 3); // Return talker ID to GNGGA after NTRIP Client set to GPGGA - if (response == false) - systemPrintln("beginExternalTriggers config failed"); + response &= _zed->addCfgValset(UBLOX_CFG_NMEA_HIGHPREC, 1); // Enable high precision NMEA + response &= _zed->addCfgValset(UBLOX_CFG_NMEA_SVNUMBERING, 1); // Enable extended satellite numbering - return (response); -} + response &= _zed->addCfgValset(UBLOX_CFG_NAVSPG_INFIL_MINELEV, settings.minElev); // Set minimum elevation -// Enable data output from the NEO -bool zedEnableLBandCommunication() -{ - /* - Paul's Notes on (NEO-D9S) L-Band: + response &= _zed->sendCfgValset(); // Closing - Torch will receive PointPerfect SPARTN via IP, run it through the PPL, and feed RTCM to the UM980. No L-Band... + if (response) + success = true; + } - The EVK has ZED-F9P and NEO-D9S. But there are two versions of the PCB: - v1.1 PCB : - Both ZED and NEO are on the i2c_0 I2C bus (the OLED is on i2c_1) - ZED UART1 is connected to the ESP32 (pins 25 and 33) only - ZED UART2 is connected to the I/O connector only - NEO UART1 is connected to test points only - NEO UART2 is not connected - v1.0 PCB (the one we are currently using for code development) : - Both ZED and NEO are on the i2c_0 I2C bus - ZED UART1 is connected to NEO UART1 only - not to ESP32 (Big mistake! Makes BT and Logging much more - complicated...) ZED UART2 is connected to the I/O connector only NEO UART2 is not connected + if (!success) + log_d("Rover config failed 1"); - Facet v2 hasn't been built yet. The v2.01 PCB probably won't get built as it needs the new soft power switch. - When v2.10 (?) gets built : - Both ZED and NEO are on the I2C bus - ZED UART1 is connected to the ESP32 (pins 14 and 13) and also to the DATA connector via the Mux (output - only) ZED UART2 is connected to the RADIO connector only NEO UART1 is not connected NEO UART2 TX is connected to - ESP32 pin 4. If the ESP32 has a UART spare - and it probably does - the PMP data can go over this connection and - avoid having double-PMP traffic on I2C. Neat huh?! + if (!success) + systemPrintln("Rover config fail"); - Facet mosaic v1.0 PCB has been built, but needs the new soft power switch and some other minor mods. - X5 COM1 is connected to the ESP32 (pins 13 and 14) - RTCM from PPL, NMEA and RTCM to PPL and Bluetooth - X5 COM2 is connected to the RADIO connector only - X5 COM3 is connected to the DATA connector via the Mux (I/O) - X5 COM4 is connected to the ESP32 (pins 4 and 25) - raw L-Band to PPL, control from ESP32 to X5 ? + return (success); +} - So, what does all of this mean? - EVK v1.0 supports direct NEO to ZED UART communication, but V1.1 will not. There is no point in supporting it - here. Facet v2 can pipe NEO UART PMP data to the ZED (over I2C or maybe UART), but the hardware doesn't exist yet - so there is no point in adding that feature yet... TODO. So, right now, we should assume NEO PMP data will arrive - via I2C, and will get pushed to the ZED via I2C if the corrections priority permits. Deleting: - useI2cForLbandCorrections, useI2cForLbandCorrectionsConfigured and rtcmTimeoutBeforeUsingLBand_s - */ +//---------------------------------------- +void ZED::debuggingDisable() +{ + if (online.gnss) + _zed->disableDebugging(); +} - bool response = true; +//---------------------------------------- +void ZED::debuggingEnable() +{ + if (online.gnss) + // Enable only the critical debug messages over Serial + _zed->enableDebugging(Serial, true); +} -#ifdef COMPILE_L_BAND +//---------------------------------------- +void ZED::enableGgaForNtrip() +{ + if (online.gnss) + { + // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA + _zed->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 1); + _zed->setNMEAGPGGAcallbackPtr(&pushGPGGA); // Set up the callback for GPGGA + + float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + if (measurementFrequency < 0.2) + measurementFrequency = 0.2; // 0.2Hz * 5 = 1 measurement every 5 seconds + log_d("Adjusting GGA setting to %f", measurementFrequency); + _zed->setVal8(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, + measurementFrequency); // Enable GGA over I2C. Tell the module to output GGA every second + } +} - response &= theGNSS->setRXMCORcallbackPtr( - &checkRXMCOR); // Enable callback to check if the PMP data is being decrypted successfully +//---------------------------------------- +// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted +// even if there is no GPS fix. We use it to test serial output. +// Returns true if successfully started and false upon failure +//---------------------------------------- +bool ZED::enableRTCMTest() +{ + if (online.gnss) + { + _zed->newCfgValset(); // Create a new Configuration Item VALSET message + _zed->addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1230_UART2, 1); // Enable message 1230 every second + _zed->sendCfgValset(); // Send the VALSET + return true; + } + return false; +} - if (present.lband_neo == true) +//---------------------------------------- +// Restore the GNSS to the factory settings +//---------------------------------------- +void ZED::factoryReset() +{ + if (online.gnss) { - response &= i2cLBand.setRXMPMPmessageCallbackPtr(&pushRXMPMP); // Enable PMP callback to push raw PMP over I2C + _zed->factoryDefault(); // Reset everything: baud rate, I2C address, update rate, everything. And save to BBR. + _zed->saveConfiguration(); + _zed->hardReset(); // Perform a reset leading to a cold start (zero info start-up) + } +} - response &= i2cLBand.newCfgValset(); +//---------------------------------------- +uint16_t ZED::fileBufferAvailable() +{ + if (online.gnss) + return (_zed->fileBufferAvailable()); + return (0); +} - response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1); // Enable UBX-RXM-PMP on NEO's I2C port +//---------------------------------------- +uint16_t ZED::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +{ + if (online.gnss) + { + _zed->extractFileBufferData(fileBuffer, + fileBytesToRead); // TODO Does extractFileBufferData not return the bytes read? + return (1); + } + return (0); +} - response &= i2cLBand.addCfgValset(UBLOX_CFG_UART1OUTPROT_UBX, 0); // Disable UBX output on NEO's UART1 +//---------------------------------------- +// Start the base using fixed coordinates +//---------------------------------------- +bool ZED::fixedBaseStart() +{ + bool response = true; - response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART1, 0); // Disable UBX-RXM-PMP on NEO's UART1 + if (online.gnss == false) + { + log_d("GNSS not online"); + return (false); + } - // TODO: change this as needed for Facet v2 - response &= i2cLBand.addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); // Disable UBX output on NEO's UART2 + if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) + { + // Break ECEF into main and high precision parts + // The type casting should not effect rounding of original double cast coordinate + long majorEcefX = floor((settings.fixedEcefX * 100.0) + 0.5); + long minorEcefX = floor((((settings.fixedEcefX * 100.0) - majorEcefX) * 100.0) + 0.5); + long majorEcefY = floor((settings.fixedEcefY * 100) + 0.5); + long minorEcefY = floor((((settings.fixedEcefY * 100.0) - majorEcefY) * 100.0) + 0.5); + long majorEcefZ = floor((settings.fixedEcefZ * 100) + 0.5); + long minorEcefZ = floor((((settings.fixedEcefZ * 100.0) - majorEcefZ) * 100.0) + 0.5); - // TODO: change this as needed for Facet v2 - response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 0); // Disable UBX-RXM-PMP on NEO's UART2 + // systemPrintf("fixedEcefY (should be -4716808.5807): %0.04f\r\n", settings.fixedEcefY); + // systemPrintf("major (should be -471680858): %ld\r\n", majorEcefY); + // systemPrintf("minor (should be -7): %ld\r\n", minorEcefY); - response &= i2cLBand.sendCfgValset(); + // Units are cm with a high precision extension so -1234.5678 should be called: (-123456, -78) + //-1280208.308,-4716803.847,4086665.811 is SparkFun HQ so... + + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 0); // Position in ECEF + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_X, majorEcefX); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_X_HP, minorEcefX); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_Y, majorEcefY); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_Y_HP, minorEcefY); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_Z, majorEcefZ); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_ECEF_Z_HP, minorEcefZ); + response &= _zed->sendCfgValset(); } - else + else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) { - systemPrintln("zedEnableLBandCorrections: Unknown platform"); - return (false); - } + // Add height of instrument (HI) to fixed altitude + // https://www.e-education.psu.edu/geog862/node/1853 + // For example, if HAE is at 100.0m, + 2m stick + 73mm ARP = 102.073 + float totalFixedAltitude = + settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); -#endif + // Break coordinates into main and high precision parts + // The type casting should not effect rounding of original double cast coordinate + int64_t majorLat = settings.fixedLat * 10000000; + int64_t minorLat = ((settings.fixedLat * 10000000) - majorLat) * 100; + int64_t majorLong = settings.fixedLong * 10000000; + int64_t minorLong = ((settings.fixedLong * 10000000) - majorLong) * 100; + int32_t majorAlt = totalFixedAltitude * 100; + int32_t minorAlt = ((totalFixedAltitude * 100) - majorAlt) * 100; + + // systemPrintf("fixedLong (should be -105.184774720): %0.09f\r\n", settings.fixedLong); + // systemPrintf("major (should be -1051847747): %lld\r\n", majorLat); + // systemPrintf("minor (should be -20): %lld\r\n", minorLat); + // + // systemPrintf("fixedLat (should be 40.090335429): %0.09f\r\n", settings.fixedLat); + // systemPrintf("major (should be 400903354): %lld\r\n", majorLong); + // systemPrintf("minor (should be 29): %lld\r\n", minorLong); + // + // systemPrintf("fixedAlt (should be 1560.2284): %0.04f\r\n", settings.fixedAltitude); + // systemPrintf("major (should be 156022): %ld\r\n", majorAlt); + // systemPrintf("minor (should be 84): %ld\r\n", minorAlt); + + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 2); // Fixed + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_POS_TYPE, 1); // Position in LLH + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LAT, majorLat); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LAT_HP, minorLat); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LON, majorLong); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_LON_HP, minorLong); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_HEIGHT, majorAlt); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_HEIGHT_HP, minorAlt); + response &= _zed->sendCfgValset(); + } return (response); } -// Disable data output from the NEO -bool zedDisableLBandCommunication() +//---------------------------------------- +// Return the number of active/enabled messages +//---------------------------------------- +uint8_t ZED::getActiveMessageCount() { - bool response = true; + uint8_t count = 0; -#ifdef COMPILE_L_BAND + for (int x = 0; x < MAX_UBX_MSG; x++) + if (settings.ubxMessageRates[x] > 0) + count++; + return (count); +} - response &= theGNSS->setRXMCORcallbackPtr( - nullptr); // Disable callback to check if the PMP data is being decrypted successfully +//---------------------------------------- +// Returns the altitude in meters or zero if the GNSS is offline +//---------------------------------------- +double ZED::getAltitude() +{ + return _altitude; +} - response &= i2cLBand.setRXMPMPmessageCallbackPtr(nullptr); // Disable PMP callback no matter the platform +//---------------------------------------- +// Returns the carrier solution or zero if not online +//---------------------------------------- +uint8_t ZED::getCarrierSolution() +{ + return (_carrierSolution); +} - if (present.lband_neo == true) - { - response &= i2cLBand.newCfgValset(); +//---------------------------------------- +uint32_t ZED::getDataBaudRate() +{ + if (online.gnss) + return _zed->getVal32(UBLOX_CFG_UART1_BAUDRATE); + return (0); +} - response &= - i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 0); // Disable UBX-RXM-PMP from NEO's I2C port +//---------------------------------------- +// Returns the day number or zero if not online +//---------------------------------------- +uint8_t ZED::getDay() +{ + return (_day); +} - // TODO: change this as needed for Facet v2 - response &= i2cLBand.addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); // Disable UBX output from NEO's UART2 +//---------------------------------------- +// Return the number of milliseconds since GNSS data was last updated +//---------------------------------------- +uint16_t ZED::getFixAgeMilliseconds() +{ + return (millis() - _pvtArrivalMillis); +} - // TODO: change this as needed for Facet v2 - response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 0); // Disable UBX-RXM-PMP on NEO's UART2 +//---------------------------------------- +// Returns the fix type or zero if not online +//---------------------------------------- +uint8_t ZED::getFixType() +{ + return (_fixType); +} - response &= i2cLBand.sendCfgValset(); - } - else - { - systemPrintln("zedEnableLBandCorrections: Unknown platform"); - return (false); - } +//---------------------------------------- +// Get the horizontal position accuracy +// Returns the horizontal position accuracy or zero if offline +//---------------------------------------- +float ZED::getHorizontalAccuracy() +{ + return (_horizontalAccuracy); +} -#endif +//---------------------------------------- +// Returns the hours of 24 hour clock or zero if not online +//---------------------------------------- +uint8_t ZED::getHour() +{ + return (_hour); +} - return (response); +//---------------------------------------- +const char * ZED::getId() +{ + if (online.gnss) + return (gnssUniqueId); + return ((char *)"\0"); } -// Given the number of seconds between desired solution reports, determine measurementRateMs and navigationRate -// measurementRateS > 25 & <= 65535 -// navigationRate >= 1 && <= 127 -// We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. -bool zedSetRate(double secondsBetweenSolutions) +//---------------------------------------- +// Get the latitude value +// Returns the latitude value or zero if not online +//---------------------------------------- +double ZED::getLatitude() { - uint16_t measRate = 0; // Calculate these locally and then attempt to apply them to ZED at completion - uint16_t navRate = 0; + return (_latitude); +} - // If we have more than an hour between readings, increase mesaurementRate to near max of 65,535 - if (secondsBetweenSolutions > 3600.0) +//---------------------------------------- +// Query GNSS for current leap seconds +//---------------------------------------- +uint8_t ZED::getLeapSeconds() +{ + if (online.gnss) { - measRate = 65000; + sfe_ublox_ls_src_e leapSecSource; + _leapSeconds = _zed->getCurrentLeapSeconds(leapSecSource); + return (_leapSeconds); } + return (18); // Default to 18 if GNSS is offline +} - // If we have more than 30s, but less than 3600s between readings, use 30s measurement rate - else if (secondsBetweenSolutions > 30.0) - { - measRate = 30000; - } +//---------------------------------------- +// Get the longitude value +// Outputs: +// Returns the longitude value or zero if not online +//---------------------------------------- +double ZED::getLongitude() +{ + return (_longitude); +} - // User wants measurements less than 30s (most common), set measRate to match user request - // This will make navRate = 1. - else +//---------------------------------------- +// Given the name of a message, return the array number +//---------------------------------------- +uint8_t ZED::getMessageNumberByName(const char *msgName) +{ + if (present.gnss_zedf9p) { - measRate = secondsBetweenSolutions * 1000.0; + for (int x = 0; x < MAX_UBX_MSG; x++) + { + if (strcmp(ubxMessages[x].msgTextName, msgName) == 0) + return (x); + } } + else + systemPrintln("getMessageNumberByName() Platform not supported"); - navRate = secondsBetweenSolutions * 1000.0 / measRate; // Set navRate to nearest int value - measRate = secondsBetweenSolutions * 1000.0 / navRate; // Adjust measurement rate to match actual navRate + systemPrintf("getMessageNumberByName: %s not found\r\n", msgName); + return (0); +} - // systemPrintf("measurementRate / navRate: %d / %d\r\n", measRate, navRate); +//---------------------------------------- +// Given the name of a message, find it, and return the rate +//---------------------------------------- +uint8_t ZED::getMessageRateByName(const char *msgName) +{ + return (settings.ubxMessageRates[getMessageNumberByName(msgName)]); +} - bool response = true; - response &= theGNSS->newCfgValset(); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_MEAS, measRate); - response &= theGNSS->addCfgValset(UBLOX_CFG_RATE_NAV, navRate); +//---------------------------------------- +// Returns two digits of milliseconds or zero if not online +//---------------------------------------- +uint8_t ZED::getMillisecond() +{ + return (_millisecond); +} - int gsvRecordNumber = zedGetMessageNumberByName("NMEA_GSV"); +//---------------------------------------- +// Returns minutes or zero if not online +//---------------------------------------- +uint8_t ZED::getMinute() +{ + return (_minute); +} - // If enabled, adjust GSV NMEA to be reported at 1Hz to avoid swamping SPP connection - if (settings.ubxMessageRates[gsvRecordNumber] > 0) - { - float measurementFrequency = (1000.0 / measRate) / navRate; - if (measurementFrequency < 1.0) - measurementFrequency = 1.0; +//---------------------------------------- +// Returns month number or zero if not online +//---------------------------------------- +uint8_t ZED::getMonth() +{ + return (_month); +} - log_d("Adjusting GSV setting to %f", measurementFrequency); +//---------------------------------------- +// Returns nanoseconds or zero if not online +//---------------------------------------- +uint32_t ZED::getNanosecond() +{ + return (_nanosecond); +} - zedSetMessageRateByName("NMEA_GSV", measurementFrequency); // Update GSV setting in file - response &= theGNSS->addCfgValset(ubxMessages[gsvRecordNumber].msgConfigKey, - settings.ubxMessageRates[gsvRecordNumber]); // Update rate on module - } +//---------------------------------------- +// Count the number of NAV2 messages with rates more than 0. Used for determining if we need the enable +// the global NAV2 feature. +//---------------------------------------- +uint8_t ZED::getNAV2MessageCount() +{ + int enabledMessages = 0; + int startOfBlock = 0; + int endOfBlock = 0; - response &= theGNSS->sendCfgValset(); // Closing value - max 4 pairs + setMessageOffsets(&ubxMessages[0], "NAV2", startOfBlock, + endOfBlock); // Find start and stop of given messageType in message array - // If we successfully set rates, only then record to settings - if (response == true) + for (int x = 0; x < (endOfBlock - startOfBlock); x++) { - settings.measurementRateMs = measRate; - settings.navigationRate = navRate; + if (settings.ubxMessageRates[x + startOfBlock] > 0) + enabledMessages++; } - else + + setMessageOffsets(&ubxMessages[0], "NMEANAV2", startOfBlock, + endOfBlock); // Find start and stop of given messageType in message array + + for (int x = 0; x < (endOfBlock - startOfBlock); x++) { - systemPrintln("Failed to set measurement and navigation rates"); - return (false); + if (settings.ubxMessageRates[x + startOfBlock] > 0) + enabledMessages++; } - return (true); + return (enabledMessages); } +//---------------------------------------- +uint32_t ZED::getRadioBaudRate() +{ + if (online.gnss) + return _zed->getVal32(UBLOX_CFG_UART2_BAUDRATE); + return (0); +} + +//---------------------------------------- // Returns the seconds between measurements -double zedGetRateS() +//---------------------------------------- +double ZED::getRateS() { // Because we may be in base mode, do not get freq from module, use settings instead float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; @@ -1090,268 +1233,642 @@ double zedGetRateS() return (measurementRateS); } -void zedSetElevation(uint8_t elevationDegrees) +//---------------------------------------- +const char * ZED::getRtcmDefaultString() { - theGNSS->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINELEV, elevationDegrees); // Set minimum elevation + return ((char *)"1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz"); } -void zedSetMinCno(uint8_t cnoValue) +//---------------------------------------- +const char * ZED::getRtcmLowDataRateString() { - theGNSS->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINCNO, cnoValue); + return ((char *)"1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz"); } -double zedGetLatitude() +//---------------------------------------- +// Returns the number of satellites in view or zero if offline +//---------------------------------------- +uint8_t ZED::getSatellitesInView() { - return (zedLatitude); + return (_satellitesInView); } -double zedGetLongitude() +//---------------------------------------- +// Returns seconds or zero if not online +//---------------------------------------- +uint8_t ZED::getSecond() { - return (zedLongitude); + return (_second); } -double zedGetAltitude() +//---------------------------------------- +// Get the survey-in mean accuracy +//---------------------------------------- +float ZED::getSurveyInMeanAccuracy() { - return (zedAltitude); -} + static float svinMeanAccuracy = 0; + static unsigned long lastCheck = 0; -float zedGetHorizontalAccuracy() -{ - return (zedHorizontalAccuracy); -} + if (online.gnss == false) + return (0); -uint8_t zedGetSatellitesInView() -{ - return (zedSatellitesInView); + // Use a local static so we don't have to request these values multiple times (ZED takes many ms to respond + // to this command) + if (millis() - lastCheck > 1000) + { + lastCheck = millis(); + svinMeanAccuracy = _zed->getSurveyInMeanAccuracy(50); + } + return (svinMeanAccuracy); } -// Return full year, ie 2023, not 23. -uint16_t zedGetYear() +//---------------------------------------- +// Return the number of seconds the survey-in process has been running +//---------------------------------------- +int ZED::getSurveyInObservationTime() { - return (zedYear); + static uint16_t svinObservationTime = 0; + static unsigned long lastCheck = 0; + + if (online.gnss == false) + return (0); + + // Use a local static so we don't have to request these values multiple times (ZED takes many ms to respond + // to this command) + if (millis() - lastCheck > 1000) + { + lastCheck = millis(); + svinObservationTime = _zed->getSurveyInObservationTime(50); + } + return (svinObservationTime); } -uint8_t zedGetMonth() + +//---------------------------------------- +// Returns timing accuracy or zero if not online +//---------------------------------------- +uint32_t ZED::getTimeAccuracy() { - return (zedMonth); + return (_tAcc); } -uint8_t zedGetDay() + +//---------------------------------------- +// Returns full year, ie 2023, not 23. +//---------------------------------------- +uint16_t ZED::getYear() { - return (zedDay); + return (_year); } -uint8_t zedGetHour() + +//---------------------------------------- +bool ZED::isBlocking() { - return (zedHour); + return (false); } -uint8_t zedGetMinute() + +//---------------------------------------- +// Date is confirmed once we have GNSS fix +//---------------------------------------- +bool ZED::isConfirmedDate() { - return (zedMinute); + return (_confirmedDate); } -uint8_t zedGetSecond() + +//---------------------------------------- +// Time is confirmed once we have GNSS fix +//---------------------------------------- +bool ZED::isConfirmedTime() { - return (zedSecond); + return (_confirmedTime); } -uint8_t zedGetMillisecond() + +//---------------------------------------- +// Return true if GNSS receiver has a higher quality DGPS fix than 3D +//---------------------------------------- +bool ZED::isDgpsFixed() { - return (zedMillisecond); + // Not supported + return (false); } -uint8_t zedGetNanosecond() + +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to know if we have a valid fix, not what type of fix +// This function checks to see if the given platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool ZED::isFixed() { - return (zedNanosecond); + // 0 = no fix + // 1 = dead reckoning only + // 2 = 2D-fix + // 3 = 3D-fix + // 4 = GNSS + dead reckoning combined + // 5 = time only fix + return (_fixType >= 3); } -uint8_t zedGetFixType() +//---------------------------------------- +// Used in tpISR() for time pulse synchronization +//---------------------------------------- +bool ZED::isFullyResolved() { - return (zedFixType); + return (_fullyResolved); } -uint8_t zedGetCarrierSolution() + +//---------------------------------------- +bool ZED::isPppConverged() { - return (zedCarrierSolution); + return (false); } -bool zedIsValidDate() +//---------------------------------------- +bool ZED::isPppConverging() { - return (zedValidDate); + return (false); } -bool zedIsValidTime() + +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to +// know if we have an RTK Fix. This function checks to see if the given +// platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool ZED::isRTKFix() { - return (zedValidTime); + // 0 = No RTK + // 1 = RTK Float + // 2 = RTK Fix + return (_carrierSolution == 2); } -bool zedIsConfirmedDate() + +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to +// know if we have an RTK Float. This function checks to see if the +// given platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool ZED::isRTKFloat() { - return (zedConfirmedDate); + // 0 = No RTK + // 1 = RTK Float + // 2 = RTK Fix + return (_carrierSolution == 1); } -bool zedIsConfirmedTime() + +//---------------------------------------- +// Determine if the survey-in operation is complete +//---------------------------------------- +bool ZED::isSurveyInComplete() { - return (zedConfirmedTime); + if (online.gnss) + return (_zed->getSurveyInValid(50)); + return (false); } -bool zedIsFullyResolved() + +//---------------------------------------- +// Date will be valid if the RTC is reporting (regardless of GNSS fix) +//---------------------------------------- +bool ZED::isValidDate() { - return (zedFullyResolved); + return (_validDate); } -uint32_t zedGetTimeAccuracy() + +//---------------------------------------- +// Time will be valid if the RTC is reporting (regardless of GNSS fix) +//---------------------------------------- +bool ZED::isValidTime() { - return (zedTAcc); + return (_validTime); } -// Return the number of milliseconds since data was updated -uint16_t zedFixAgeMilliseconds() +//---------------------------------------- +// Disable data output from the NEO +//---------------------------------------- +bool ZED::lBandCommunicationDisable() { - return (millis() - zedPvtArrivalMillis); + bool response = true; + +#ifdef COMPILE_L_BAND + + response &= _zed->setRXMCORcallbackPtr( + nullptr); // Disable callback to check if the PMP data is being decrypted successfully + + response &= i2cLBand.setRXMPMPmessageCallbackPtr(nullptr); // Disable PMP callback no matter the platform + + if (present.lband_neo) + { + response &= i2cLBand.newCfgValset(); + + response &= + i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 0); // Disable UBX-RXM-PMP from NEO's I2C port + + // TODO: change this as needed for Facet v2 + response &= i2cLBand.addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); // Disable UBX output from NEO's UART2 + + // TODO: change this as needed for Facet v2 + response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 0); // Disable UBX-RXM-PMP on NEO's UART2 + + response &= i2cLBand.sendCfgValset(); + } + else + { + systemPrintln("zedEnableLBandCorrections: Unknown platform"); + return (false); + } + +#endif + + return (response); } -// Print the module type and firmware version -void zedPrintInfo() +//---------------------------------------- +// Enable data output from the NEO +//---------------------------------------- +bool ZED::lBandCommunicationEnable() { - systemPrintf("ZED-F9P firmware: %s\r\n", gnssFirmwareVersion); + /* + Paul's Notes on (NEO-D9S) L-Band: + + Torch will receive PointPerfect SPARTN via IP, run it through the PPL, and feed RTCM to the UM980. No L-Band... + + The EVK has ZED-F9P and NEO-D9S. But there are two versions of the PCB: + v1.1 PCB : + Both ZED and NEO are on the i2c_0 I2C bus (the OLED is on i2c_1) + ZED UART1 is connected to the ESP32 (pins 25 and 33) only + ZED UART2 is connected to the I/O connector only + NEO UART1 is connected to test points only + NEO UART2 is not connected + v1.0 PCB (the one we are currently using for code development) : + Both ZED and NEO are on the i2c_0 I2C bus + ZED UART1 is connected to NEO UART1 only - not to ESP32 (Big mistake! Makes BT and Logging much more + complicated...) ZED UART2 is connected to the I/O connector only NEO UART2 is not connected + + Facet v2 hasn't been built yet. The v2.01 PCB probably won't get built as it needs the new soft power switch. + When v2.10 (?) gets built : + Both ZED and NEO are on the I2C bus + ZED UART1 is connected to the ESP32 (pins 14 and 13) and also to the DATA connector via the Mux (output + only) ZED UART2 is connected to the RADIO connector only NEO UART1 is not connected NEO UART2 TX is connected to + ESP32 pin 4. If the ESP32 has a UART spare - and it probably does - the PMP data can go over this connection and + avoid having double-PMP traffic on I2C. Neat huh?! + + Facet mosaic v1.0 PCB has been built, but needs the new soft power switch and some other minor mods. + X5 COM1 is connected to the ESP32 (pins 13 and 14) - RTCM from PPL, NMEA and RTCM to PPL and Bluetooth + X5 COM2 is connected to the RADIO connector only + X5 COM3 is connected to the DATA connector via the Mux (I/O) + X5 COM4 is connected to the ESP32 (pins 4 and 25) - raw L-Band to PPL, control from ESP32 to X5 ? + + So, what does all of this mean? + EVK v1.0 supports direct NEO to ZED UART communication, but V1.1 will not. There is no point in supporting it + here. Facet v2 can pipe NEO UART PMP data to the ZED (over I2C or maybe UART), but the hardware doesn't exist yet + so there is no point in adding that feature yet... TODO. So, right now, we should assume NEO PMP data will arrive + via I2C, and will get pushed to the ZED via I2C if the corrections priority permits. Deleting: + useI2cForLbandCorrections, useI2cForLbandCorrectionsConfigured and rtcmTimeoutBeforeUsingLBand_s + */ + + bool response = true; + +#ifdef COMPILE_L_BAND + + response &= _zed->setRXMCORcallbackPtr( + &checkRXMCOR); // Enable callback to check if the PMP data is being decrypted successfully + + if (present.lband_neo) + { + response &= i2cLBand.setRXMPMPmessageCallbackPtr(&pushRXMPMP); // Enable PMP callback to push raw PMP over I2C + + response &= i2cLBand.newCfgValset(); + + response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1); // Enable UBX-RXM-PMP on NEO's I2C port + + response &= i2cLBand.addCfgValset(UBLOX_CFG_UART1OUTPROT_UBX, 0); // Disable UBX output on NEO's UART1 + + response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART1, 0); // Disable UBX-RXM-PMP on NEO's UART1 + + // TODO: change this as needed for Facet v2 + response &= i2cLBand.addCfgValset(UBLOX_CFG_UART2OUTPROT_UBX, 0); // Disable UBX output on NEO's UART2 + + // TODO: change this as needed for Facet v2 + response &= i2cLBand.addCfgValset(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 0); // Disable UBX-RXM-PMP on NEO's UART2 + + response &= i2cLBand.sendCfgValset(); + } + else + { + systemPrintln("zedEnableLBandCorrections: Unknown platform"); + return (false); + } + +#endif + + return (response); } -void zedFactoryReset() +//---------------------------------------- +bool ZED::lock(void) { - theGNSS->factoryDefault(); // Reset everything: baud rate, I2C address, update rate, everything. And save to BBR. - theGNSS->saveConfiguration(); - theGNSS->hardReset(); // Perform a reset leading to a cold start (zero info start-up) + if (!iAmLocked) + { + iAmLocked = true; + return true; + } + + unsigned long startTime = millis(); + while (((millis() - startTime) < 2100) && (iAmLocked)) + delay(1); // Yield + + if (!iAmLocked) + { + iAmLocked = true; + return true; + } + + return false; } -void zedSaveConfiguration() +//---------------------------------------- +bool ZED::lockCreate(void) { - theGNSS->saveConfiguration(); // Save the current settings to flash and BBR on the ZED-F9P + return true; } -void zedEnableDebugging() +//---------------------------------------- +void ZED::lockDelete(void) { - theGNSS->enableDebugging(Serial, true); // Enable only the critical debug messages over Serial } -void zedDisableDebugging() + +//---------------------------------------- +// Controls the constellations that are used to generate a fix and logged +//---------------------------------------- +void ZED::menuConstellations() { - theGNSS->disableDebugging(); + while (1) + { + systemPrintln(); + systemPrintln("Menu: Constellations"); + + for (int x = 0; x < MAX_UBX_CONSTELLATIONS; x++) + { + systemPrintf("%d) Constellation %s: ", x + 1, settings.ubxConstellations[x].textName); + if (settings.ubxConstellations[x].enabled) + systemPrint("Enabled"); + else + systemPrint("Disabled"); + systemPrintln(); + } + + systemPrintln("x) Exit"); + + int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long + + if (incoming >= 1 && incoming <= MAX_UBX_CONSTELLATIONS) + { + incoming--; // Align choice to constellation array of 0 to 5 + + settings.ubxConstellations[incoming].enabled ^= 1; + + // 3.10.6: To avoid cross-correlation issues, it is recommended that GPS and QZSS are always both enabled or + // both disabled. + if (incoming == ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS)) // Match QZSS to GPS + { + settings.ubxConstellations[ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_QZSS)].enabled = + settings.ubxConstellations[incoming].enabled; + } + if (incoming == ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_QZSS)) // Match GPS to QZSS + { + settings.ubxConstellations[ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS)].enabled = + settings.ubxConstellations[incoming].enabled; + } + } + else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) + break; + else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) + break; + else + printUnknown(incoming); + } + + // Apply current settings to module + setConstellations(); + + clearBuffer(); // Empty buffer of any newline chars } -void zedSetTalkerGNGGA() +//---------------------------------------- +void ZED::menuMessageBaseRtcm() { - theGNSS->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 3); // Return talker ID to GNGGA after NTRIP Client set to GPGGA - theGNSS->setNMEAGPGGAcallbackPtr(nullptr); // Remove callback + zedMenuMessagesSubtype(settings.ubxMessageRatesBase, "RTCM-Base"); } -void zedEnableGgaForNtrip() + +//---------------------------------------- +// Control the messages that get broadcast over Bluetooth and logged (if enabled) +//---------------------------------------- +void ZED::menuMessages() { - // Set the Main Talker ID to "GP". The NMEA GGA messages will be GPGGA instead of GNGGA - theGNSS->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 1); - theGNSS->setNMEAGPGGAcallbackPtr(&pushGPGGA); // Set up the callback for GPGGA + while (1) + { + systemPrintln(); + systemPrintln("Menu: GNSS Messages"); - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; - if (measurementFrequency < 0.2) - measurementFrequency = 0.2; // 0.2Hz * 5 = 1 measurement every 5 seconds - log_d("Adjusting GGA setting to %f", measurementFrequency); - theGNSS->setVal8(UBLOX_CFG_MSGOUT_NMEA_ID_GGA_I2C, - measurementFrequency); // Enable GGA over I2C. Tell the module to output GGA every second -} + systemPrintf("Active messages: %d\r\n", getActiveMessageCount()); -// Enable all the valid messages for this platform -// There are many messages so split into batches. VALSET is limited to 64 max per batch -// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set -bool zedSetMessages(int maxRetries) -{ - bool success = false; - int tryNo = -1; + systemPrintln("1) Set NMEA Messages"); + systemPrintln("2) Set RTCM Messages"); + systemPrintln("3) Set RXM Messages"); + systemPrintln("4) Set NAV Messages"); + systemPrintln("5) Set NAV2 Messages"); + systemPrintln("6) Set NMEA NAV2 Messages"); + systemPrintln("7) Set MON Messages"); + systemPrintln("8) Set TIM Messages"); + systemPrintln("9) Set PUBX Messages"); - // Try up to maxRetries times to configure the messages - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < maxRetries) && !success) - { - bool response = true; - int messageNumber = 0; + systemPrintln("10) Reset to Surveying Defaults (NMEAx5)"); + systemPrintln("11) Reset to PPP Logging Defaults (NMEAx5 + RXMx2)"); + systemPrintln("12) Turn off all messages"); + systemPrintln("13) Turn on all messages"); - while (messageNumber < MAX_UBX_MSG) - { - response &= theGNSS->newCfgValset(); + systemPrintln("x) Exit"); - do - { - if (messageSupported(messageNumber) == true) - { - uint8_t rate = settings.ubxMessageRates[messageNumber]; + int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long - response &= theGNSS->addCfgValset(ubxMessages[messageNumber].msgConfigKey, rate); - } - messageNumber++; - } while (((messageNumber % 43) < 42) && - (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 in - // size. It's a HHGTTG thing. + if (incoming == 1) + zedMenuMessagesSubtype(settings.ubxMessageRates, + "NMEA_"); // The following _ avoids listing NMEANAV2 messages + else if (incoming == 2) + zedMenuMessagesSubtype(settings.ubxMessageRates, "RTCM"); + else if (incoming == 3) + zedMenuMessagesSubtype(settings.ubxMessageRates, "RXM"); + else if (incoming == 4) + zedMenuMessagesSubtype(settings.ubxMessageRates, "NAV_"); // The following _ avoids listing NAV2 messages + else if (incoming == 5) + zedMenuMessagesSubtype(settings.ubxMessageRates, "NAV2"); + else if (incoming == 6) + zedMenuMessagesSubtype(settings.ubxMessageRates, "NMEANAV2"); + else if (incoming == 7) + zedMenuMessagesSubtype(settings.ubxMessageRates, "MON"); + else if (incoming == 8) + zedMenuMessagesSubtype(settings.ubxMessageRates, "TIM"); + else if (incoming == 9) + zedMenuMessagesSubtype(settings.ubxMessageRates, "PUBX"); + else if (incoming == 10) + { + setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages + setMessageRateByName("NMEA_GGA", 1); + setMessageRateByName("NMEA_GSA", 1); + setMessageRateByName("NMEA_GST", 1); - if (theGNSS->sendCfgValset() == false) - { - log_d("sendCfg failed at messageNumber %d %s. Try %d of %d.", messageNumber - 1, - (messageNumber - 1) < MAX_UBX_MSG ? ubxMessages[messageNumber - 1].msgTextName : "", tryNo + 1, - maxRetries); - response &= false; // If any one of the Valset fails, report failure overall - } + // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection + float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + if (measurementFrequency < 1.0) + measurementFrequency = 1.0; + setMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second + + setMessageRateByName("NMEA_RMC", 1); + systemPrintln("Reset to Surveying Defaults (NMEAx5)"); } + else if (incoming == 11) + { + setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages + setMessageRateByName("NMEA_GGA", 1); + setMessageRateByName("NMEA_GSA", 1); + setMessageRateByName("NMEA_GST", 1); - if (response) - success = true; + // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection + float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; + if (measurementFrequency < 1.0) + measurementFrequency = 1.0; + setMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second + + setMessageRateByName("NMEA_RMC", 1); + + setMessageRateByName("RXM_RAWX", 1); + setMessageRateByName("RXM_SFRBX", 1); + systemPrintln("Reset to PPP Logging Defaults (NMEAx5 + RXMx2)"); + } + else if (incoming == 12) + { + setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages + systemPrintln("All messages disabled"); + } + else if (incoming == 13) + { + setGNSSMessageRates(settings.ubxMessageRates, 1); // Turn on all messages to report once per fix + systemPrintln("All messages enabled"); + } + else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) + break; + else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) + break; + else + printUnknown(incoming); } - return (success); + clearBuffer(); // Empty buffer of any newline chars + + // Make sure the appropriate messages are enabled + bool response = setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set + if (response == false) + systemPrintf("menuMessages: Failed to enable messages - after %d tries", MAX_SET_MESSAGES_RETRIES); + else + systemPrintln("menuMessages: Messages successfully enabled"); + + setLoggingType(); // Update Standard, PPP, or custom for icon selection } -// Enable all the valid messages for this platform over the USB port -// Add 2 to every UART1 key. This is brittle and non-perfect, but works. -bool zedSetMessagesUsb(int maxRetries) +//---------------------------------------- +// Print the module type and firmware version +//---------------------------------------- +void ZED::printModuleInfo() { - bool success = false; - int tryNo = -1; + systemPrintf("ZED-F9P firmware: %s\r\n", gnssFirmwareVersion); +} - // Try up to maxRetries times to configure the messages - // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI - // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being - // processed. - while ((++tryNo < maxRetries) && !success) - { - bool response = true; - int messageNumber = 0; +//---------------------------------------- +// Send correction data to the GNSS +// Returns the number of correction data bytes written +//---------------------------------------- +int ZED::pushRawData(uint8_t *dataToSend, int dataLength) +{ + if (online.gnss) + return (_zed->pushRawData((uint8_t *)dataToSend, dataLength)); + return (0); +} + +//---------------------------------------- +uint16_t ZED::rtcmBufferAvailable() +{ + if (online.gnss) + return (_zed->rtcmBufferAvailable()); + return (0); +} + +//---------------------------------------- +// If LBand is being used, ignore any RTCM that may come in from the GNSS +//---------------------------------------- +void ZED::rtcmOnGnssDisable() +{ + if (online.gnss) + _zed->setUART2Input(COM_TYPE_UBX); // Set ZED's UART2 to input UBX (no RTCM) +} - while (messageNumber < MAX_UBX_MSG) - { - response &= theGNSS->newCfgValset(); +//---------------------------------------- +// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver +//---------------------------------------- +void ZED::rtcmOnGnssEnable() +{ + if (online.gnss) + _zed->setUART2Input(COM_TYPE_RTCM3); // Set the ZED's UART2 to input RTCM +} - do - { - if (messageSupported(messageNumber) == true) - response &= theGNSS->addCfgValset(ubxMessages[messageNumber].msgConfigKey + 2, - settings.ubxMessageRates[messageNumber]); - messageNumber++; - } while (((messageNumber % 43) < 42) && - (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 in - // size. It's a HHGTTG thing. - - response &= theGNSS->sendCfgValset(); - } +//---------------------------------------- +uint16_t ZED::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +{ + if (online.gnss) + return (_zed->extractRTCMBufferData(rtcmBuffer, rtcmBytesToRead)); + return (0); +} - if (response) - success = true; - } +//---------------------------------------- +// Save the current configuration +// Returns true when the configuration was saved and false upon failure +//---------------------------------------- +bool ZED::saveConfiguration() +{ + if (online.gnss == false) + return false; - return (success); + _zed->saveConfiguration(); // Save the current settings to flash and BBR on the ZED-F9P + return true; +} + +//---------------------------------------- +// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS +// This just sets the GNSS side +// Used during Bluetooth testing +//---------------------------------------- +bool ZED::setBaudrate(uint32_t baudRate) +{ + if (online.gnss) + return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, + (115200 * 2)); // Defaults to 230400 to maximize message output support + return false; } +//---------------------------------------- // Enable all the valid constellations and bands for this platform // Band support varies between platforms and firmware versions -// We open/close a complete set if sendCompleteBatch = true -// 19 messages -bool zedSetConstellations(bool sendCompleteBatch) +// We open/close a complete set of 19 messages +//---------------------------------------- +bool ZED::setConstellations() { + if (online.gnss == false) + return (false); + bool response = true; - if (sendCompleteBatch) - response &= theGNSS->newCfgValset(); + response &= _zed->newCfgValset(); // GPS int gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS); bool enableMe = settings.ubxConstellations[gnssIndex].enabled; - response &= theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GPS_L1CA_ENA, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GPS_L2C_ENA, enableMe); + response &= _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GPS_L1CA_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GPS_L2C_ENA, enableMe); // SBAS // v1.12 ZED-F9P firmware does not allow for SBAS control @@ -1364,598 +1881,645 @@ bool zedSetConstellations(bool sendCompleteBatch) { gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_SBAS); enableMe = settings.ubxConstellations[gnssIndex].enabled; - response &= theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_SBAS_L1CA_ENA, enableMe); + response &= _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_SBAS_L1CA_ENA, enableMe); } // GAL gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GALILEO); enableMe = settings.ubxConstellations[gnssIndex].enabled; response &= - theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GAL_E1_ENA, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GAL_E5B_ENA, enableMe); + _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GAL_E1_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GAL_E5B_ENA, enableMe); // BDS gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_BEIDOU); enableMe = settings.ubxConstellations[gnssIndex].enabled; response &= - theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_BDS_B1_ENA, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_BDS_B2_ENA, enableMe); + _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_BDS_B1_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_BDS_B2_ENA, enableMe); // QZSS gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_QZSS); enableMe = settings.ubxConstellations[gnssIndex].enabled; response &= - theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L1CA_ENA, enableMe); + _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L1CA_ENA, enableMe); // UBLOX_CFG_SIGNAL_QZSS_L1S_ENA not supported on F9R in v1.21 and below - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L1S_ENA, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L2C_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L1S_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_QZSS_L2C_ENA, enableMe); // GLO gnssIndex = ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GLONASS); enableMe = settings.ubxConstellations[gnssIndex].enabled; response &= - theGNSS->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GLO_L1_ENA, enableMe); - response &= theGNSS->addCfgValset(UBLOX_CFG_SIGNAL_GLO_L2_ENA, enableMe); + _zed->addCfgValset(settings.ubxConstellations[gnssIndex].configKey, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GLO_L1_ENA, enableMe); + response &= _zed->addCfgValset(UBLOX_CFG_SIGNAL_GLO_L2_ENA, enableMe); - if (sendCompleteBatch) - response &= theGNSS->sendCfgValset(); + response &= _zed->sendCfgValset(); return (response); } -uint16_t zedFileBufferAvailable() -{ - return (theGNSS->fileBufferAvailable()); -} - -uint16_t zedRtcmBufferAvailable() -{ - return (theGNSS->rtcmBufferAvailable()); -} - -uint16_t zedRtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +//---------------------------------------- +bool ZED::setDataBaudRate(uint32_t baud) { - return (theGNSS->extractRTCMBufferData(rtcmBuffer, rtcmBytesToRead)); + if (online.gnss) + return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, baud); + return false; } -uint16_t zedExtractFileBufferData(uint8_t *fileBuffer, int fileBytesToRead) +//---------------------------------------- +// Set the elevation in degrees +//---------------------------------------- +bool ZED::setElevation(uint8_t elevationDegrees) { - theGNSS->extractFileBufferData(fileBuffer, - fileBytesToRead); // TODO Does extractFileBufferData not return the bytes read? - return (1); + if (online.gnss) + { + _zed->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINELEV, elevationDegrees); // Set minimum elevation + return true; + } + return false; } -// Query GNSS for current leap seconds -uint8_t zedGetLeapSeconds() +//---------------------------------------- +// Given a unique string, find first and last records containing that string in message array +//---------------------------------------- +void ZED::setMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock) { - uint8_t leapSeconds; + char messageNamePiece[40]; // UBX_RTCM + snprintf(messageNamePiece, sizeof(messageNamePiece), "%s", messageType); // Put UBX_ infront of type - sfe_ublox_ls_src_e leapSecSource; - leapSeconds = theGNSS->getCurrentLeapSeconds(leapSecSource); - gnss->_leapSeconds = leapSeconds; - return (leapSeconds); -} - -// If we have decryption keys, configure module -// Note: don't check online.lband_neo here. We could be using ip corrections -void zedApplyPointPerfectKeys() -{ - if (online.gnss == false) + // Find the first occurrence + for (startOfBlock = 0; startOfBlock < MAX_UBX_MSG; startOfBlock++) { - if (settings.debugCorrections == true) - systemPrintln("ZED-F9P not available"); + if (strstr(localMessage[startOfBlock].msgTextName, messageNamePiece) != nullptr) + break; + } + if (startOfBlock == MAX_UBX_MSG) + { + // Error out + startOfBlock = 0; + endOfBlock = 0; return; } - // NEO-D9S encrypted PMP messages are only supported on ZED-F9P firmware v1.30 and above - if (gnssFirmwareVersionInt < 130) + // Find the last occurrence + for (endOfBlock = startOfBlock + 1; endOfBlock < MAX_UBX_MSG; endOfBlock++) { - systemPrintln("Error: PointPerfect corrections currently supported by ZED-F9P firmware v1.30 and above. " - "Please upgrade your ZED firmware: " - "https://learn.sparkfun.com/tutorials/how-to-upgrade-firmware-of-a-u-blox-gnss-receiver"); - return; + if (strstr(localMessage[endOfBlock].msgTextName, messageNamePiece) == nullptr) + break; } +} - if (strlen(settings.pointPerfectNextKey) > 0) +//---------------------------------------- +// Given the name of a message, find it, and set the rate +//---------------------------------------- +bool ZED::setMessageRateByName(const char *msgName, uint8_t msgRate) +{ + for (int x = 0; x < MAX_UBX_MSG; x++) { - const uint8_t currentKeyLengthBytes = 16; - const uint8_t nextKeyLengthBytes = 16; + if (strcmp(ubxMessages[x].msgTextName, msgName) == 0) + { + settings.ubxMessageRates[x] = msgRate; + return (true); + } + } + systemPrintf("setMessageRateByName: %s not found\r\n", msgName); + return (false); +} - uint16_t currentKeyGPSWeek; - uint32_t currentKeyGPSToW; - long long epoch = thingstreamEpochToGPSEpoch(settings.pointPerfectCurrentKeyStart); - epochToWeekToW(epoch, ¤tKeyGPSWeek, ¤tKeyGPSToW); +//---------------------------------------- +// Enable all the valid messages for this platform +// There are many messages so split into batches. VALSET is limited to 64 max per batch +// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set +//---------------------------------------- +bool ZED::setMessages(int maxRetries) +{ + bool success = false; - uint16_t nextKeyGPSWeek; - uint32_t nextKeyGPSToW; - epoch = thingstreamEpochToGPSEpoch(settings.pointPerfectNextKeyStart); - epochToWeekToW(epoch, &nextKeyGPSWeek, &nextKeyGPSToW); + if (online.gnss) + { + int tryNo = -1; - // If we are on a L-Band-only or L-Band+IP, set the SOURCE to 1 (L-Band) - // Else set the SOURCE to 0 (IP) - // If we are on L-Band+IP and IP corrections start to arrive, the corrections - // priority code will change SOURCE to match - if (strstr(settings.pointPerfectKeyDistributionTopic, "/Lb") != nullptr) + // Try up to maxRetries times to configure the messages + // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI + // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being + // processed. + while ((++tryNo < maxRetries) && !success) { - updateZEDCorrectionsSource(1); // Set SOURCE to 1 (L-Band) if needed - } - else - { - updateZEDCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed - } + bool response = true; + int messageNumber = 0; - theGNSS->setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C - - theGNSS->setVal8(UBLOX_CFG_NAVHPG_DGNSSMODE, - 3); // Set the differential mode - ambiguities are fixed whenever possible + while (messageNumber < MAX_UBX_MSG) + { + response &= _zed->newCfgValset(); - bool response = theGNSS->setDynamicSPARTNKeys(currentKeyLengthBytes, currentKeyGPSWeek, currentKeyGPSToW, - settings.pointPerfectCurrentKey, nextKeyLengthBytes, - nextKeyGPSWeek, nextKeyGPSToW, settings.pointPerfectNextKey); + do + { + if (messageSupported(messageNumber)) + { + uint8_t rate = settings.ubxMessageRates[messageNumber]; + + response &= _zed->addCfgValset(ubxMessages[messageNumber].msgConfigKey, rate); + } + messageNumber++; + } while (((messageNumber % 43) < 42) && + (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 in + // size. It's a HHGTTG thing. + + if (_zed->sendCfgValset() == false) + { + log_d("sendCfg failed at messageNumber %d %s. Try %d of %d.", messageNumber - 1, + (messageNumber - 1) < MAX_UBX_MSG ? ubxMessages[messageNumber - 1].msgTextName : "", tryNo + 1, + maxRetries); + response &= false; // If any one of the Valset fails, report failure overall + } + } - if (response == false) - systemPrintln("setDynamicSPARTNKeys failed"); - else - { - if (settings.debugCorrections == true) - systemPrintln("PointPerfect keys applied"); - online.lbandCorrections = true; + if (response) + success = true; } } - else - { - if (settings.debugCorrections == true) - systemPrintln("No PointPerfect keys available"); - } + return (success); } -void updateZEDCorrectionsSource(uint8_t source) +//---------------------------------------- +// Enable all the valid messages for this platform over the USB port +// Add 2 to every UART1 key. This is brittle and non-perfect, but works. +//---------------------------------------- +bool ZED::setMessagesUsb(int maxRetries) { - if (!online.gnss) - return; + bool success = false; - if (!present.gnss_zedf9p) - return; + if (online.gnss) + { + int tryNo = -1; - if (zedCorrectionsSource == source) - return; + // Try up to maxRetries times to configure the messages + // This corrects occasional failures seen on the Reference Station where the GNSS is connected via SPI + // instead of I2C and UART1. I believe the SETVAL ACK is occasionally missed due to the level of messages being + // processed. + while ((++tryNo < maxRetries) && !success) + { + bool response = true; + int messageNumber = 0; - // This is important. Retry if needed - int retries = 0; - while ((!theGNSS->setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, source)) && (retries < 3)) - retries++; - if (retries < 3) + while (messageNumber < MAX_UBX_MSG) + { + response &= _zed->newCfgValset(); + + do + { + if (messageSupported(messageNumber)) + response &= _zed->addCfgValset(ubxMessages[messageNumber].msgConfigKey + 2, + settings.ubxMessageRates[messageNumber]); + messageNumber++; + } while (((messageNumber % 43) < 42) && + (messageNumber < MAX_UBX_MSG)); // Limit 1st batch to 42. Batches after that will be (up to) 43 in + // size. It's a HHGTTG thing. + + response &= _zed->sendCfgValset(); + } + + if (response) + success = true; + } + } + return (success); +} + +//---------------------------------------- +// Set the minimum satellite signal level for navigation. +//---------------------------------------- +bool ZED::setMinCnoRadio (uint8_t cnoValue) +{ + if (online.gnss) { - zedCorrectionsSource = source; - if (settings.debugCorrections == true && !inMainMenu) - systemPrintf("ZED UBLOX_CFG_SPARTN_USE_SOURCE changed to %d\r\n", source); + _zed->setVal8(UBLOX_CFG_NAVSPG_INFIL_MINCNO, cnoValue); + return true; } - else - systemPrintf("updateZEDCorrectionsSource(%d) failed!\r\n", source); + return false; } -uint8_t zedGetActiveMessageCount() +//---------------------------------------- +// Set the dynamic model to use for RTK +//---------------------------------------- +bool ZED::setModel(uint8_t modelNumber) { - uint8_t count = 0; + if (online.gnss) + { + _zed->setVal8(UBLOX_CFG_NAVSPG_DYNMODEL, (dynModel)modelNumber); // Set dynamic model + return true; + } + return false; +} - for (int x = 0; x < MAX_UBX_MSG; x++) - if (settings.ubxMessageRates[x] > 0) - count++; - return (count); +//---------------------------------------- +bool ZED::setRadioBaudRate(uint32_t baud) +{ + if (online.gnss) + return _zed->setVal32(UBLOX_CFG_UART2_BAUDRATE, baud); + return false; } -// Control the messages that get broadcast over Bluetooth and logged (if enabled) -void zedMenuMessages() +//---------------------------------------- +// Given the number of seconds between desired solution reports, determine measurementRateMs and navigationRate +// measurementRateS > 25 & <= 65535 +// navigationRate >= 1 && <= 127 +// We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. +//---------------------------------------- +bool ZED::setRate(double secondsBetweenSolutions) { - while (1) + uint16_t measRate = 0; // Calculate these locally and then attempt to apply them to ZED at completion + uint16_t navRate = 0; + + if (online.gnss == false) + return (false); + + // If we have more than an hour between readings, increase mesaurementRate to near max of 65,535 + if (secondsBetweenSolutions > 3600.0) { - systemPrintln(); - systemPrintln("Menu: GNSS Messages"); + measRate = 65000; + } - systemPrintf("Active messages: %d\r\n", gnss->getActiveMessageCount()); + // If we have more than 30s, but less than 3600s between readings, use 30s measurement rate + else if (secondsBetweenSolutions > 30.0) + { + measRate = 30000; + } - systemPrintln("1) Set NMEA Messages"); - systemPrintln("2) Set RTCM Messages"); - systemPrintln("3) Set RXM Messages"); - systemPrintln("4) Set NAV Messages"); - systemPrintln("5) Set NAV2 Messages"); - systemPrintln("6) Set NMEA NAV2 Messages"); - systemPrintln("7) Set MON Messages"); - systemPrintln("8) Set TIM Messages"); - systemPrintln("9) Set PUBX Messages"); + // User wants measurements less than 30s (most common), set measRate to match user request + // This will make navRate = 1. + else + { + measRate = secondsBetweenSolutions * 1000.0; + } - systemPrintln("10) Reset to Surveying Defaults (NMEAx5)"); - systemPrintln("11) Reset to PPP Logging Defaults (NMEAx5 + RXMx2)"); - systemPrintln("12) Turn off all messages"); - systemPrintln("13) Turn on all messages"); + navRate = secondsBetweenSolutions * 1000.0 / measRate; // Set navRate to nearest int value + measRate = secondsBetweenSolutions * 1000.0 / navRate; // Adjust measurement rate to match actual navRate - systemPrintln("x) Exit"); + // systemPrintf("measurementRate / navRate: %d / %d\r\n", measRate, navRate); - int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long + bool response = true; + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_MEAS, measRate); + response &= _zed->addCfgValset(UBLOX_CFG_RATE_NAV, navRate); - if (incoming == 1) - zedMenuMessagesSubtype(settings.ubxMessageRates, - "NMEA_"); // The following _ avoids listing NMEANAV2 messages - else if (incoming == 2) - zedMenuMessagesSubtype(settings.ubxMessageRates, "RTCM"); - else if (incoming == 3) - zedMenuMessagesSubtype(settings.ubxMessageRates, "RXM"); - else if (incoming == 4) - zedMenuMessagesSubtype(settings.ubxMessageRates, "NAV_"); // The following _ avoids listing NAV2 messages - else if (incoming == 5) - zedMenuMessagesSubtype(settings.ubxMessageRates, "NAV2"); - else if (incoming == 6) - zedMenuMessagesSubtype(settings.ubxMessageRates, "NMEANAV2"); - else if (incoming == 7) - zedMenuMessagesSubtype(settings.ubxMessageRates, "MON"); - else if (incoming == 8) - zedMenuMessagesSubtype(settings.ubxMessageRates, "TIM"); - else if (incoming == 9) - zedMenuMessagesSubtype(settings.ubxMessageRates, "PUBX"); - else if (incoming == 10) - { - setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages - zedSetMessageRateByName("NMEA_GGA", 1); - zedSetMessageRateByName("NMEA_GSA", 1); - zedSetMessageRateByName("NMEA_GST", 1); + int gsvRecordNumber = getMessageNumberByName("NMEA_GSV"); - // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; - if (measurementFrequency < 1.0) - measurementFrequency = 1.0; - zedSetMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second + // If enabled, adjust GSV NMEA to be reported at 1Hz to avoid swamping SPP connection + if (settings.ubxMessageRates[gsvRecordNumber] > 0) + { + float measurementFrequency = (1000.0 / measRate) / navRate; + if (measurementFrequency < 1.0) + measurementFrequency = 1.0; - zedSetMessageRateByName("NMEA_RMC", 1); - systemPrintln("Reset to Surveying Defaults (NMEAx5)"); - } - else if (incoming == 11) - { - setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages - zedSetMessageRateByName("NMEA_GGA", 1); - zedSetMessageRateByName("NMEA_GSA", 1); - zedSetMessageRateByName("NMEA_GST", 1); + log_d("Adjusting GSV setting to %f", measurementFrequency); - // We want GSV NMEA to be reported at 1Hz to avoid swamping SPP connection - float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; - if (measurementFrequency < 1.0) - measurementFrequency = 1.0; - zedSetMessageRateByName("NMEA_GSV", measurementFrequency); // One report per second + setMessageRateByName("NMEA_GSV", measurementFrequency); // Update GSV setting in file + response &= _zed->addCfgValset(ubxMessages[gsvRecordNumber].msgConfigKey, + settings.ubxMessageRates[gsvRecordNumber]); // Update rate on module + } - zedSetMessageRateByName("NMEA_RMC", 1); + response &= _zed->sendCfgValset(); // Closing value - max 4 pairs - zedSetMessageRateByName("RXM_RAWX", 1); - zedSetMessageRateByName("RXM_SFRBX", 1); - systemPrintln("Reset to PPP Logging Defaults (NMEAx5 + RXMx2)"); - } - else if (incoming == 12) - { - setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages - systemPrintln("All messages disabled"); - } - else if (incoming == 13) - { - setGNSSMessageRates(settings.ubxMessageRates, 1); // Turn on all messages to report once per fix - systemPrintln("All messages enabled"); - } - else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) - break; - else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) - break; - else - printUnknown(incoming); + // If we successfully set rates, only then record to settings + if (response) + { + settings.measurementRateMs = measRate; + settings.navigationRate = navRate; } - - clearBuffer(); // Empty buffer of any newline chars - - // Make sure the appropriate messages are enabled - bool response = gnss->setMessages(MAX_SET_MESSAGES_RETRIES); // Does a complete open/closed val set - if (response == false) - systemPrintf("menuMessages: Failed to enable messages - after %d tries", MAX_SET_MESSAGES_RETRIES); else - systemPrintln("menuMessages: Messages successfully enabled"); + { + systemPrintln("Failed to set measurement and navigation rates"); + return (false); + } - setLoggingType(); // Update Standard, PPP, or custom for icon selection + return (true); } -// Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) -void zedBaseRtcmDefault() +//---------------------------------------- +bool ZED::setTalkerGNGGA() { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 1; // 1105 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1074") - firstRTCMRecord] = 1; // 1074 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1077") - firstRTCMRecord] = 0; // 1077 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1084") - firstRTCMRecord] = 1; // 1084 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1087") - firstRTCMRecord] = 0; // 1087 - - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1094") - firstRTCMRecord] = 1; // 1094 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1097") - firstRTCMRecord] = 0; // 1097 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1124") - firstRTCMRecord] = 1; // 1124 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1127") - firstRTCMRecord] = 0; // 1127 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1230") - firstRTCMRecord] = 10; // 1230 + if (online.gnss) + { + bool success = true; + success &= _zed->setVal8(UBLOX_CFG_NMEA_MAINTALKERID, 3); // Return talker ID to GNGGA after NTRIP Client set to GPGGA + success &= _zed->setNMEAGPGGAcallbackPtr(nullptr); // Remove callback + return success; + } + return false; +} - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_4072_0") - firstRTCMRecord] = 0; // 4072_0 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_4072_1") - firstRTCMRecord] = 0; // 4072_1 +//---------------------------------------- +// Hotstart GNSS to try to get RTK lock +//---------------------------------------- +bool ZED::softwareReset() +{ + if (online.gnss == false) + return false; + _zed->softwareResetGNSSOnly(); + return true; } -// Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) -void zedBaseRtcmLowDataRate() +//---------------------------------------- +bool ZED::standby() { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 10; // 1105 0.1Hz - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1074") - firstRTCMRecord] = 2; // 1074 0.5Hz - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1077") - firstRTCMRecord] = 0; // 1077 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1084") - firstRTCMRecord] = 2; // 1084 0.5Hz - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1087") - firstRTCMRecord] = 0; // 1087 + return true; // TODO - this would be a perfect place for Save-On-Shutdown +} - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1094") - firstRTCMRecord] = 2; // 1094 0.5Hz - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1097") - firstRTCMRecord] = 0; // 1097 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1124") - firstRTCMRecord] = 2; // 1124 0.5Hz - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1127") - firstRTCMRecord] = 0; // 1127 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_1230") - firstRTCMRecord] = 10; // 1230 0.1Hz +//---------------------------------------- +// Callback to save the high precision data +// Inputs: +// ubxDataStruct: Address of an UBX_NAV_HPPOSLLH_data_t structure +// containing the high precision position data +//---------------------------------------- +void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) +{ + ZED * zed = (ZED *)gnss; - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_4072_0") - firstRTCMRecord] = 0; // 4072_0 - settings.ubxMessageRatesBase[zedGetMessageNumberByName("RTCM_4072_1") - firstRTCMRecord] = 0; // 4072_1 + zed->storeHPdataRadio(ubxDataStruct); } -char *zedGetRtcmDefaultString() +//---------------------------------------- +// Callback to save the high precision data +//---------------------------------------- +void ZED::storeHPdataRadio(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) { - return ((char *)"1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz"); + double latitude; + double longitude; + + latitude = ((double)ubxDataStruct->lat) / 10000000.0; + latitude += ((double)ubxDataStruct->latHp) / 1000000000.0; + longitude = ((double)ubxDataStruct->lon) / 10000000.0; + longitude += ((double)ubxDataStruct->lonHp) / 1000000000.0; + + _horizontalAccuracy = ((float)ubxDataStruct->hAcc) / 10000.0; // Convert hAcc from mm*0.1 to m + _latitude = latitude; + _longitude = longitude; } -char *zedGetRtcmLowDataRateString() + +//---------------------------------------- +// Callback to save aStatus +//---------------------------------------- +void storeMONHWdata(UBX_MON_HW_data_t *ubxDataStruct) { - return ((char *)"1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz"); + aStatus = ubxDataStruct->aStatus; } -int ubxConstellationIDToIndex(int id) +//---------------------------------------- +// Callback to save the PVT data +// Inputs: +// ubxDataStruct: Address of an UBX_NAV_PVT_data_t structure +// containing the time, date and satellite data +//---------------------------------------- +void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct) { - for (int x = 0; x < MAX_UBX_CONSTELLATIONS; x++) - { - if (settings.ubxConstellations[x].gnssID == id) - return x; - } + ZED * zed = (ZED *)gnss; - return -1; // Should never be reached...! + zed->storePVTdataRadio(ubxDataStruct); } -// Controls the constellations that are used to generate a fix and logged -void zedMenuConstellations() +//---------------------------------------- +// Callback to save the PVT data +//---------------------------------------- +void ZED::storePVTdataRadio(UBX_NAV_PVT_data_t *ubxDataStruct) { - while (1) - { - systemPrintln(); - systemPrintln("Menu: Constellations"); - - for (int x = 0; x < MAX_UBX_CONSTELLATIONS; x++) - { - systemPrintf("%d) Constellation %s: ", x + 1, settings.ubxConstellations[x].textName); - if (settings.ubxConstellations[x].enabled == true) - systemPrint("Enabled"); - else - systemPrint("Disabled"); - systemPrintln(); - } + _altitude = ubxDataStruct->height / 1000.0; - systemPrintln("x) Exit"); + _day = ubxDataStruct->day; + _month = ubxDataStruct->month; + _year = ubxDataStruct->year; - int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long + _hour = ubxDataStruct->hour; + _minute = ubxDataStruct->min; + _second = ubxDataStruct->sec; + _nanosecond = ubxDataStruct->nano; + _millisecond = ceil((ubxDataStruct->iTOW % 1000) / 10.0); // Limit to first two digits - if (incoming >= 1 && incoming <= MAX_UBX_CONSTELLATIONS) - { - incoming--; // Align choice to constellation array of 0 to 5 + _satellitesInView = ubxDataStruct->numSV; + _fixType = ubxDataStruct->fixType; // 0 = no fix, 1 = dead reckoning only, 2 = 2D-fix, 3 = 3D-fix, 4 = GNSS + dead + // reckoning combined, 5 = time only fix + _carrierSolution = ubxDataStruct->flags.bits.carrSoln; - settings.ubxConstellations[incoming].enabled ^= 1; + _validDate = ubxDataStruct->valid.bits.validDate; + _validTime = ubxDataStruct->valid.bits.validTime; + _confirmedDate = ubxDataStruct->flags2.bits.confirmedDate; + _confirmedTime = ubxDataStruct->flags2.bits.confirmedTime; + _fullyResolved = ubxDataStruct->valid.bits.fullyResolved; + _tAcc = ubxDataStruct->tAcc; // Nanoseconds - // 3.10.6: To avoid cross-correlation issues, it is recommended that GPS and QZSS are always both enabled or - // both disabled. - if (incoming == ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS)) // Match QZSS to GPS - { - settings.ubxConstellations[ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_QZSS)].enabled = - settings.ubxConstellations[incoming].enabled; - } - if (incoming == ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_QZSS)) // Match GPS to QZSS - { - settings.ubxConstellations[ubxConstellationIDToIndex(SFE_UBLOX_GNSS_ID_GPS)].enabled = - settings.ubxConstellations[incoming].enabled; - } - } - else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) - break; - else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) - break; - else - printUnknown(incoming); - } + _pvtArrivalMillis = millis(); + _pvtUpdated = true; +} - // Apply current settings to module - gnss->setConstellations(); +//---------------------------------------- +// Callback to save ARPECEF* +//---------------------------------------- +void storeRTCM1005data(RTCM_1005_data_t *rtcmData1005) +{ + ARPECEFX = rtcmData1005->AntennaReferencePointECEFX; + ARPECEFY = rtcmData1005->AntennaReferencePointECEFY; + ARPECEFZ = rtcmData1005->AntennaReferencePointECEFZ; + ARPECEFH = 0; + newARPAvailable = true; +} - clearBuffer(); // Empty buffer of any newline chars +//---------------------------------------- +// Callback to save ARPECEF* +//---------------------------------------- +void storeRTCM1006data(RTCM_1006_data_t *rtcmData1006) +{ + ARPECEFX = rtcmData1006->AntennaReferencePointECEFX; + ARPECEFY = rtcmData1006->AntennaReferencePointECEFY; + ARPECEFZ = rtcmData1006->AntennaReferencePointECEFZ; + ARPECEFH = rtcmData1006->AntennaHeight; + newARPAvailable = true; } -// Given a unique string, find first and last records containing that string in message array -void zedSetMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock) +//---------------------------------------- +void storeTIMTPdata(UBX_TIM_TP_data_t *ubxDataStruct) { - if (present.gnss_zedf9p) - { - char messageNamePiece[40]; // UBX_RTCM - snprintf(messageNamePiece, sizeof(messageNamePiece), "%s", messageType); // Put UBX_ infront of type + uint32_t tow = ubxDataStruct->week - SFE_UBLOX_JAN_1ST_2020_WEEK; // Calculate the number of weeks since Jan 1st + // 2020 + tow *= SFE_UBLOX_SECS_PER_WEEK; // Convert weeks to seconds + tow += SFE_UBLOX_EPOCH_WEEK_2086; // Add the TOW for Jan 1st 2020 + tow += ubxDataStruct->towMS / 1000; // Add the TOW for the next TP - // Find the first occurrence - for (startOfBlock = 0; startOfBlock < MAX_UBX_MSG; startOfBlock++) - { - if (strstr(localMessage[startOfBlock].msgTextName, messageNamePiece) != nullptr) - break; - } - if (startOfBlock == MAX_UBX_MSG) - { - // Error out - startOfBlock = 0; - endOfBlock = 0; - return; - } + uint32_t us = ubxDataStruct->towMS % 1000; // Extract the milliseconds + us *= 1000; // Convert to microseconds - // Find the last occurrence - for (endOfBlock = startOfBlock + 1; endOfBlock < MAX_UBX_MSG; endOfBlock++) - { - if (strstr(localMessage[endOfBlock].msgTextName, messageNamePiece) == nullptr) - break; - } - } - else - systemPrintln("zedSetMessageOffsets() Platform not supported"); + double subMS = ubxDataStruct->towSubMS; // Get towSubMS (ms * 2^-32) + subMS *= pow(2.0, -32.0); // Convert to milliseconds + subMS *= 1000; // Convert to microseconds + + us += (uint32_t)subMS; // Add subMS + + timTpEpoch = tow; + timTpMicros = us; + timTpArrivalMillis = millis(); + timTpUpdated = true; } -// Count the number of NAV2 messages with rates more than 0. Used for determining if we need the enable -// the global NAV2 feature. -uint8_t zedGetNAV2MessageCount() +//---------------------------------------- +// Slightly modified method for restarting survey-in from: +// https://portal.u-blox.com/s/question/0D52p00009IsVoMCAV/restarting-surveyin-on-an-f9p +//---------------------------------------- +bool ZED::surveyInReset() { - if (present.gnss_zedf9p) - { - int enabledMessages = 0; - int startOfBlock = 0; - int endOfBlock = 0; - - zedSetMessageOffsets(&ubxMessages[0], "NAV2", startOfBlock, - endOfBlock); // Find start and stop of given messageType in message array + bool response = true; - for (int x = 0; x < (endOfBlock - startOfBlock); x++) - { - if (settings.ubxMessageRates[x + startOfBlock] > 0) - enabledMessages++; - } + if (online.gnss == false) + return (false); - zedSetMessageOffsets(&ubxMessages[0], "NMEANAV2", startOfBlock, - endOfBlock); // Find start and stop of given messageType in message array + // Disable survey-in mode + response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0); + delay(1000); - for (int x = 0; x < (endOfBlock - startOfBlock); x++) - { - if (settings.ubxMessageRates[x + startOfBlock] > 0) - enabledMessages++; - } + // Enable Survey in with bogus values + response &= _zed->newCfgValset(); + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_MODE, 1); // Survey-in enable + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, 40 * 10000); // 40.0m + response &= _zed->addCfgValset(UBLOX_CFG_TMODE_SVIN_MIN_DUR, 1000); // 1000s + response &= _zed->sendCfgValset(); + delay(1000); - return (enabledMessages); - } - else - systemPrintln("zedGetNAV2MessageCount() Platform not supported"); + // Disable survey-in mode + response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0); - return (false); -} + if (response == false) + return (response); -// Given the name of a message, find it, and set the rate -bool zedSetMessageRateByName(const char *msgName, uint8_t msgRate) -{ - if (present.gnss_zedf9p) + // Wait until active and valid becomes false + long maxTime = 5000; + long startTime = millis(); + while (_zed->getSurveyInActive(100) || _zed->getSurveyInValid(100)) { - for (int x = 0; x < MAX_UBX_MSG; x++) - { - if (strcmp(ubxMessages[x].msgTextName, msgName) == 0) - { - settings.ubxMessageRates[x] = msgRate; - return (true); - } - } + delay(100); + if (millis() - startTime > maxTime) + return (false); // Reset of survey failed } - else - systemPrintln("zedSetMessageRateByName() Platform not supported"); - systemPrintf("zedSetMessageRateByName: %s not found\r\n", msgName); - return (false); + return (true); } -// Given the name of a message, find it, and return the rate -uint8_t zedGetMessageRateByName(const char *msgName) +//---------------------------------------- +// Start the survey-in operation +// The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. +//---------------------------------------- +bool ZED::surveyInStart() { - if (present.gnss_zedf9p) - { - return (settings.ubxMessageRates[zedGetMessageNumberByName(msgName)]); - } - else - systemPrintln("zedGetMessageRateByName() Platform not supported"); + if (online.gnss == false) + return (false); - return (0); -} + _zed->setVal8(UBLOX_CFG_TMODE_MODE, 0); // Disable survey-in mode + delay(100); -// Given the name of a message, return the array number -uint8_t zedGetMessageNumberByName(const char *msgName) -{ - if (present.gnss_zedf9p) + bool needSurveyReset = false; + if (_zed->getSurveyInActive(100)) + needSurveyReset = true; + if (_zed->getSurveyInValid(100)) + needSurveyReset = true; + + if (needSurveyReset) { - for (int x = 0; x < MAX_UBX_MSG; x++) + systemPrintln("Resetting survey"); + + if (surveyInReset() == false) { - if (strcmp(ubxMessages[x].msgTextName, msgName) == 0) - return (x); + systemPrintln("Survey reset failed - attempt 1/3"); + if (surveyInReset() == false) + { + systemPrintln("Survey reset failed - attempt 2/3"); + if (surveyInReset() == false) + { + systemPrintln("Survey reset failed - attempt 3/3"); + } + } } } - else - systemPrintln("zedGetMessageNumberByName() Platform not supported"); - systemPrintf("zedGetMessageNumberByName: %s not found\r\n", msgName); - return (0); -} + bool response = true; + response &= _zed->setVal8(UBLOX_CFG_TMODE_MODE, 1); // Survey-in enable + response &= _zed->setVal32(UBLOX_CFG_TMODE_SVIN_ACC_LIMIT, settings.observationPositionAccuracy * 10000); + response &= _zed->setVal32(UBLOX_CFG_TMODE_SVIN_MIN_DUR, settings.observationSeconds); -uint32_t zedGetRadioBaudRate() -{ - if (present.gnss_zedf9p) + if (response == false) { - return theGNSS->getVal32(UBLOX_CFG_UART2_BAUDRATE); + systemPrintln("Survey start failed"); + return (false); } - return (0); -} + systemPrintf("Survey started. This will run until %d seconds have passed and less than %0.03f meter accuracy is " + "achieved.\r\n", + settings.observationSeconds, settings.observationPositionAccuracy); -bool zedSetRadioBaudRate(uint32_t baud) -{ - if (present.gnss_zedf9p) + // Wait until active becomes true + long maxTime = 5000; + long startTime = millis(); + while (_zed->getSurveyInActive(100) == false) { - return theGNSS->setVal32(UBLOX_CFG_UART2_BAUDRATE, baud); + delay(100); + if (millis() - startTime > maxTime) + return (false); // Reset of survey failed } - return false; + return (true); } -uint32_t zedGetDataBaudRate() +//---------------------------------------- +int ZED::ubxConstellationIDToIndex(int id) { - if (present.gnss_zedf9p) + for (int x = 0; x < MAX_UBX_CONSTELLATIONS; x++) { - return theGNSS->getVal32(UBLOX_CFG_UART1_BAUDRATE); + if (settings.ubxConstellations[x].gnssID == id) + return x; } - return (0); + return -1; // Should never be reached...! } -bool zedSetDataBaudRate(uint32_t baud) +//---------------------------------------- +void ZED::unlock(void) { - if (present.gnss_zedf9p) - { - return theGNSS->setVal32(UBLOX_CFG_UART1_BAUDRATE, baud); - } - - return false; + iAmLocked = false; } -bool zedCheckGnssNMEARates() +//---------------------------------------- +// Poll routine to update the GNSS state +//---------------------------------------- +void ZED::update() { - if (present.gnss_zedf9p) + if (online.gnss) { - return (zedGetMessageRateByName("NMEA_GGA") > 0 && zedGetMessageRateByName("NMEA_GSA") > 0 && - zedGetMessageRateByName("NMEA_GST") > 0 && zedGetMessageRateByName("NMEA_GSV") > 0 && - zedGetMessageRateByName("NMEA_RMC") > 0); + _zed->checkUblox(); // Regularly poll to get latest data and any RTCM + _zed->checkCallbacks(); // Process any callbacks: ie, eventTriggerReceived } - - return false; } -bool zedCheckGnssPPPRates() +//---------------------------------------- +void ZED::updateCorrectionsSource(uint8_t source) { - if (present.gnss_zedf9p) + if (!online.gnss) + return; + + if (zedCorrectionsSource == source) + return; + + // This is important. Retry if needed + int retries = 0; + while ((!_zed->setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, source)) && (retries < 3)) + retries++; + if (retries < 3) { - return (zedGetMessageRateByName("RXM_RAWX") > 0 && zedGetMessageRateByName("RXM_SFRBX") > 0); + zedCorrectionsSource = source; + if (settings.debugCorrections && !inMainMenu) + systemPrintf("ZED UBLOX_CFG_SPARTN_USE_SOURCE changed to %d\r\n", source); } - - return false; + else + systemPrintf("updateZEDCorrectionsSource(%d) failed!\r\n", source); } diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index edb755c17..03ac9e461 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -713,7 +713,8 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting } break; case tUbMsgRtb: { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) { @@ -1468,7 +1469,8 @@ void createSettingsString(char *newSettings) case tUbMsgRtb: { // Record message settings - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { @@ -2243,7 +2245,8 @@ SettingValueResponse getSettingValue(bool inCommands, const char *settingName, c } break; case tUbMsgRtb: { - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) { @@ -2739,7 +2742,8 @@ void commandList(bool inCommands, int i) break; case tUbMsgRtb: { // Record message settings - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) { diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino index d471a9308..47a366223 100644 --- a/Firmware/RTK_Everywhere/menuMessages.ino +++ b/Firmware/RTK_Everywhere/menuMessages.ino @@ -203,15 +203,16 @@ void zedMenuMessagesSubtype(uint8_t *localMessageRate, const char *messageType) int endOfBlock = 0; int rtcmOffset = 0; // Used to offset messageSupported lookup + ZED * zed = (ZED *)gnss; if (strcmp(messageType, "RTCM-Base") == 0) // The ubxMessageRatesBase array is 0 to MAX_UBX_MSG_RTCM - 1 { startOfBlock = 0; endOfBlock = MAX_UBX_MSG_RTCM; - rtcmOffset = zedGetMessageNumberByName("RTCM_1005"); + rtcmOffset = zed->getMessageNumberByName("RTCM_1005"); } else - zedSetMessageOffsets(&ubxMessages[0], messageType, startOfBlock, - endOfBlock); // Find start and stop of given messageType in message array + zed->setMessageOffsets(&ubxMessages[0], messageType, startOfBlock, + endOfBlock); // Find start and stop of given messageType in message array for (int x = 0; x < (endOfBlock - startOfBlock); x++) { @@ -624,7 +625,8 @@ void checkGNSSArrayDefaults() defaultsApplied = true; // Reset Base rates to defaults - int firstRTCMRecord = zedGetMessageNumberByName("RTCM_1005"); + ZED * zed = (ZED *)gnss; + int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) settings.ubxMessageRatesBase[x] = ubxMessages[firstRTCMRecord + x].msgDefaultRate; } @@ -778,25 +780,26 @@ void setLogTestFrequencyMessages(int rate, int messages) // Set messages setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages + ZED * zed = (ZED *)gnss; if (messages == 5) { - zedSetMessageRateByName("NMEA_GGA", 1); - zedSetMessageRateByName("NMEA_GSA", 1); - zedSetMessageRateByName("NMEA_GST", 1); - zedSetMessageRateByName("NMEA_GSV", rate); // One report per second - zedSetMessageRateByName("NMEA_RMC", 1); + zed->setMessageRateByName("NMEA_GGA", 1); + zed->setMessageRateByName("NMEA_GSA", 1); + zed->setMessageRateByName("NMEA_GST", 1); + zed->setMessageRateByName("NMEA_GSV", rate); // One report per second + zed->setMessageRateByName("NMEA_RMC", 1); log_d("Messages: Surveying Defaults (NMEAx5)"); } else if (messages == 7) { - zedSetMessageRateByName("NMEA_GGA", 1); - zedSetMessageRateByName("NMEA_GSA", 1); - zedSetMessageRateByName("NMEA_GST", 1); - zedSetMessageRateByName("NMEA_GSV", rate); // One report per second - zedSetMessageRateByName("NMEA_RMC", 1); - zedSetMessageRateByName("RXM_RAWX", 1); - zedSetMessageRateByName("RXM_SFRBX", 1); + zed->setMessageRateByName("NMEA_GGA", 1); + zed->setMessageRateByName("NMEA_GSA", 1); + zed->setMessageRateByName("NMEA_GST", 1); + zed->setMessageRateByName("NMEA_GSV", rate); // One report per second + zed->setMessageRateByName("NMEA_RMC", 1); + zed->setMessageRateByName("RXM_RAWX", 1); + zed->setMessageRateByName("RXM_SFRBX", 1); log_d("Messages: PPP NMEAx5+RXMx2"); } diff --git a/Firmware/RTK_Everywhere/menuPP.ino b/Firmware/RTK_Everywhere/menuPP.ino index b2fe68143..f44babd44 100644 --- a/Firmware/RTK_Everywhere/menuPP.ino +++ b/Firmware/RTK_Everywhere/menuPP.ino @@ -727,7 +727,8 @@ void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData) if (correctionLastSeen(CORR_LBAND)) { - updateZEDCorrectionsSource(1); // Set SOURCE to 1 (L-Band) if needed + ZED * zed = (ZED *)gnss; + zed->updateCorrectionsSource(1); // Set SOURCE to 1 (L-Band) if needed if (settings.debugCorrections == true && !inMainMenu) systemPrintf("Pushing %d bytes of RXM-PMP data to GNSS\r\n", payloadLen); @@ -854,7 +855,8 @@ void beginLBand() response &= i2cLBand.sendCfgValset(); - response &= zedEnableLBandCommunication(); + ZED * zed = (ZED *)gnss; + response &= zed->lBandCommunicationEnable(); if (response == false) systemPrintln("L-Band failed to configure"); From e71998e4f9b8f2e5543c685b8be6fd9aa2f64059 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Sun, 22 Sep 2024 13:19:02 -1000 Subject: [PATCH 3/5] Implement the RTK_UM980 class --- Firmware/RTK_Everywhere/Begin.ino | 3 + Firmware/RTK_Everywhere/RTK_Everywhere.ino | 12 +- Firmware/RTK_Everywhere/UM980.h | 378 +++- Firmware/RTK_Everywhere/UM980.ino | 1882 +++++++++++++------- 4 files changed, 1624 insertions(+), 651 deletions(-) diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 7ae0b5ccd..a2d6ab725 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -189,6 +189,9 @@ void beginBoard() } else if (productVariant == RTK_TORCH) { + // Specify the GNSS radio + gnss = (GNSS *) new RTK_UM980(); + present.psram_2mb = true; present.gnss_um980 = true; present.radio_lora = true; diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 96986f372..4c819ab85 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -376,12 +376,8 @@ bool usbSerialIncomingRtcm; // Incoming RTCM over the USB serial port #define RTCM_CORRECTION_INPUT_TIMEOUT (2 * 1000) //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -// GNSS configuration - UM980 +// Extensible Message Parser //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= -#ifdef COMPILE_UM980 -#include //http://librarymanager/All#SparkFun_Unicore_GNSS -#endif // COMPILE_UM980 - #include //http://librarymanager/All#SparkFun_Extensible_Message_Parser SEMP_PARSE_STATE *rtkParse = nullptr; SEMP_PARSE_STATE *sbfParse = nullptr; // mosaic-X5 @@ -1007,9 +1003,9 @@ void setup() if (checkUpdateLoraFirmware() == true) // Check if updateLoraFirmware.txt exists beginLoraFirmwareUpdate(); - DMW_b("checkUpdateUm980Firmware"); - if (checkUpdateUm980Firmware() == true) // Check if updateLoraFirmware.txt exists - beginUm980FirmwareUpdate(); + DMW_b("um980FirmwareCheckUpdate"); + if (um980FirmwareCheckUpdate() == true) // Check if updateUm980Firmware.txt exists + um980FirmwareBeginUpdate(); DMW_b("checkConfigureViaEthernet"); configureViaEthernet = diff --git a/Firmware/RTK_Everywhere/UM980.h b/Firmware/RTK_Everywhere/UM980.h index 54b375ca1..c3a1c00ae 100644 --- a/Firmware/RTK_Everywhere/UM980.h +++ b/Firmware/RTK_Everywhere/UM980.h @@ -1,6 +1,16 @@ +/*------------------------------------------------------------------------------ +UM980.h + + Declarations and definitions for the UM980 GNSS receiver and the RTK_UM980 class +------------------------------------------------------------------------------*/ + #ifndef _RTK_EVERYWHERE_UM980_H #define _RTK_EVERYWHERE_UM980_H +#ifdef COMPILE_UM980 + +#include //http://librarymanager/All#SparkFun_Unicore_GNSS + /* Unicore defaults: RTCM1006 10 @@ -91,4 +101,370 @@ enum um980_Models UM980_DYN_MODEL_AUTOMOTIVE, }; -#endif +class RTK_UM980 : GNSS +{ + private: + + UM980 * _um980; // Library class instance + + protected: + + bool configureOnce(); + + // Setup the general configuration of the GNSS + // Not Rover or Base specific (ie, baud rates) + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureRadio(); + + // Turn off all NMEA and RTCM + void disableAllOutput(); + + // Disable all output, then re-enable + void disableRTCM(); + + // Turn on all the enabled NMEA messages on COM3 + bool enableNMEA(); + + // Turn on all the enabled RTCM Rover messages on COM3 + bool enableRTCMRover(); + + // Turn on all the enabled RTCM Base messages on COM3 + bool enableRTCMBase(); + + uint8_t getActiveNmeaMessageCount(); + + uint8_t getActiveRtcmMessageCount(); + + // Given the name of an NMEA message, return the array number + uint8_t getNmeaMessageNumberByName(const char *msgName); + + // Given the name of an RTCM message, return the array number + uint8_t getRtcmMessageNumberByName(const char *msgName); + + // Returns true if the device is in Rover mode + // Currently the only two modes are Rover or Base + bool inRoverMode(); + + // Return true if the GPGGA message is active + bool isGgaActive(); + + // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype + // Controls the messages that get broadcast over Bluetooth and logged (if enabled) + void menuMessagesSubtype(float *localMessageRate, const char *messageType); + + // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS + // Inputs: + // baudRate: The desired baudrate + bool setBaudRateCOM3(uint32_t baudRate); + + bool setHighAccuracyService(bool enableGalileoHas); + + // Set the minimum satellite signal level for navigation. + bool setMinCnoRadio (uint8_t cnoValue); + + bool setMultipathMitigation(bool enableMultipathMitigation); + + public: + + // If we have decryption keys, configure module + // Note: don't check online.lband_neo here. We could be using ip corrections + void applyPointPerfectKeys(); + + // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) + void baseRtcmDefault(); + + // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) + void baseRtcmLowDataRate(); + + // Connect to GNSS and identify particulars + void begin(); + + // Setup TM2 time stamp input as need + // Outputs: + // Returns true when an external event occurs and false if no event + bool beginExternalEvent(); + + // Setup the timepulse output on the PPS pin for external triggering + // Outputs + // Returns true if the pin was successfully setup and false upon + // failure + bool beginPPS(); + + bool checkNMEARates(); + + bool checkPPPRates(); + + // Configure the Base + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureBase(); + + // Configure specific aspects of the receiver for NTP mode + bool configureNtpMode(); + + // Configure the Rover + // Outputs: + // Returns true if successfully configured and false upon failure + bool configureRover(); + + void debuggingDisable(); + + void debuggingEnable(); + + void enableGgaForNtrip(); + + // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted + // even if there is no GPS fix. We use it to test serial output. + bool enableRTCMTest(); + + // Restore the GNSS to the factory settings + void factoryReset(); + + uint16_t fileBufferAvailable(); + + uint16_t fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead); + + // Start the base using fixed coordinates + // Outputs: + // Returns true if successfully started and false upon failure + bool fixedBaseStart(); + + // Return the number of active/enabled messages + uint8_t getActiveMessageCount(); + + // Get the altitude + // Outputs: + // Returns the altitude in meters or zero if the GNSS is offline + double getAltitude(); + + // Returns the carrier solution or zero if not online + uint8_t getCarrierSolution(); + + uint32_t getDataBaudRate(); + + // Returns the day number or zero if not online + uint8_t getDay(); + + // Return the number of milliseconds since GNSS data was last updated + uint16_t getFixAgeMilliseconds(); + + // Returns the fix type or zero if not online + uint8_t getFixType(); + + // Returns the hours of 24 hour clock or zero if not online + uint8_t getHour(); + + // Get the horizontal position accuracy + // Outputs: + // Returns the horizontal position accuracy or zero if offline + float getHorizontalAccuracy(); + + const char * getId(); + + // Get the latitude value + // Outputs: + // Returns the latitude value or zero if not online + double getLatitude(); + + // Query GNSS for current leap seconds + uint8_t getLeapSeconds(); + + // Get the longitude value + // Outputs: + // Returns the longitude value or zero if not online + double getLongitude(); + + // Returns two digits of milliseconds or zero if not online + uint8_t getMillisecond(); + + // Get the minimum satellite signal level for navigation. + uint8_t getMinCno(); + + // Returns minutes or zero if not online + uint8_t getMinute(); + + // Returns month number or zero if not online + uint8_t getMonth(); + + // Returns nanoseconds or zero if not online + uint32_t getNanosecond(); + + uint32_t getRadioBaudRate(); + + // Returns the seconds between solutions + double getRateS(); + + const char * getRtcmDefaultString(); + + const char * getRtcmLowDataRateString(); + + // Returns the number of satellites in view or zero if offline + uint8_t getSatellitesInView(); + + // Returns seconds or zero if not online + uint8_t getSecond(); + + // Get the survey-in mean accuracy + // Outputs: + // Returns the mean accuracy or zero (0) + float getSurveyInMeanAccuracy(); + + // Return the number of seconds the survey-in process has been running + int getSurveyInObservationTime(); + + float getSurveyInStartingAccuracy(); + + // Returns timing accuracy or zero if not online + uint32_t getTimeAccuracy(); + + // Returns full year, ie 2023, not 23. + uint16_t getYear(); + + bool isBlocking(); + + // Date is confirmed once we have GNSS fix + bool isConfirmedDate(); + + // Date is confirmed once we have GNSS fix + bool isConfirmedTime(); + + // Return true if GNSS receiver has a higher quality DGPS fix than 3D + bool isDgpsFixed(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have a valid fix, not what type of fix + // This function checks to see if the given platform has reached + // sufficient fix type to be considered valid + bool isFixed(); + + // Used in tpISR() for time pulse synchronization + bool isFullyResolved(); + + bool isPppConverged(); + + bool isPppConverging(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Fix. This function checks to see if the + // given platform has reached sufficient fix type to be considered valid + bool isRTKFix(); + + // Some functions (L-Band area frequency determination) merely need + // to know if we have an RTK Float. This function checks to see if + // the given platform has reached sufficient fix type to be considered + // valid + bool isRTKFloat(); + + // Determine if the survey-in operation is complete + // Outputs: + // Returns true if the survey-in operation is complete and false + // if the operation is still running + bool isSurveyInComplete(); + + // Date will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidDate(); + + // Time will be valid if the RTC is reporting (regardless of GNSS fix) + bool isValidTime(); + + // Controls the constellations that are used to generate a fix and logged + void menuConstellations(); + + void menuMessageBaseRtcm(); + + // Control the messages that get broadcast over Bluetooth and logged (if enabled) + void menuMessages(); + + // Print the module type and firmware version + void printModuleInfo(); + + // Send correction data to the GNSS + // Inputs: + // dataToSend: Address of a buffer containing the data + // dataLength: The number of valid data bytes in the buffer + // Outputs: + // Returns the number of correction data bytes written + int pushRawData(uint8_t *dataToSend, int dataLength); + + uint16_t rtcmBufferAvailable(); + + // If LBand is being used, ignore any RTCM that may come in from the GNSS + void rtcmOnGnssDisable(); + + // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver + void rtcmOnGnssEnable(); + + uint16_t rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead); + + // Save the current configuration + // Outputs: + // Returns true when the configuration was saved and false upon failure + bool saveConfiguration(); + + // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS + // This just sets the GNSS side + // Used during Bluetooth testing + // Inputs: + // baudRate: The desired baudrate + bool setBaudrate(uint32_t baudRate); + + // Enable all the valid constellations and bands for this platform + bool setConstellations(); + + bool setDataBaudRate(uint32_t baud); + + // Set the elevation in degrees + // Inputs: + // elevationDegrees: The elevation value in degrees + bool setElevation(uint8_t elevationDegrees); + + // Enable all the valid messages for this platform + bool setMessages(int maxRetries); + + // Enable all the valid messages for this platform over the USB port + bool setMessagesUsb(int maxRetries); + + // Set the dynamic model to use for RTK + // Inputs: + // modelNumber: Number of the model to use, provided by radio library + bool setModel(uint8_t modelNumber); + + bool setRadioBaudRate(uint32_t baud); + + // Specify the interval between solutions + // Inputs: + // secondsBetweenSolutions: Number of seconds between solutions + // Outputs: + // Returns true if the rate was successfully set and false upon + // failure + bool setRate(double secondsBetweenSolutions); + + bool setTalkerGNGGA(); + + // Hotstart GNSS to try to get RTK lock + bool softwareReset(); + + bool standby(); + + // Reset the survey-in operation + // Outputs: + // Returns true if the survey-in operation was reset successfully + // and false upon failure + bool surveyInReset(); + + // Start the survey-in operation + // Outputs: + // Return true if successful and false upon failure + bool surveyInStart(); + + // If we have received serial data from the UM980 outside of the Unicore library (ie, from processUart1Message task) + // we can pass data back into the Unicore library to allow it to update its own variables + void unicoreHandler(uint8_t *buffer, int length); + + // Poll routine to update the GNSS state + void update(); +}; + +#endif // COMPILE_UM980 +#endif // _RTK_EVERYWHERE_UM980_H diff --git a/Firmware/RTK_Everywhere/UM980.ino b/Firmware/RTK_Everywhere/UM980.ino index 4cc6b2b16..eea2f52bd 100644 --- a/Firmware/RTK_Everywhere/UM980.ino +++ b/Firmware/RTK_Everywhere/UM980.ino @@ -1,19 +1,61 @@ -/* - IM19 reads in binary+NMEA from the UM980 and passes out binary with tilt-corrected lat/long/alt - to the ESP32. +/*------------------------------------------------------------------------------ +UM980.ino - The ESP32 reads in binary from the IM19. + Implementation of the RTK_UM980 class - The ESP32 reads in binary and NMEA from the UM980 and passes that data over Bluetooth. - If tilt compensation is activated, the ESP32 intercepts the NMEA from the UM980 and - injects the new tilt-compensated data, previously read from the IM19. -*/ + IM19 reads in binary+NMEA from the UM980 and passes out binary with tilt-corrected lat/long/alt + to the ESP32. + + The ESP32 reads in binary from the IM19. + + The ESP32 reads in binary and NMEA from the UM980 and passes that data over Bluetooth. + If tilt compensation is activated, the ESP32 intercepts the NMEA from the UM980 and + injects the new tilt-compensated data, previously read from the IM19. +------------------------------------------------------------------------------*/ #ifdef COMPILE_UM980 -UM980 *um980 = nullptr; // Don't instantiate until we know what gnssPlatform we're on +//---------------------------------------- +// If we have decryption keys, configure module +// Note: don't check online.lband_neo here. We could be using ip corrections +//---------------------------------------- +void RTK_UM980::applyPointPerfectKeys() +{ + // Taken care of in beginPPL() +} + +//---------------------------------------- +// Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz) +//---------------------------------------- +void RTK_UM980::baseRtcmDefault() +{ + // Reset RTCM rates to defaults + for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + settings.um980MessageRatesRTCMBase[x] = umMessagesRTCM[x].msgDefaultRate; +} + +//---------------------------------------- +// Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz) +//---------------------------------------- +void RTK_UM980::baseRtcmLowDataRate() +{ + // Zero out everything + for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + settings.um980MessageRatesRTCMBase[x] = 0; + + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1005")] = + 10; // 1005 0.1Hz - Exclude antenna height + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1074")] = 2; // 1074 0.5Hz + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1084")] = 2; // 1084 0.5Hz + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1094")] = 2; // 1094 0.5Hz + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1124")] = 2; // 1124 0.5Hz + settings.um980MessageRatesRTCMBase[getRtcmMessageNumberByName("RTCM1033")] = 10; // 1033 0.1Hz +} -void um980Begin() +//---------------------------------------- +// Connect to GNSS and identify particulars +//---------------------------------------- +void RTK_UM980::begin() { // During identifyBoard(), the GNSS UART and DR pins are set @@ -25,30 +67,30 @@ void um980Begin() } // Instantiate the library - um980 = new UM980(); + _um980 = new UM980(); // Turn on/off debug messages - if (settings.debugGnss == true) - um980EnableDebugging(); + if (settings.debugGnss) + debuggingEnable(); // In order to reduce UM980 configuration time, the UM980 library blocks the start of BESTNAV and RECTIME until 3D // fix is achieved However, if all NMEA messages are disabled, the UM980 will never detect a 3D fix. - if (um980IsGgaActive() == true) + if (isGgaActive()) // If NMEA GPGGA is turned on, suppress BESTNAV messages until GPGGA reports a 3D fix - um980->disableBinaryBeforeFix(); + _um980->disableBinaryBeforeFix(); else // If NMEA GPGGA is turned off, enable BESTNAV messages at power on which may lead to longer UM980 configuration // times - um980->enableBinaryBeforeFix(); + _um980->enableBinaryBeforeFix(); - if (um980->begin(*serialGNSS) == false) // Give the serial port over to the library + if (_um980->begin(*serialGNSS) == false) // Give the serial port over to the library { if (settings.debugGnss) systemPrintln("GNSS Failed to begin. Trying again."); // Try again with power on delay delay(1000); - if (um980->begin(*serialGNSS) == false) + if (_um980->begin(*serialGNSS) == false) { systemPrintln("GNSS offline"); displayGNSSFail(1000); @@ -62,54 +104,107 @@ void um980Begin() delay(2000); // 1s fails, 2s ok // Check firmware version and print info - um980PrintInfo(); + printModuleInfo(); // Shortly after reset, the UM980 responds to the VERSIONB command with OK but doesn't report version information - snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s", um980->getVersion()); + snprintf(gnssFirmwareVersion, sizeof(gnssFirmwareVersion), "%s", _um980->getVersion()); // getVersion returns the "Build" "7923". I think we probably need the "R4.10" which preceeds Build? TODO if (sscanf(gnssFirmwareVersion, "%d", &gnssFirmwareVersionInt) != 1) gnssFirmwareVersionInt = 99; - snprintf(gnssUniqueId, sizeof(gnssUniqueId), "%s", um980->getID()); + snprintf(gnssUniqueId, sizeof(gnssUniqueId), "%s", _um980->getID()); online.gnss = true; } -bool um980IsBlocking() +//---------------------------------------- +// Setup the timepulse output on the PPS pin for external triggering +// Setup TM2 time stamp input as need +//---------------------------------------- +bool RTK_UM980::beginExternalEvent() { - return um980->isBlocking(); + // UM980 Event signal not exposed + return (false); } -// Attempt 3 tries on UM980 config -bool um980Configure() +//---------------------------------------- +// Setup the timepulse output on the PPS pin for external triggering +//---------------------------------------- +bool RTK_UM980::beginPPS() { - // Skip configuring the UM980 if no new changes are necessary - if (settings.updateGNSSSettings == false) + // UM980 PPS signal not exposed + return (false); +} + +//---------------------------------------- +bool RTK_UM980::checkNMEARates() +{ + return false; +} + +//---------------------------------------- +bool RTK_UM980::checkPPPRates() +{ + return false; +} + +//---------------------------------------- +// Configure specific aspects of the receiver for base mode +//---------------------------------------- +bool RTK_UM980::configureBase() +{ + /* + Disable all messages + Start base + Enable RTCM Base messages + Enable NMEA messages + */ + + if (online.gnss == false) { - systemPrintln("UM980 configuration maintained"); - return (true); + systemPrintln("GNSS not online"); + return (false); } - for (int x = 0; x < 3; x++) - { - if (um980ConfigureOnce() == true) - return (true); + disableAllOutput(); - // If we fail, reset UM980 - systemPrintln("Resetting UM980 to complete configuration"); + bool response = true; - um980Reset(); - delay(500); - um980Boot(); - delay(500); + // Set the dynamic mode. This will cancel any base averaging mode and is needed + // to allow a freshly started device to settle in regular GNSS reception mode before issuing + // um980BaseAverageStart(). + response &= setModel(settings.dynamicModel); + + response &= setMultipathMitigation(settings.enableMultipathMitigation); + + response &= setHighAccuracyService(settings.enableGalileoHas); + + response &= enableRTCMBase(); // Only turn on messages, do not turn off messages. We assume the caller has + // UNLOG or similar. + + // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. + response &= enableNMEA(); + + // Save the current configuration into non-volatile memory (NVM) + // We don't need to re-configure the UM980 at next boot + bool settingsWereSaved = _um980->saveConfiguration(); + if (settingsWereSaved) + settings.updateGNSSSettings = false; + + if (response == false) + { + systemPrintln("UM980 Base failed to configure"); } - systemPrintln("UM980 failed to configure"); - return (false); + if (settings.debugGnss) + systemPrintln("UM980 Base configured"); + + return (response); } -bool um980ConfigureOnce() +//---------------------------------------- +bool RTK_UM980::configureOnce() { /* Disable all message traffic @@ -126,30 +221,30 @@ bool um980ConfigureOnce() */ if (settings.debugGnss) - um980EnableDebugging(); // Print all debug to Serial + debuggingEnable(); // Print all debug to Serial - um980DisableAllOutput(); // Disable COM1/2/3 + disableAllOutput(); // Disable COM1/2/3 bool response = true; - response &= um980->setPortBaudrate("COM1", 115200); // COM1 is connected to switch, then USB - response &= um980->setPortBaudrate("COM2", 115200); // COM2 is connected to the IMU - response &= um980->setPortBaudrate("COM3", 115200); // COM3 is connected to the switch, then ESP32 + response &= _um980->setPortBaudrate("COM1", 115200); // COM1 is connected to switch, then USB + response &= _um980->setPortBaudrate("COM2", 115200); // COM2 is connected to the IMU + response &= _um980->setPortBaudrate("COM3", 115200); // COM3 is connected to the switch, then ESP32 // For now, let's not change the baud rate of the interface. We'll be using the default 115200 for now. - response &= um980SetBaudRateCOM3(settings.dataPortBaud); // COM3 is connected to ESP UART2 + response &= setBaudRateCOM3(settings.dataPortBaud); // COM3 is connected to ESP UART2 // Enable PPS signal with a width of 200ms, and a period of 1 second - response &= um980->enablePPS(200000, 1000); // widthMicroseconds, periodMilliseconds + response &= _um980->enablePPS(200000, 1000); // widthMicroseconds, periodMilliseconds - response &= um980SetMinElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. + response &= setElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. - response &= um980SetMinCNO(settings.minCNO); + response &= setMinCnoRadio(settings.minCNO); - response &= um980SetConstellations(); + response &= setConstellations(); - if (um980->isConfigurationPresent("CONFIG SIGNALGROUP 2") == false) + if (_um980->isConfigurationPresent("CONFIG SIGNALGROUP 2") == false) { - if (um980->sendCommand("CONFIG SIGNALGROUP 2") == false) + if (_um980->sendCommand("CONFIG SIGNALGROUP 2") == false) systemPrintln("Signal group 2 command failed"); else { @@ -158,7 +253,7 @@ bool um980ConfigureOnce() while (1) { delay(1000); // Wait for device to reboot - if (um980->isConnected() == true) + if (_um980->isConnected()) break; else systemPrintln("UM980 rebooting"); @@ -168,7 +263,7 @@ bool um980ConfigureOnce() } } - if (response == true) + if (response) { online.gnss = true; // If we failed before, mark as online now @@ -176,7 +271,7 @@ bool um980ConfigureOnce() // Save the current configuration into non-volatile memory (NVM) // We don't need to re-configure the UM980 at next boot - bool settingsWereSaved = um980->saveConfiguration(); + bool settingsWereSaved = _um980->saveConfiguration(); if (settingsWereSaved) settings.updateGNSSSettings = false; } @@ -186,7 +281,50 @@ bool um980ConfigureOnce() return (response); } -bool um980ConfigureRover() +//---------------------------------------- +// Configure specific aspects of the receiver for NTP mode +//---------------------------------------- +bool RTK_UM980::configureNtpMode() +{ + return false; +} + +//---------------------------------------- +// Setup the u-blox module for any setup (base or rover) +// In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare +// occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. +//---------------------------------------- +bool RTK_UM980::configureRadio() +{ + // Skip configuring the UM980 if no new changes are necessary + if (settings.updateGNSSSettings == false) + { + systemPrintln("UM980 configuration maintained"); + return (true); + } + + for (int x = 0; x < 3; x++) + { + if (configureOnce()) + return (true); + + // If we fail, reset UM980 + systemPrintln("Resetting UM980 to complete configuration"); + + um980Reset(); + delay(500); + um980Boot(); + delay(500); + } + + systemPrintln("UM980 failed to configure"); + return (false); +} + +//---------------------------------------- +// Configure specific aspects of the receiver for rover mode +//---------------------------------------- +bool RTK_UM980::configureRover() { /* Disable all message traffic @@ -202,38 +340,38 @@ bool um980ConfigureRover() return (false); } - um980DisableAllOutput(); + disableAllOutput(); bool response = true; - response &= um980SetModel(settings.dynamicModel); // This will cancel any base averaging mode + response &= setModel(settings.dynamicModel); // This will cancel any base averaging mode - response &= um980SetMinElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. + response &= setElevation(settings.minElev); // UM980 default is 5 degrees. Our default is 10. - response &= um980SetMultipathMitigation(settings.enableMultipathMitigation); + response &= setMultipathMitigation(settings.enableMultipathMitigation); - response &= um980SetHighAccuracyService(settings.enableGalileoHas); + response &= setHighAccuracyService(settings.enableGalileoHas); // Configure UM980 to output binary reports out COM2, connected to IM19 COM3 - response &= um980->sendCommand("BESTPOSB COM2 0.2"); // 5Hz - response &= um980->sendCommand("PSRVELB COM2 0.2"); + response &= _um980->sendCommand("BESTPOSB COM2 0.2"); // 5Hz + response &= _um980->sendCommand("PSRVELB COM2 0.2"); // Configure UM980 to output NMEA reports out COM2, connected to IM19 COM3 - response &= um980->setNMEAPortMessage("GPGGA", "COM2", 0.2); // 5Hz + response &= _um980->setNMEAPortMessage("GPGGA", "COM2", 0.2); // 5Hz // Enable the NMEA sentences and RTCM on COM3 last. This limits the traffic on the config // interface port during config. // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= um980EnableRTCMRover(); + response &= enableRTCMRover(); // TODO consider reducing the GSV sentence to 1/4 of the GPGGA setting // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= um980EnableNMEA(); + response &= enableNMEA(); // Save the current configuration into non-volatile memory (NVM) // We don't need to re-configure the UM980 at next boot - bool settingsWereSaved = um980->saveConfiguration(); + bool settingsWereSaved = _um980->saveConfiguration(); if (settingsWereSaved) settings.updateGNSSSettings = false; @@ -245,104 +383,64 @@ bool um980ConfigureRover() return (response); } -bool um980ConfigureBase() +//---------------------------------------- +void RTK_UM980::debuggingDisable() { - /* - Disable all messages - Start base - Enable RTCM Base messages - Enable NMEA messages - */ - - if (online.gnss == false) - { - systemPrintln("GNSS not online"); - return (false); - } - - um980DisableAllOutput(); - - bool response = true; - - // Set the dynamic mode. This will cancel any base averaging mode and is needed - // to allow a freshly started device to settle in regular GNSS reception mode before issuing - // um980BaseAverageStart(). - response &= um980SetModel(settings.dynamicModel); - - response &= um980SetMultipathMitigation(settings.enableMultipathMitigation); - - response &= um980SetHighAccuracyService(settings.enableGalileoHas); - - response &= um980EnableRTCMBase(); // Only turn on messages, do not turn off messages. We assume the caller has - // UNLOG or similar. - - // Only turn on messages, do not turn off messages. We assume the caller has UNLOG or similar. - response &= um980EnableNMEA(); - - // Save the current configuration into non-volatile memory (NVM) - // We don't need to re-configure the UM980 at next boot - bool settingsWereSaved = um980->saveConfiguration(); - if (settingsWereSaved) - settings.updateGNSSSettings = false; + if (online.gnss) + _um980->disableDebugging(); +} - if (response == false) +//---------------------------------------- +void RTK_UM980::debuggingEnable() +{ + if (online.gnss) { - systemPrintln("UM980 Base failed to configure"); + _um980->enableDebugging(); // Print all debug to Serial + _um980->enablePrintRxMessages(); // Print incoming processed messages from SEMP } - - if (settings.debugGnss) - systemPrintln("UM980 Base configured"); - - return (response); } -// Start a Self-optimizing Base Station -// We do not use the distance parameter (settings.observationPositionAccuracy) because that -// setting on the UM980 is related to automatically restarting base mode -// at power on (very different from ZED-F9P). -bool um980BaseAverageStart() +//---------------------------------------- +// Turn off all NMEA and RTCM +void RTK_UM980::disableAllOutput() { - bool response = true; - - response &= - um980->setModeBaseAverage(settings.observationSeconds); // Average for a number of seconds (default is 60) + if (settings.debugGnss) + systemPrintln("UM980 disable output"); - gnss->_autoBaseStartTimer = millis(); // Stamp when averaging began + // Turn off local noise before moving to other ports + _um980->disableOutput(); - if (response == false) + // Re-attempt as necessary + for (int x = 0; x < 3; x++) { - systemPrintln("Survey start failed"); - return (false); + bool response = true; + response &= _um980->disableOutputPort("COM1"); + response &= _um980->disableOutputPort("COM2"); + response &= _um980->disableOutputPort("COM3"); + if (response) + break; } - - return (response); } -// Start the base using fixed coordinates -bool um980FixedBaseStart() +//---------------------------------------- +// Disable all output, then re-enable +//---------------------------------------- +void RTK_UM980::disableRTCM() { - bool response = true; - - if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) - { - um980->setModeBaseECEF(settings.fixedEcefX, settings.fixedEcefY, settings.fixedEcefZ); - } - else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) - { - // Add height of instrument (HI) to fixed altitude - // https://www.e-education.psu.edu/geog862/node/1853 - // For example, if HAE is at 100.0m, + 2m stick + 73mm APC = 102.073 - float totalFixedAltitude = - settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); - - um980->setModeBaseGeodetic(settings.fixedLat, settings.fixedLong, totalFixedAltitude); - } + disableAllOutput(); + enableNMEA(); +} - return (response); +//---------------------------------------- +void RTK_UM980::enableGgaForNtrip() +{ + // TODO um980EnableGgaForNtrip(); } +//---------------------------------------- // Turn on all the enabled NMEA messages on COM3 -bool um980EnableNMEA() +//---------------------------------------- +bool RTK_UM980::enableNMEA() { bool response = true; bool gpggaEnabled = false; @@ -354,7 +452,7 @@ bool um980EnableNMEA() // has UNLOG or similar. if (settings.um980MessageRatesNMEA[messageNumber] > 0) { - if (um980->setNMEAPortMessage(umMessagesNMEA[messageNumber].msgTextName, "COM3", + if (_um980->setNMEAPortMessage(umMessagesNMEA[messageNumber].msgTextName, "COM3", settings.um980MessageRatesNMEA[messageNumber]) == false) { if (settings.debugGnss) @@ -380,16 +478,45 @@ bool um980EnableNMEA() { // Force on any messages that are needed for PPL if (gpggaEnabled == false) - response &= um980->setNMEAPortMessage("GPGGA", "COM3", 1); + response &= _um980->setNMEAPortMessage("GPGGA", "COM3", 1); if (gpzdaEnabled == false) - response &= um980->setNMEAPortMessage("GPZDA", "COM3", 1); + response &= _um980->setNMEAPortMessage("GPZDA", "COM3", 1); + } + + return (response); +} + +//---------------------------------------- +// Turn on all the enabled RTCM Base messages on COM3 +//---------------------------------------- +bool RTK_UM980::enableRTCMBase() +{ + bool response = true; + + for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) + { + // Only turn on messages, do not turn off messages set to 0. This saves on command sending. We assume the caller + // has UNLOG or similar. + if (settings.um980MessageRatesRTCMBase[messageNumber] > 0) + { + if (_um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", + settings.um980MessageRatesRTCMBase[messageNumber]) == false) + { + if (settings.debugGnss) + systemPrintf("Enable RTCM failed at messageNumber %d %s.", messageNumber, + umMessagesRTCM[messageNumber].msgTextName); + response &= false; // If any one of the commands fails, report failure overall + } + } } return (response); } +//---------------------------------------- // Turn on all the enabled RTCM Rover messages on COM3 -bool um980EnableRTCMRover() +//---------------------------------------- +bool RTK_UM980::enableRTCMRover() { bool response = true; bool rtcm1019Enabled = false; @@ -403,7 +530,7 @@ bool um980EnableRTCMRover() // has UNLOG or similar. if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) { - if (um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", + if (_um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", settings.um980MessageRatesRTCMRover[messageNumber]) == false) { if (settings.debugGnss) @@ -433,550 +560,680 @@ bool um980EnableRTCMRover() { // Force on any messages that are needed for PPL if (rtcm1019Enabled == false) - response &= um980->setRTCMPortMessage("RTCM1019", "COM3", 1); + response &= _um980->setRTCMPortMessage("RTCM1019", "COM3", 1); if (rtcm1020Enabled == false) - response &= um980->setRTCMPortMessage("RTCM1020", "COM3", 1); + response &= _um980->setRTCMPortMessage("RTCM1020", "COM3", 1); if (rtcm1042Enabled == false) - response &= um980->setRTCMPortMessage("RTCM1042", "COM3", 1); + response &= _um980->setRTCMPortMessage("RTCM1042", "COM3", 1); if (rtcm1046Enabled == false) - response &= um980->setRTCMPortMessage("RTCM1046", "COM3", 1); + response &= _um980->setRTCMPortMessage("RTCM1046", "COM3", 1); } return (response); } -// Turn on all the enabled RTCM Base messages on COM3 -bool um980EnableRTCMBase() +//---------------------------------------- +// Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted +// even if there is no GPS fix. We use it to test serial output. +//---------------------------------------- +bool RTK_UM980::enableRTCMTest() { - bool response = true; + // There is no data port on devices with the UM980 + return false; +} - for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) +//---------------------------------------- +// Restore the GNSS to the factory settings +//---------------------------------------- +void RTK_UM980::factoryReset() +{ + if (online.gnss) { - // Only turn on messages, do not turn off messages set to 0. This saves on command sending. We assume the caller - // has UNLOG or similar. - if (settings.um980MessageRatesRTCMBase[messageNumber] > 0) - { - if (um980->setRTCMPortMessage(umMessagesRTCM[messageNumber].msgTextName, "COM3", - settings.um980MessageRatesRTCMBase[messageNumber]) == false) - { - if (settings.debugGnss) - systemPrintf("Enable RTCM failed at messageNumber %d %s.", messageNumber, - umMessagesRTCM[messageNumber].msgTextName); - response &= false; // If any one of the commands fails, report failure overall - } - } + _um980->factoryReset(); + + // systemPrintln("Waiting for UM980 to reboot"); + // while (1) + // { + // delay(1000); //Wait for device to reboot + // if (_um980->isConnected()) break; + // else systemPrintln("Device still rebooting"); + // } + // systemPrintln("UM980 has been factory reset"); } +} - return (response); +//---------------------------------------- +uint16_t RTK_UM980::fileBufferAvailable() +{ + // TODO return(um980FileBufferAvailable()); + return (0); +} + +//---------------------------------------- +uint16_t RTK_UM980::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +{ + // TODO return(um980FileBufferAvailable()); + return (0); } -// Turn on all the enabled Constellations -bool um980SetConstellations() +//---------------------------------------- +// Start the base using fixed coordinates +//---------------------------------------- +bool RTK_UM980::fixedBaseStart() { bool response = true; - for (int constellationNumber = 0; constellationNumber < MAX_UM980_CONSTELLATIONS; constellationNumber++) + if (online.gnss == false) + return (false); + + if (settings.fixedBaseCoordinateType == COORD_TYPE_ECEF) { - if (settings.um980Constellations[constellationNumber] == true) - { - if (um980->enableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) - { - if (settings.debugGnss) - systemPrintf("Enable constellation failed at constellationNumber %d %s.", constellationNumber, - um980ConstellationCommands[constellationNumber].textName); - response &= false; // If any one of the commands fails, report failure overall - } - } - else - { - if (um980->disableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) - { - if (settings.debugGnss) - systemPrintf("Disable constellation failed at constellationNumber %d %s.", constellationNumber, - um980ConstellationCommands[constellationNumber].textName); - response &= false; // If any one of the commands fails, report failure overall - } - } + _um980->setModeBaseECEF(settings.fixedEcefX, settings.fixedEcefY, settings.fixedEcefZ); } - - return (response); -} - -// Turn off all NMEA and RTCM -void um980DisableAllOutput() -{ - if (settings.debugGnss) - systemPrintln("UM980 disable output"); - - // Turn off local noise before moving to other ports - um980->disableOutput(); - - // Re-attempt as necessary - for (int x = 0; x < 3; x++) + else if (settings.fixedBaseCoordinateType == COORD_TYPE_GEODETIC) { - bool response = true; - response &= um980->disableOutputPort("COM1"); - response &= um980->disableOutputPort("COM2"); - response &= um980->disableOutputPort("COM3"); - if (response == true) - break; + // Add height of instrument (HI) to fixed altitude + // https://www.e-education.psu.edu/geog862/node/1853 + // For example, if HAE is at 100.0m, + 2m stick + 73mm APC = 102.073 + float totalFixedAltitude = + settings.fixedAltitude + ((settings.antennaHeight_mm + settings.antennaPhaseCenter_mm) / 1000.0); + + _um980->setModeBaseGeodetic(settings.fixedLat, settings.fixedLong, totalFixedAltitude); } -} -// Disable all output, then re-enable -void um980DisableRTCM() -{ - um980DisableAllOutput(); - um980EnableNMEA(); + return (response); } -bool um980SetMinElevation(uint8_t elevation) +//---------------------------------------- +// Return the number of active/enabled messages +//---------------------------------------- +uint8_t RTK_UM980::getActiveMessageCount() { - return (um980->setElevationAngle(elevation)); -} + uint8_t count = 0; -bool um980SetMinCNO(uint8_t minCNO) -{ - return (um980->setMinCNO(minCNO)); + count += getActiveNmeaMessageCount(); + count += getActiveRtcmMessageCount(); + return (count); } -bool um980SetModel(uint8_t modelNumber) +//---------------------------------------- +uint8_t RTK_UM980::getActiveNmeaMessageCount() { - if (modelNumber == UM980_DYN_MODEL_SURVEY) - return (um980->setModeRoverSurvey()); - else if (modelNumber == UM980_DYN_MODEL_UAV) - return (um980->setModeRoverUAV()); - else if (modelNumber == UM980_DYN_MODEL_AUTOMOTIVE) - return (um980->setModeRoverAutomotive()); - return (false); -} + uint8_t count = 0; -bool um980SetMultipathMitigation(bool enableMultipathMitigation) -{ - bool result = true; + for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) + if (settings.um980MessageRatesNMEA[x] > 0) + count++; - // Enable MMP as required - if (enableMultipathMitigation == true) - { - if (um980->isConfigurationPresent("CONFIG MMP ENABLE") == false) - { - if (um980->sendCommand("CONFIG MMP ENABLE") == true) - systemPrintln("Multipath Mitigation enabled"); - else - { - systemPrintln("Multipath Mitigation failed to enable"); - result = false; - } - } - } - else - { - // Turn off MMP - if (um980->isConfigurationPresent("CONFIG MMP ENABLE") == true) - { - if (um980->sendCommand("CONFIG MMP DISABLE") == true) - systemPrintln("Multipath Mitigation disabled"); - else - { - systemPrintln("Multipath Mitigation failed to disable"); - result = false; - } - } - } - return (result); + return (count); } -bool um980SetHighAccuracyService(bool enableGalileoHas) +//---------------------------------------- +uint8_t RTK_UM980::getActiveRtcmMessageCount() { - bool result = true; + uint8_t count = 0; - // Enable E6 and PPP if enabled and possible - if (settings.enableGalileoHas == true) + // Determine which state we are in + if (inRoverMode()) { - // E6 reception requires version 11833 or greater - int um980Version = String(um980->getVersion()).toInt(); // Convert the string response to a value - if (um980Version >= 11833) - { - if (um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS") == false) - { - if (um980->sendCommand("CONFIG PPP ENABLE E6-HAS") == true) - systemPrintln("Galileo E6 service enabled"); - else - { - systemPrintln("Galileo E6 service failed to enable"); - result = false; - } - - if (um980->sendCommand("CONFIG PPP DATUM WGS84") == true) - systemPrintln("WGS84 Datum applied"); - else - { - systemPrintln("WGS84 Datum failed to apply"); - result = false; - } - } - } - else - { - systemPrintf( - "Current UM980 firmware: v%d. Galileo E6 reception requires v11833 or newer. Please update the " - "firmware on your UM980 to allow for HAS operation. Please see https://bit.ly/sfe-rtk-um980-update\r\n", - um980Version); - // Don't fail the result. Module is still configured, just without HAS. - } + for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + if (settings.um980MessageRatesRTCMRover[x] > 0) + count++; } else { - // Turn off HAS/E6 - if (um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS") == true) - { - if (um980->sendCommand("CONFIG PPP DISABLE") == true) - systemPrintln("Galileo E6 service disabled"); - else - { - systemPrintln("Galileo E6 service failed to disable"); - result = false; - } - } + for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + if (settings.um980MessageRatesRTCMBase[x] > 0) + count++; } - return (result); + + return (count); } -void um980FactoryReset() +//---------------------------------------- +// Returns the altitude in meters or zero if the GNSS is offline +//---------------------------------------- +double RTK_UM980::getAltitude() { - um980->factoryReset(); + if (online.gnss) + return (_um980->getAltitude()); + return (0); +} - // systemPrintln("Waiting for UM980 to reboot"); - // while (1) - // { - // delay(1000); //Wait for device to reboot - // if (um980->isConnected() == true) break; - // else systemPrintln("Device still rebooting"); - // } - // systemPrintln("UM980 has been factory reset"); +//---------------------------------------- +// Returns the carrier solution or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getCarrierSolution() +{ + if (online.gnss) + // 0 = Solution computed + // 1 = Insufficient observation + // 3 = No convergence, + // 4 = Covariance trace + return (_um980->getSolutionStatus()); + return 0; } -// The UM980 does not have a rate setting. Instead the report rate of -// the GNSS messages can be set. For example, 0.5 is 2Hz, 0.2 is 5Hz. -// We assume, if the user wants to set the 'rate' to 5Hz, they want all -// messages set to that rate. -// All NMEA/RTCM for a rover will be based on the measurementRateMs setting -// ie, if a message != 0, then it will be output at the measurementRate. -// All RTCM for a base will be based on a measurementRateMs of 1000 with messages -// that can be reported more slowly than that (ie 1 per 10 seconds). -bool um980SetRate(double secondsBetweenSolutions) +//---------------------------------------- +uint32_t RTK_UM980::getDataBaudRate() { - bool response = true; + return (0); // UM980 has no multiplexer +} - um980DisableAllOutput(); +//---------------------------------------- +// Returns the day number or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getDay() +{ + if (online.gnss) + return (_um980->getDay()); + return 0; +} - // Overwrite any enabled messages with this rate - for (int messageNumber = 0; messageNumber < MAX_UM980_NMEA_MSG; messageNumber++) - { - if (settings.um980MessageRatesNMEA[messageNumber] > 0) - { - settings.um980MessageRatesNMEA[messageNumber] = secondsBetweenSolutions; - } - } - response &= um980EnableNMEA(); // Enact these rates +//---------------------------------------- +// Return the number of milliseconds since GNSS data was last updated +//---------------------------------------- +uint16_t RTK_UM980::getFixAgeMilliseconds() +{ + if (online.gnss) + return (_um980->getFixAgeMilliseconds()); + return 0; +} - // TODO We don't know what state we are in, so we don't - // know which RTCM settings to update. Assume we are - // in rover for now - for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) - { - if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) - { - settings.um980MessageRatesRTCMRover[messageNumber] = secondsBetweenSolutions; - } - } - response &= um980EnableRTCMRover(); // Enact these rates +//---------------------------------------- +// Returns the fix type or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getFixType() +{ + if (online.gnss) + // 0 = None + // 1 = FixedPos + // 8 = DopplerVelocity, + // 16 = 3D Fix (Single) + // 17 = Pseudorange differential solution + // 18 = SBAS, 32 = L1 float + // 33 = Ionosphere-free float solution + // 34 = Narrow-land float solution + // 48 = L1 fixed solution + // 49 = RTK Float (Presumed) (Wide-lane fixed solution) + // 50 = RTK Fixed (Narrow-lane fixed solution) + // 68 = Precise Point Positioning solution converging + // 69 = Precise Point Positioning + return (_um980->getPositionType()); + return 0; +} - // If we successfully set rates, only then record to settings - if (response == true) - { - uint16_t msBetweenSolutions = secondsBetweenSolutions * 1000; - settings.measurementRateMs = msBetweenSolutions; - } - else +//---------------------------------------- +// Get the horizontal position accuracy +// Returns the horizontal position accuracy or zero if offline +//---------------------------------------- +float RTK_UM980::getHorizontalAccuracy() +{ + if (online.gnss) { - systemPrintln("Failed to set measurement and navigation rates"); - return (false); + float latitudeDeviation = _um980->getLatitudeDeviation(); + float longitudeDeviation = _um980->getLongitudeDeviation(); + + // The binary message may contain all 0xFFs leading to a very large negative number. + if (longitudeDeviation < -0.01) + longitudeDeviation = 50.0; + if (latitudeDeviation < -0.01) + latitudeDeviation = 50.0; + + // Return the lower of the two Lat/Long deviations + if (longitudeDeviation < latitudeDeviation) + return (longitudeDeviation); + return (latitudeDeviation); } - - return (true); + return 0; } -// Returns the seconds between measurements -double um980GetRateS() +//---------------------------------------- +// Returns the hours of 24 hour clock or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getHour() { - return (((double)settings.measurementRateMs) / 1000.0); + if (online.gnss) + return (_um980->getHour()); + return 0; } -// Send data directly from ESP GNSS UART1 to UM980 UART3 -int um980PushRawData(uint8_t *dataToSend, int dataLength) +//---------------------------------------- +const char * RTK_UM980::getId() { - return (serialGNSS->write(dataToSend, dataLength)); + if (online.gnss) + return (_um980->getID()); + return ((char *)"\0"); } -// Set the baud rate of the UM980 com port 3 -// This is used during the Bluetooth test -bool um980SetBaudRateCOM3(uint32_t baudRate) +//---------------------------------------- +// Get the latitude value +// Returns the latitude value or zero if not online +//---------------------------------------- +double RTK_UM980::getLatitude() { - bool response = true; - - response &= um980->setPortBaudrate("COM3", baudRate); - - return (response); + if (online.gnss) + return (_um980->getLatitude()); + return 0; } -// Return the lower of the two Lat/Long deviations -float um980GetHorizontalAccuracy() +//---------------------------------------- +// Query GNSS for current leap seconds +//---------------------------------------- +uint8_t RTK_UM980::getLeapSeconds() { - float latitudeDeviation = um980->getLatitudeDeviation(); - float longitudeDeviation = um980->getLongitudeDeviation(); - - // The binary message may contain all 0xFFs leading to a very large negative number. - if (longitudeDeviation < -0.01) - longitudeDeviation = 50.0; - if (latitudeDeviation < -0.01) - latitudeDeviation = 50.0; - - if (longitudeDeviation < latitudeDeviation) - return (longitudeDeviation); - - return (latitudeDeviation); + // TODO Need to find leap seconds in UM980 + return (18); // Default to 18 } -int um980GetSatellitesInView() +//---------------------------------------- +// Get the longitude value +// Outputs: +// Returns the longitude value or zero if not online +//---------------------------------------- +double RTK_UM980::getLongitude() { - return (um980->getSIV()); + if (online.gnss) + return (_um980->getLongitude()); + return 0; } -double um980GetLatitude() +//---------------------------------------- +// Returns two digits of milliseconds or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getMillisecond() { - return (um980->getLatitude()); + if (online.gnss) + return (_um980->getMillisecond()); + return 0; } -double um980GetLongitude() +//---------------------------------------- +// Returns minutes or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getMinute() { - return (um980->getLongitude()); + if (online.gnss) + return (_um980->getMinute()); + return 0; } -double um980GetAltitude() +//---------------------------------------- +// Returns month number or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getMonth() { - return (um980->getAltitude()); + if (online.gnss) + return (_um980->getMonth()); + return 0; } -bool um980IsValidTime() +//---------------------------------------- +// Returns nanoseconds or zero if not online +//---------------------------------------- +uint32_t RTK_UM980::getNanosecond() { - if (um980->getTimeStatus() == 0) // 0 = valid, 3 = invalid - return (true); - return (false); + if (online.gnss) + // UM980 does not have nanosecond, but it does have millisecond + return (getMillisecond() * 1000L); // Convert to ns + return 0; } -bool um980IsValidDate() +//---------------------------------------- +// Given the name of an NMEA message, return the array number +//---------------------------------------- +uint8_t RTK_UM980::getNmeaMessageNumberByName(const char *msgName) { - if (um980->getDateStatus() == 1) // 0 = Invalid, 1 = valid, 2 = leap second warning - return (true); - return (false); + for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) + { + if (strcmp(umMessagesNMEA[x].msgTextName, msgName) == 0) + return (x); + } + + systemPrintf("getNmeaMessageNumberByName: %s not found\r\n", msgName); + return (0); } -uint8_t um980GetSolutionStatus() +//---------------------------------------- +uint32_t RTK_UM980::getRadioBaudRate() { - return (um980->getSolutionStatus()); // 0 = Solution computed, 1 = Insufficient observation, 3 = No convergence, 4 = - // Covariance trace + return (0); // UM980 has no multiplexer } -bool um980IsFullyResolved() +//---------------------------------------- +// Returns the seconds between measurements +//---------------------------------------- +double RTK_UM980::getRateS() { - // UM980 does not have this feature directly. - // getSolutionStatus: 0 = Solution computed, 1 = Insufficient observation, 3 = No convergence, 4 = Covariance trace - if (um980GetSolutionStatus() == 0) - return (true); - return (false); + return (((double)settings.measurementRateMs) / 1000.0); } -// Standard deviation of the receiver clock offset, s. -// UM980 returns seconds, ZED returns nanoseconds. We convert here to ns. -// Return just ns in uint32_t form -uint32_t um980GetTimeDeviation() +//---------------------------------------- +const char * RTK_UM980::getRtcmDefaultString() { - double timeDeviation_s = um980->getTimeOffsetDeviation(); - // systemPrintf("um980 timeDeviation_s: %0.10f\r\n", timeDeviation_s); - if (timeDeviation_s > 1.0) - return (999999999); - - uint32_t timeDeviation_ns = timeDeviation_s * 1000000000L; // Convert to nanoseconds - // systemPrintf("um980 timeDeviation_ns: %d\r\n", timeDeviation_ns); - return (timeDeviation_ns); + return "1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz"; } -// 0 = None -// 16 = 3D Fix (Single) -// 49 = RTK Float (Presumed) (Wide-lane fixed solution) -// 50 = RTK Fixed (Narrow-lane fixed solution) -// Other position types, not yet seen -// 1 = FixedPos, 8 = DopplerVelocity, -// 17 = Pseudorange differential solution, 18 = SBAS, 32 = L1 float, 33 = Ionosphere-free float solution -// 34 = Narrow-land float solution, 48 = L1 fixed solution -// 68 = Precise Point Positioning solution converging -// 69 = Precise Point Positioning -uint8_t um980GetPositionType() +//---------------------------------------- +const char * RTK_UM980::getRtcmLowDataRateString() { - return (um980->getPositionType()); + return "1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz"; } -// Return full year, ie 2023, not 23. -uint16_t um980GetYear() +//---------------------------------------- +// Given the name of an RTCM message, return the array number +//---------------------------------------- +uint8_t RTK_UM980::getRtcmMessageNumberByName(const char *msgName) { - return (um980->getYear()); + for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + { + if (strcmp(umMessagesRTCM[x].msgTextName, msgName) == 0) + return (x); + } + + systemPrintf("getRtcmMessageNumberByName: %s not found\r\n", msgName); + return (0); } -uint8_t um980GetMonth() + +//---------------------------------------- +// Returns the number of satellites in view or zero if offline +//---------------------------------------- +uint8_t RTK_UM980::getSatellitesInView() { - return (um980->getMonth()); + if (online.gnss) + return (_um980->getSIV()); + return 0; } -uint8_t um980GetDay() + +//---------------------------------------- +// Returns seconds or zero if not online +//---------------------------------------- +uint8_t RTK_UM980::getSecond() { - return (um980->getDay()); + if (online.gnss) + return (_um980->getSecond()); + return 0; } -uint8_t um980GetHour() + +//---------------------------------------- +// Get the survey-in mean accuracy +//---------------------------------------- +float RTK_UM980::getSurveyInMeanAccuracy() { - return (um980->getHour()); + // Not supported on the UM980 + // Return the current HPA instead + return getHorizontalAccuracy(); } -uint8_t um980GetMinute() + +//---------------------------------------- +// Return the number of seconds the survey-in process has been running +//---------------------------------------- +int RTK_UM980::getSurveyInObservationTime() { - return (um980->getMinute()); + int elapsedSeconds = (millis() - _autoBaseStartTimer) / 1000; + return (elapsedSeconds); } -uint8_t um980GetSecond() + +//---------------------------------------- +// Returns timing accuracy or zero if not online +//---------------------------------------- +uint32_t RTK_UM980::getTimeAccuracy() { - return (um980->getSecond()); + if (online.gnss) + { + // Standard deviation of the receiver clock offset, s. + // UM980 returns seconds, ZED returns nanoseconds. We convert here to ns. + // Return just ns in uint32_t form + double timeDeviation_s = _um980->getTimeOffsetDeviation(); + if (timeDeviation_s > 1.0) + return (999999999); + + uint32_t timeDeviation_ns = timeDeviation_s * 1000000000L; // Convert to nanoseconds + return (timeDeviation_ns); + } + return 0; } -uint8_t um980GetMillisecond() + +//---------------------------------------- +// Returns full year, ie 2023, not 23. +//---------------------------------------- +uint16_t RTK_UM980::getYear() { - return (um980->getMillisecond()); + if (online.gnss) + return (_um980->getYear()); + return 0; } -// Print the module type and firmware version -void um980PrintInfo() +//---------------------------------------- +// Returns true if the device is in Rover mode +// Currently the only two modes are Rover or Base +//---------------------------------------- +bool RTK_UM980::inRoverMode() { - uint8_t modelType = um980->getModelType(); - - if (modelType == 18) - systemPrint("UM980"); - else - systemPrintf("Unicore Model Unknown %d", modelType); + // Determine which state we are in + if (settings.lastState == STATE_BASE_NOT_STARTED) + return (false); - systemPrintf(" firmware: %s\r\n", um980->getVersion()); + return (true); // Default to Rover } -// Return the number of milliseconds since the data was updated -uint16_t um980FixAgeMilliseconds() +//---------------------------------------- +bool RTK_UM980::isBlocking() { - return (um980->getFixAgeMilliseconds()); + if (online.gnss) + return _um980->isBlocking(); + return false; } -bool um980SaveConfiguration() +//---------------------------------------- +// Date is confirmed once we have GNSS fix +//---------------------------------------- +bool RTK_UM980::isConfirmedDate() { - return (um980->saveConfiguration()); + // UM980 doesn't have this feature. Check for valid date. + return isValidDate(); } -void um980EnableDebugging() +//---------------------------------------- +// Time is confirmed once we have GNSS fix +//---------------------------------------- +bool RTK_UM980::isConfirmedTime() { - um980->enableDebugging(); // Print all debug to Serial - um980->enablePrintRxMessages(); // Print incoming processed messages from SEMP + // UM980 doesn't have this feature. Check for valid time. + return isValidTime(); } -void um980DisableDebugging() + +//---------------------------------------- +// Return true if GNSS receiver has a higher quality DGPS fix than 3D +//---------------------------------------- +bool RTK_UM980::isDgpsFixed() { - um980->disableDebugging(); + if (online.gnss) + // 17 = Pseudorange differential solution + return (_um980->getPositionType() == 17); + return false; } -bool um980SetModeRoverSurvey() +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to know if we have a valid fix, not what type of fix +// This function checks to see if the given platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool RTK_UM980::isFixed() { - return (um980->setModeRoverSurvey()); + if (online.gnss) + // 16 = 3D Fix (Single) + return (_um980->getPositionType() >= 16); + return false; } -// If we have received serial data from the UM980 outside of the Unicore library (ie, from processUart1Message task) -// we can pass data back into the Unicore library to allow it to update its own variables -void um980UnicoreHandler(uint8_t *buffer, int length) +//---------------------------------------- +// Used in tpISR() for time pulse synchronization +//---------------------------------------- +bool RTK_UM980::isFullyResolved() { - um980->unicoreHandler(buffer, length); + if (online.gnss) + // UM980 does not have this feature directly. + // getSolutionStatus: + // 0 = Solution computed + // 1 = Insufficient observation + // 3 = No convergence + // 4 = Covariance trace + return (_um980->getSolutionStatus() == 0); + return false; } -char *um980GetId() +//---------------------------------------- +// Return true if the GPGGA message is active +//---------------------------------------- +bool RTK_UM980::isGgaActive() { - return (um980->getID()); + if (settings.um980MessageRatesNMEA[getNmeaMessageNumberByName("GPGGA")] > 0) + return (true); + return (false); } -void um980Boot() +//---------------------------------------- +bool RTK_UM980::isPppConverged() { - digitalWrite(pin_GNSS_DR_Reset, HIGH); // Tell UM980 and DR to boot + if (online.gnss) + // 69 = Precision Point Positioning + return (_um980->getPositionType() == 69); + return (false); } -void um980Reset() + +//---------------------------------------- +bool RTK_UM980::isPppConverging() { - digitalWrite(pin_GNSS_DR_Reset, LOW); // Tell UM980 and DR to reset + if (online.gnss) + // 68 = PPP solution converging + return (_um980->getPositionType() == 68); + return (false); } -// Query GNSS for current leap seconds -uint8_t um980GetLeapSeconds() +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to +// know if we have an RTK Fix. This function checks to see if the given +// platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool RTK_UM980::isRTKFix() { - // TODO Need to find leap seconds in UM980 - return (18); // Default to 18 + if (online.gnss) + // 50 = RTK Fixed (Narrow-lane fixed solution) + return (_um980->getPositionType() == 50); + return (false); } -uint8_t um980GetActiveMessageCount() +//---------------------------------------- +// Some functions (L-Band area frequency determination) merely need to +// know if we have an RTK Float. This function checks to see if the +// given platform has reached sufficient fix type to be considered valid +//---------------------------------------- +bool RTK_UM980::isRTKFloat() { - uint8_t count = 0; - - count += um980GetActiveNmeaMessageCount(); - - count += um980GetActiveRtcmMessageCount(); - - return (count); + if (online.gnss) + // 34 = Narrow-land float solution + // 49 = Wide-lane fixed solution + return ((_um980->getPositionType() == 49) || (_um980->getPositionType() == 34)); + return (false); } -uint8_t um980GetActiveNmeaMessageCount() +//---------------------------------------- +// Determine if the survey-in operation is complete +//---------------------------------------- +bool RTK_UM980::isSurveyInComplete() { - uint8_t count = 0; - - for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) - if (settings.um980MessageRatesNMEA[x] > 0) - count++; - - return (count); + return (false); } -// Return true if the GPGGA message is active -bool um980IsGgaActive() +//---------------------------------------- +// Date will be valid if the RTC is reporting (regardless of GNSS fix) +//---------------------------------------- +bool RTK_UM980::isValidDate() { - if (settings.um980MessageRatesNMEA[um980GetNmeaMessageNumberByName("GPGGA")] > 0) - return (true); + if (online.gnss) + // 0 = Invalid + // 1 = valid + // 2 = leap second warning + return (_um980->getDateStatus() == 1); return (false); } -uint8_t um980GetActiveRtcmMessageCount() +//---------------------------------------- +// Time will be valid if the RTC is reporting (regardless of GNSS fix) +//---------------------------------------- +bool RTK_UM980::isValidTime() { - uint8_t count = 0; + if (online.gnss) + // 0 = valid + // 3 = invalid + return (_um980->getTimeStatus() == 0); + return (false); +} - // Determine which state we are in - if (um980InRoverMode() == true) - { - for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) - if (settings.um980MessageRatesRTCMRover[x] > 0) - count++; - } - else +//---------------------------------------- +// Controls the constellations that are used to generate a fix and logged +//---------------------------------------- +void RTK_UM980::menuConstellations() +{ + while (1) { - for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) - if (settings.um980MessageRatesRTCMBase[x] > 0) - count++; + systemPrintln(); + systemPrintln("Menu: Constellations"); + + for (int x = 0; x < MAX_UM980_CONSTELLATIONS; x++) + { + systemPrintf("%d) Constellation %s: ", x + 1, um980ConstellationCommands[x].textName); + if (settings.um980Constellations[x] > 0) + systemPrint("Enabled"); + else + systemPrint("Disabled"); + systemPrintln(); + } + + if (present.galileoHasCapable) + { + systemPrintf("%d) Galileo E6 Corrections: %s\r\n", MAX_UM980_CONSTELLATIONS + 1, + settings.enableGalileoHas ? "Enabled" : "Disabled"); + } + + systemPrintln("x) Exit"); + + int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long + + if (incoming >= 1 && incoming <= MAX_UM980_CONSTELLATIONS) + { + incoming--; // Align choice to constellation array of 0 to 5 + + settings.um980Constellations[incoming] ^= 1; + } + else if ((incoming == MAX_UM980_CONSTELLATIONS + 1) && present.galileoHasCapable) + { + settings.enableGalileoHas ^= 1; + } + else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) + break; + else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) + break; + else + printUnknown(incoming); } - return (count); + // Apply current settings to module + gnss->setConstellations(); + + clearBuffer(); // Empty buffer of any newline chars } +//---------------------------------------- +void RTK_UM980::menuMessageBaseRtcm() +{ + menuMessagesSubtype(settings.um980MessageRatesRTCMBase, "RTCMBase"); +} + +//---------------------------------------- // Control the messages that get broadcast over Bluetooth and logged (if enabled) -void um980MenuMessages() +//---------------------------------------- +void RTK_UM980::menuMessages() { while (1) { @@ -996,11 +1253,11 @@ void um980MenuMessages() int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long if (incoming == 1) - um980MenuMessagesSubtype(settings.um980MessageRatesNMEA, "NMEA"); + menuMessagesSubtype(settings.um980MessageRatesNMEA, "NMEA"); else if (incoming == 2) - um980MenuMessagesSubtype(settings.um980MessageRatesRTCMRover, "RTCMRover"); + menuMessagesSubtype(settings.um980MessageRatesRTCMRover, "RTCMRover"); else if (incoming == 3) - um980MenuMessagesSubtype(settings.um980MessageRatesRTCMBase, "RTCMBase"); + menuMessagesSubtype(settings.um980MessageRatesRTCMBase, "RTCMBase"); else if (incoming == 10) { // Reset rates to defaults @@ -1029,15 +1286,17 @@ void um980MenuMessages() clearBuffer(); // Empty buffer of any newline chars // Apply these changes at menu exit - if (um980InRoverMode() == true) + if (inRoverMode()) restartRover = true; else restartBase = true; } +//---------------------------------------- // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype // Controls the messages that get broadcast over Bluetooth and logged (if enabled) -void um980MenuMessagesSubtype(float *localMessageRate, const char *messageType) +//---------------------------------------- +void RTK_UM980::menuMessagesSubtype(float *localMessageRate, const char *messageType) { while (1) { @@ -1141,168 +1400,464 @@ void um980MenuMessagesSubtype(float *localMessageRate, const char *messageType) settings.updateGNSSSettings = true; // Update the GNSS config at the next boot - clearBuffer(); // Empty buffer of any newline chars + clearBuffer(); // Empty buffer of any newline chars +} + +//---------------------------------------- +// Print the module type and firmware version +//---------------------------------------- +void RTK_UM980::printModuleInfo() +{ + if (online.gnss) + { + uint8_t modelType = _um980->getModelType(); + + if (modelType == 18) + systemPrint("UM980"); + else + systemPrintf("Unicore Model Unknown %d", modelType); + + systemPrintf(" firmware: %s\r\n", _um980->getVersion()); + } +} + +//---------------------------------------- +// Send data directly from ESP GNSS UART1 to UM980 UART3 +// Returns the number of correction data bytes written +//---------------------------------------- +int RTK_UM980::pushRawData(uint8_t *dataToSend, int dataLength) +{ + if (online.gnss) + return (serialGNSS->write(dataToSend, dataLength)); + return (0); +} + +//---------------------------------------- +uint16_t RTK_UM980::rtcmBufferAvailable() +{ + // TODO return(um980RtcmBufferAvailable()); + return (0); +} + +//---------------------------------------- +// If LBand is being used, ignore any RTCM that may come in from the GNSS +//---------------------------------------- +void RTK_UM980::rtcmOnGnssDisable() +{ + // UM980 does not have a separate interface for RTCM +} + +//---------------------------------------- +// If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver +//---------------------------------------- +void RTK_UM980::rtcmOnGnssEnable() +{ + // UM980 does not have a separate interface for RTCM +} + +//---------------------------------------- +uint16_t RTK_UM980::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +{ + // TODO return(um980RtcmRead(rtcmBuffer, rtcmBytesToRead)); + return (0); +} + +//---------------------------------------- +// Save the current configuration +// Returns true when the configuration was saved and false upon failure +//---------------------------------------- +bool RTK_UM980::saveConfiguration() +{ + if (online.gnss) + return (_um980->saveConfiguration()); + return false; +} + +//---------------------------------------- +// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS +// This just sets the GNSS side +// Used during Bluetooth testing +//---------------------------------------- +bool RTK_UM980::setBaudrate(uint32_t baudRate) +{ + if (online.gnss) + // Set the baud rate on COM3 of the UM980 + return setBaudRateCOM3(baudRate); + return false; +} + +//---------------------------------------- +// Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS +//---------------------------------------- +bool RTK_UM980::setBaudRateCOM3(uint32_t baudRate) +{ + if (online.gnss) + return _um980->setPortBaudrate("COM3", baudRate); + return false; +} + +//---------------------------------------- +// Enable all the valid constellations and bands for this platform +// Band support varies between platforms and firmware versions +// We open/close a complete set 19 messages +//---------------------------------------- +bool RTK_UM980::setConstellations() +{ + bool response = true; + + for (int constellationNumber = 0; constellationNumber < MAX_UM980_CONSTELLATIONS; constellationNumber++) + { + if (settings.um980Constellations[constellationNumber]) + { + if (_um980->enableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) + { + if (settings.debugGnss) + systemPrintf("Enable constellation failed at constellationNumber %d %s.", constellationNumber, + um980ConstellationCommands[constellationNumber].textName); + response &= false; // If any one of the commands fails, report failure overall + } + } + else + { + if (_um980->disableConstellation(um980ConstellationCommands[constellationNumber].textCommand) == false) + { + if (settings.debugGnss) + systemPrintf("Disable constellation failed at constellationNumber %d %s.", constellationNumber, + um980ConstellationCommands[constellationNumber].textName); + response &= false; // If any one of the commands fails, report failure overall + } + } + } + + return (response); } -// Returns true if the device is in Rover mode -// Currently the only two modes are Rover or Base -bool um980InRoverMode() +//---------------------------------------- +bool RTK_UM980::setDataBaudRate(uint32_t baud) { - // Determine which state we are in - if (settings.lastState == STATE_BASE_NOT_STARTED) - return (false); - - return (true); // Default to Rover + return false; // UM980 has no multiplexer } -char *um980GetRtcmDefaultString() +//---------------------------------------- +// Set the elevation in degrees +//---------------------------------------- +bool RTK_UM980::setElevation(uint8_t elevationDegrees) { - return ((char *)"1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz"); + if (online.gnss) + return _um980->setElevationAngle(elevationDegrees); + return false; } -char *um980GetRtcmLowDataRateString() + +//---------------------------------------- +bool RTK_UM980::setHighAccuracyService(bool enableGalileoHas) { - return ((char *)"1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz"); + bool result = true; + + // Enable E6 and PPP if enabled and possible + if (settings.enableGalileoHas) + { + // E6 reception requires version 11833 or greater + int um980Version = String(_um980->getVersion()).toInt(); // Convert the string response to a value + if (um980Version >= 11833) + { + if (_um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS") == false) + { + if (_um980->sendCommand("CONFIG PPP ENABLE E6-HAS")) + systemPrintln("Galileo E6 service enabled"); + else + { + systemPrintln("Galileo E6 service failed to enable"); + result = false; + } + + if (_um980->sendCommand("CONFIG PPP DATUM WGS84")) + systemPrintln("WGS84 Datum applied"); + else + { + systemPrintln("WGS84 Datum failed to apply"); + result = false; + } + } + } + else + { + systemPrintf( + "Current UM980 firmware: v%d. Galileo E6 reception requires v11833 or newer. Please update the " + "firmware on your UM980 to allow for HAS operation. Please see https://bit.ly/sfe-rtk-um980-update\r\n", + um980Version); + // Don't fail the result. Module is still configured, just without HAS. + } + } + else + { + // Turn off HAS/E6 + if (_um980->isConfigurationPresent("CONFIG PPP ENABLE E6-HAS")) + { + if (_um980->sendCommand("CONFIG PPP DISABLE")) + systemPrintln("Galileo E6 service disabled"); + else + { + systemPrintln("Galileo E6 service failed to disable"); + result = false; + } + } + } + return (result); } -// Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz) -void um980BaseRtcmDefault() +//---------------------------------------- +// Enable all the valid messages for this platform +// There are many messages so split into batches. VALSET is limited to 64 max per batch +// Uses dummy newCfg and sendCfg values to be sure we open/close a complete set +//---------------------------------------- +bool RTK_UM980::setMessages(int maxRetries) { - // Reset RTCM rates to defaults - for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) - settings.um980MessageRatesRTCMBase[x] = umMessagesRTCM[x].msgDefaultRate; + // We probably don't need this for the UM980 + // TODO return(um980SetMessages(maxRetries)); + return (true); } -// Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz) -void um980BaseRtcmLowDataRate() +//---------------------------------------- +// Enable all the valid messages for this platform over the USB port +// Add 2 to every UART1 key. This is brittle and non-perfect, but works. +//---------------------------------------- +bool RTK_UM980::setMessagesUsb(int maxRetries) { - // Zero out everything - for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) - settings.um980MessageRatesRTCMBase[x] = 0; - - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1005")] = - 10; // 1005 0.1Hz - Exclude antenna height - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1074")] = 2; // 1074 0.5Hz - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1084")] = 2; // 1084 0.5Hz - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1094")] = 2; // 1094 0.5Hz - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1124")] = 2; // 1124 0.5Hz - settings.um980MessageRatesRTCMBase[um980GetRtcmMessageNumberByName("RTCM1033")] = 10; // 1033 0.1Hz + // We probably don't need this for the UM980 + // TODO return(um980SetMessagesUsb(maxRetries)); + return (true); } -// Given the name of an RTCM message, return the array number -uint8_t um980GetRtcmMessageNumberByName(const char *msgName) +//---------------------------------------- +// Set the minimum satellite signal level for navigation. +//---------------------------------------- +bool RTK_UM980::setMinCnoRadio (uint8_t cnoValue) { - for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) + if (online.gnss) { - if (strcmp(umMessagesRTCM[x].msgTextName, msgName) == 0) - return (x); + _um980->setMinCNO(cnoValue); + return true; } - - systemPrintf("um980GetRtcmMessageNumberByName: %s not found\r\n", msgName); - return (0); + return false; } -// Given the name of an NMEA message, return the array number -uint8_t um980GetNmeaMessageNumberByName(const char *msgName) +//---------------------------------------- +// Set the dynamic model to use for RTK +//---------------------------------------- +bool RTK_UM980::setModel(uint8_t modelNumber) { - for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) + if (online.gnss) { - if (strcmp(umMessagesNMEA[x].msgTextName, msgName) == 0) - return (x); + if (modelNumber == UM980_DYN_MODEL_SURVEY) + return (_um980->setModeRoverSurvey()); + else if (modelNumber == UM980_DYN_MODEL_UAV) + return (_um980->setModeRoverUAV()); + else if (modelNumber == UM980_DYN_MODEL_AUTOMOTIVE) + return (_um980->setModeRoverAutomotive()); } - - systemPrintf("um980GetNmeaMessageNumberByName: %s not found\r\n", msgName); - return (0); + return (false); } -// Controls the constellations that are used to generate a fix and logged -void um980MenuConstellations() +//---------------------------------------- +bool RTK_UM980::setMultipathMitigation(bool enableMultipathMitigation) { - while (1) - { - systemPrintln(); - systemPrintln("Menu: Constellations"); + bool result = true; - for (int x = 0; x < MAX_UM980_CONSTELLATIONS; x++) + // Enable MMP as required + if (enableMultipathMitigation) + { + if (_um980->isConfigurationPresent("CONFIG MMP ENABLE") == false) { - systemPrintf("%d) Constellation %s: ", x + 1, um980ConstellationCommands[x].textName); - if (settings.um980Constellations[x] > 0) - systemPrint("Enabled"); + if (_um980->sendCommand("CONFIG MMP ENABLE")) + systemPrintln("Multipath Mitigation enabled"); else - systemPrint("Disabled"); - systemPrintln(); + { + systemPrintln("Multipath Mitigation failed to enable"); + result = false; + } } - - if (present.galileoHasCapable) + } + else + { + // Turn off MMP + if (_um980->isConfigurationPresent("CONFIG MMP ENABLE")) { - systemPrintf("%d) Galileo E6 Corrections: %s\r\n", MAX_UM980_CONSTELLATIONS + 1, - settings.enableGalileoHas ? "Enabled" : "Disabled"); + if (_um980->sendCommand("CONFIG MMP DISABLE")) + systemPrintln("Multipath Mitigation disabled"); + else + { + systemPrintln("Multipath Mitigation failed to disable"); + result = false; + } } + } + return (result); +} - systemPrintln("x) Exit"); +//---------------------------------------- +bool RTK_UM980::setRadioBaudRate(uint32_t baud) +{ + return false; // UM980 has no multiplexer +} - int incoming = getUserInputNumber(); // Returns EXIT, TIMEOUT, or long +//---------------------------------------- +// Given the number of seconds between desired solution reports, determine measurementRateMs and navigationRate +// measurementRateS > 25 & <= 65535 +// navigationRate >= 1 && <= 127 +// We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. +//---------------------------------------- +bool RTK_UM980::setRate(double secondsBetweenSolutions) +{ + // The UM980 does not have a rate setting. Instead the report rate of + // the GNSS messages can be set. For example, 0.5 is 2Hz, 0.2 is 5Hz. + // We assume, if the user wants to set the 'rate' to 5Hz, they want all + // messages set to that rate. + // All NMEA/RTCM for a rover will be based on the measurementRateMs setting + // ie, if a message != 0, then it will be output at the measurementRate. + // All RTCM for a base will be based on a measurementRateMs of 1000 with messages + // that can be reported more slowly than that (ie 1 per 10 seconds). + bool response = true; - if (incoming >= 1 && incoming <= MAX_UM980_CONSTELLATIONS) - { - incoming--; // Align choice to constellation array of 0 to 5 + disableAllOutput(); - settings.um980Constellations[incoming] ^= 1; + // Overwrite any enabled messages with this rate + for (int messageNumber = 0; messageNumber < MAX_UM980_NMEA_MSG; messageNumber++) + { + if (settings.um980MessageRatesNMEA[messageNumber] > 0) + { + settings.um980MessageRatesNMEA[messageNumber] = secondsBetweenSolutions; } - else if ((incoming == MAX_UM980_CONSTELLATIONS + 1) && present.galileoHasCapable) + } + response &= enableNMEA(); // Enact these rates + + // TODO We don't know what state we are in, so we don't + // know which RTCM settings to update. Assume we are + // in rover for now + for (int messageNumber = 0; messageNumber < MAX_UM980_RTCM_MSG; messageNumber++) + { + if (settings.um980MessageRatesRTCMRover[messageNumber] > 0) { - settings.enableGalileoHas ^= 1; + settings.um980MessageRatesRTCMRover[messageNumber] = secondsBetweenSolutions; } - else if (incoming == INPUT_RESPONSE_GETNUMBER_EXIT) - break; - else if (incoming == INPUT_RESPONSE_GETNUMBER_TIMEOUT) - break; - else - printUnknown(incoming); } + response &= enableRTCMRover(); // Enact these rates - // Apply current settings to module - gnss->setConstellations(); + // If we successfully set rates, only then record to settings + if (response) + { + uint16_t msBetweenSolutions = secondsBetweenSolutions * 1000; + settings.measurementRateMs = msBetweenSolutions; + } + else + { + systemPrintln("Failed to set measurement and navigation rates"); + return (false); + } - clearBuffer(); // Empty buffer of any newline chars + return (true); } -#endif // COMPILE_UM980 +//---------------------------------------- +bool RTK_UM980::setTalkerGNGGA() +{ + // TODO um980SetTalkerGNGGA(); + return false; +} -// Check if updateUm980Firmware.txt exists -bool checkUpdateUm980Firmware() +//---------------------------------------- +// Hotstart GNSS to try to get RTK lock +//---------------------------------------- +bool RTK_UM980::softwareReset() { - if (online.fs == false) - return false; + return false; +} - if (LittleFS.exists("/updateUm980Firmware.txt")) +//---------------------------------------- +bool RTK_UM980::standby() +{ + return true; +} + +//---------------------------------------- +// Slightly modified method for restarting survey-in from: +// https://portal.u-blox.com/s/question/0D52p00009IsVoMCAV/restarting-surveyin-on-an-f9p +//---------------------------------------- +bool RTK_UM980::surveyInReset() +{ + if (online.gnss) + return (_um980->setModeRoverSurvey()); + return false; +} + +//---------------------------------------- +// Start the survey-in operation +// The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. +//---------------------------------------- +bool RTK_UM980::surveyInStart() +{ + if (online.gnss) { - if (settings.debugGnss) - systemPrintln("LittleFS updateUm980Firmware.txt exists"); + bool response = true; - // We do not remove the file here. See removeupdateUm980Firmware(). + // Start a Self-optimizing Base Station + // We do not use the distance parameter (settings.observationPositionAccuracy) because that + // setting on the UM980 is related to automatically restarting base mode + // at power on (very different from ZED-F9P). + response &= + _um980->setModeBaseAverage(settings.observationSeconds); // Average for a number of seconds (default is 60) - return true; - } + _autoBaseStartTimer = millis(); // Stamp when averaging began + + if (response == false) + { + systemPrintln("Survey start failed"); + return (false); + } + return (response); + } return false; } -void removeUpdateUm980Firmware() +//---------------------------------------- +// If we have received serial data from the UM980 outside of the Unicore library (ie, from processUart1Message task) +// we can pass data back into the Unicore library to allow it to update its own variables +//---------------------------------------- +void um980UnicoreHandler(uint8_t *buffer, int length) { - if (online.fs == false) - return; + RTK_UM980 * um980 = (RTK_UM980 *)gnss; + um980->unicoreHandler(buffer, length); +} - if (LittleFS.exists("/updateUm980Firmware.txt")) - { - if (settings.debugGnss) - systemPrintln("Removing updateUm980Firmware.txt "); +//---------------------------------------- +// If we have received serial data from the UM980 outside of the Unicore library (ie, from processUart1Message task) +// we can pass data back into the Unicore library to allow it to update its own variables +//---------------------------------------- +void RTK_UM980::unicoreHandler(uint8_t *buffer, int length) +{ + _um980->unicoreHandler(buffer, length); +} - LittleFS.remove("/updateUm980Firmware.txt"); - } +//---------------------------------------- +// Poll routine to update the GNSS state +//---------------------------------------- +void RTK_UM980::update() +{ + // We don't check serial data here; the gnssReadTask takes care of serial consumption +} + +#endif // COMPILE_UM980 + +//---------------------------------------- +void um980Boot() +{ + digitalWrite(pin_GNSS_DR_Reset, HIGH); // Tell UM980 and DR to boot } +//---------------------------------------- // Force UART connection to UM980 for firmware update on the next boot by creating updateUm980Firmware.txt in // LittleFS +//---------------------------------------- bool createUm980Passthrough() { if (online.fs == false) @@ -1326,7 +1881,8 @@ bool createUm980Passthrough() return false; } -void beginUm980FirmwareUpdate() +//---------------------------------------- +void um980FirmwareBeginUpdate() { // Note: We cannot increase the bootloading speed beyond 115200 because // we would need to alter the UM980 baud, then save to NVM, then allow the UM980 to reset. @@ -1392,7 +1948,7 @@ void beginUm980FirmwareUpdate() delay(100); // Remove file and reset to exit pass-through mode - removeUpdateUm980Firmware(); + um980FirmwareRemoveUpdate(); // Beep to indicate exit beepOn(); @@ -1412,3 +1968,45 @@ void beginUm980FirmwareUpdate() systemFlush(); // Complete prints } + +//---------------------------------------- +// Check if updateUm980Firmware.txt exists +//---------------------------------------- +bool um980FirmwareCheckUpdate() +{ + if (online.fs == false) + return false; + + if (LittleFS.exists("/updateUm980Firmware.txt")) + { + if (settings.debugGnss) + systemPrintln("LittleFS updateUm980Firmware.txt exists"); + + // We do not remove the file here. See removeupdateUm980Firmware(). + + return true; + } + + return false; +} + +//---------------------------------------- +void um980FirmwareRemoveUpdate() +{ + if (online.fs == false) + return; + + if (LittleFS.exists("/updateUm980Firmware.txt")) + { + if (settings.debugGnss) + systemPrintln("Removing updateUm980Firmware.txt "); + + LittleFS.remove("/updateUm980Firmware.txt"); + } +} + +//---------------------------------------- +void um980Reset() +{ + digitalWrite(pin_GNSS_DR_Reset, LOW); // Tell UM980 and DR to reset +} From 06c1c434738e0456ecea6c9913f357737a715a42 Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Oct 2024 10:40:52 -1000 Subject: [PATCH 4/5] Implement the GNSS_ZED class --- Firmware/RTK_Everywhere/Begin.ino | 2 +- Firmware/RTK_Everywhere/Form.ino | 2 +- Firmware/RTK_Everywhere/{ZED.h => GNSS_ZED.h} | 12 +- .../RTK_Everywhere/{ZED.ino => GNSS_ZED.ino} | 210 +++++++++--------- Firmware/RTK_Everywhere/MQTT_Client.ino | 2 +- Firmware/RTK_Everywhere/NVM.ino | 4 +- .../RTK_Everywhere/PointPerfectLibrary.ino | 2 +- Firmware/RTK_Everywhere/RTK_Everywhere.ino | 2 +- Firmware/RTK_Everywhere/menuCommands.ino | 8 +- Firmware/RTK_Everywhere/menuMessages.ino | 6 +- Firmware/RTK_Everywhere/menuPP.ino | 4 +- 11 files changed, 127 insertions(+), 127 deletions(-) rename Firmware/RTK_Everywhere/{ZED.h => GNSS_ZED.h} (98%) rename Firmware/RTK_Everywhere/{ZED.ino => GNSS_ZED.ino} (95%) diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 7ae0b5ccd..c03533be4 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -284,7 +284,7 @@ void beginBoard() else if (productVariant == RTK_EVK) { // Specify the GNSS radio - gnss = (GNSS *) new ZED(); + gnss = (GNSS *) new GNSS_ZED(); // Pin defs etc. for EVK v1.1 present.psram_4mb = true; diff --git a/Firmware/RTK_Everywhere/Form.ino b/Firmware/RTK_Everywhere/Form.ino index a8829f9bd..55ed46608 100644 --- a/Firmware/RTK_Everywhere/Form.ino +++ b/Firmware/RTK_Everywhere/Form.ino @@ -1108,7 +1108,7 @@ void createMessageListBase(String &returnText) if (present.gnss_zedf9p) { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int messageNumber = 0; messageNumber < MAX_UBX_MSG_RTCM; messageNumber++) diff --git a/Firmware/RTK_Everywhere/ZED.h b/Firmware/RTK_Everywhere/GNSS_ZED.h similarity index 98% rename from Firmware/RTK_Everywhere/ZED.h rename to Firmware/RTK_Everywhere/GNSS_ZED.h index 85d77c124..b9da553b2 100644 --- a/Firmware/RTK_Everywhere/ZED.h +++ b/Firmware/RTK_Everywhere/GNSS_ZED.h @@ -1,15 +1,15 @@ /*------------------------------------------------------------------------------ -ZED.h +GNSS_ZED.h - Declarations and definitions for the ZED implementation + Declarations and definitions for the GNSS_ZED implementation ------------------------------------------------------------------------------*/ -#ifndef __ZED_H__ -#define __ZED_H__ +#ifndef __GNSS_ZED_H__ +#define __GNSS_ZED_H__ #include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 -class ZED : GNSS +class GNSS_ZED : GNSS { private: @@ -370,4 +370,4 @@ class ZED : GNSS void updateCorrectionsSource(uint8_t source); }; -#endif // __ZED_H__ +#endif // __GNSS_ZED_H__ diff --git a/Firmware/RTK_Everywhere/ZED.ino b/Firmware/RTK_Everywhere/GNSS_ZED.ino similarity index 95% rename from Firmware/RTK_Everywhere/ZED.ino rename to Firmware/RTK_Everywhere/GNSS_ZED.ino index 423e112c5..053461ed8 100644 --- a/Firmware/RTK_Everywhere/ZED.ino +++ b/Firmware/RTK_Everywhere/GNSS_ZED.ino @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------------ -ZED.ino +GNSS_ZED.ino - Implementation of the ZED class + Implementation of the GNSS_ZED class ------------------------------------------------------------------------------*/ //---------------------------------------- // If we have decryption keys, configure module // Note: don't check online.lband_neo here. We could be using ip corrections //---------------------------------------- -void ZED::applyPointPerfectKeys() +void GNSS_ZED::applyPointPerfectKeys() { if (online.gnss == false) { @@ -82,7 +82,7 @@ void ZED::applyPointPerfectKeys() //---------------------------------------- // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz) //---------------------------------------- -void ZED::baseRtcmDefault() +void GNSS_ZED::baseRtcmDefault() { int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 1; // 1105 @@ -104,7 +104,7 @@ void ZED::baseRtcmDefault() //---------------------------------------- // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz) //---------------------------------------- -void ZED::baseRtcmLowDataRate() +void GNSS_ZED::baseRtcmLowDataRate() { int firstRTCMRecord = getMessageNumberByName("RTCM_1005"); settings.ubxMessageRatesBase[getMessageNumberByName("RTCM_1005") - firstRTCMRecord] = 10; // 1105 0.1Hz @@ -126,7 +126,7 @@ void ZED::baseRtcmLowDataRate() //---------------------------------------- // Connect to GNSS and identify particulars //---------------------------------------- -void ZED::begin() +void GNSS_ZED::begin() { // Instantiate the library if (_zed == nullptr) @@ -224,7 +224,7 @@ void ZED::begin() // Setup the timepulse output on the PPS pin for external triggering // Setup TM2 time stamp input as need //---------------------------------------- -bool ZED::beginExternalEvent() +bool GNSS_ZED::beginExternalEvent() { if (online.gnss == false) return (false); @@ -255,7 +255,7 @@ bool ZED::beginExternalEvent() //---------------------------------------- // Setup the timepulse output on the PPS pin for external triggering //---------------------------------------- -bool ZED::beginPPS() +bool GNSS_ZED::beginPPS() { if (online.gnss == false) return (false); @@ -300,7 +300,7 @@ bool ZED::beginPPS() } //---------------------------------------- -bool ZED::checkNMEARates() +bool GNSS_ZED::checkNMEARates() { if (online.gnss) return (getMessageRateByName("NMEA_GGA") > 0 && getMessageRateByName("NMEA_GSA") > 0 && @@ -310,7 +310,7 @@ bool ZED::checkNMEARates() } //---------------------------------------- -bool ZED::checkPPPRates() +bool GNSS_ZED::checkPPPRates() { if (online.gnss) return (getMessageRateByName("RXM_RAWX") > 0 && getMessageRateByName("RXM_SFRBX") > 0); @@ -320,7 +320,7 @@ bool ZED::checkPPPRates() //---------------------------------------- // Configure specific aspects of the receiver for base mode //---------------------------------------- -bool ZED::configureBase() +bool GNSS_ZED::configureBase() { if (online.gnss == false) return (false); @@ -424,7 +424,7 @@ bool ZED::configureBase() //---------------------------------------- // Configure specific aspects of the receiver for NTP mode //---------------------------------------- -bool ZED::configureNtpMode() +bool GNSS_ZED::configureNtpMode() { bool success = false; @@ -507,7 +507,7 @@ bool ZED::configureNtpMode() // In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare // occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. //---------------------------------------- -bool ZED::configureRadio() +bool GNSS_ZED::configureRadio() { if (online.gnss == false) return (false); @@ -739,7 +739,7 @@ bool ZED::configureRadio() //---------------------------------------- // Configure specific aspects of the receiver for rover mode //---------------------------------------- -bool ZED::configureRover() +bool GNSS_ZED::configureRover() { if (online.gnss == false) { @@ -835,14 +835,14 @@ bool ZED::configureRover() } //---------------------------------------- -void ZED::debuggingDisable() +void GNSS_ZED::debuggingDisable() { if (online.gnss) _zed->disableDebugging(); } //---------------------------------------- -void ZED::debuggingEnable() +void GNSS_ZED::debuggingEnable() { if (online.gnss) // Enable only the critical debug messages over Serial @@ -850,7 +850,7 @@ void ZED::debuggingEnable() } //---------------------------------------- -void ZED::enableGgaForNtrip() +void GNSS_ZED::enableGgaForNtrip() { if (online.gnss) { @@ -872,7 +872,7 @@ void ZED::enableGgaForNtrip() // even if there is no GPS fix. We use it to test serial output. // Returns true if successfully started and false upon failure //---------------------------------------- -bool ZED::enableRTCMTest() +bool GNSS_ZED::enableRTCMTest() { if (online.gnss) { @@ -887,7 +887,7 @@ bool ZED::enableRTCMTest() //---------------------------------------- // Restore the GNSS to the factory settings //---------------------------------------- -void ZED::factoryReset() +void GNSS_ZED::factoryReset() { if (online.gnss) { @@ -898,7 +898,7 @@ void ZED::factoryReset() } //---------------------------------------- -uint16_t ZED::fileBufferAvailable() +uint16_t GNSS_ZED::fileBufferAvailable() { if (online.gnss) return (_zed->fileBufferAvailable()); @@ -906,7 +906,7 @@ uint16_t ZED::fileBufferAvailable() } //---------------------------------------- -uint16_t ZED::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +uint16_t GNSS_ZED::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) { if (online.gnss) { @@ -920,7 +920,7 @@ uint16_t ZED::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) //---------------------------------------- // Start the base using fixed coordinates //---------------------------------------- -bool ZED::fixedBaseStart() +bool GNSS_ZED::fixedBaseStart() { bool response = true; @@ -1006,7 +1006,7 @@ bool ZED::fixedBaseStart() //---------------------------------------- // Return the number of active/enabled messages //---------------------------------------- -uint8_t ZED::getActiveMessageCount() +uint8_t GNSS_ZED::getActiveMessageCount() { uint8_t count = 0; @@ -1019,7 +1019,7 @@ uint8_t ZED::getActiveMessageCount() //---------------------------------------- // Returns the altitude in meters or zero if the GNSS is offline //---------------------------------------- -double ZED::getAltitude() +double GNSS_ZED::getAltitude() { return _altitude; } @@ -1027,13 +1027,13 @@ double ZED::getAltitude() //---------------------------------------- // Returns the carrier solution or zero if not online //---------------------------------------- -uint8_t ZED::getCarrierSolution() +uint8_t GNSS_ZED::getCarrierSolution() { return (_carrierSolution); } //---------------------------------------- -uint32_t ZED::getDataBaudRate() +uint32_t GNSS_ZED::getDataBaudRate() { if (online.gnss) return _zed->getVal32(UBLOX_CFG_UART1_BAUDRATE); @@ -1043,7 +1043,7 @@ uint32_t ZED::getDataBaudRate() //---------------------------------------- // Returns the day number or zero if not online //---------------------------------------- -uint8_t ZED::getDay() +uint8_t GNSS_ZED::getDay() { return (_day); } @@ -1051,7 +1051,7 @@ uint8_t ZED::getDay() //---------------------------------------- // Return the number of milliseconds since GNSS data was last updated //---------------------------------------- -uint16_t ZED::getFixAgeMilliseconds() +uint16_t GNSS_ZED::getFixAgeMilliseconds() { return (millis() - _pvtArrivalMillis); } @@ -1059,7 +1059,7 @@ uint16_t ZED::getFixAgeMilliseconds() //---------------------------------------- // Returns the fix type or zero if not online //---------------------------------------- -uint8_t ZED::getFixType() +uint8_t GNSS_ZED::getFixType() { return (_fixType); } @@ -1068,7 +1068,7 @@ uint8_t ZED::getFixType() // Get the horizontal position accuracy // Returns the horizontal position accuracy or zero if offline //---------------------------------------- -float ZED::getHorizontalAccuracy() +float GNSS_ZED::getHorizontalAccuracy() { return (_horizontalAccuracy); } @@ -1076,13 +1076,13 @@ float ZED::getHorizontalAccuracy() //---------------------------------------- // Returns the hours of 24 hour clock or zero if not online //---------------------------------------- -uint8_t ZED::getHour() +uint8_t GNSS_ZED::getHour() { return (_hour); } //---------------------------------------- -const char * ZED::getId() +const char * GNSS_ZED::getId() { if (online.gnss) return (gnssUniqueId); @@ -1093,7 +1093,7 @@ const char * ZED::getId() // Get the latitude value // Returns the latitude value or zero if not online //---------------------------------------- -double ZED::getLatitude() +double GNSS_ZED::getLatitude() { return (_latitude); } @@ -1101,7 +1101,7 @@ double ZED::getLatitude() //---------------------------------------- // Query GNSS for current leap seconds //---------------------------------------- -uint8_t ZED::getLeapSeconds() +uint8_t GNSS_ZED::getLeapSeconds() { if (online.gnss) { @@ -1117,7 +1117,7 @@ uint8_t ZED::getLeapSeconds() // Outputs: // Returns the longitude value or zero if not online //---------------------------------------- -double ZED::getLongitude() +double GNSS_ZED::getLongitude() { return (_longitude); } @@ -1125,7 +1125,7 @@ double ZED::getLongitude() //---------------------------------------- // Given the name of a message, return the array number //---------------------------------------- -uint8_t ZED::getMessageNumberByName(const char *msgName) +uint8_t GNSS_ZED::getMessageNumberByName(const char *msgName) { if (present.gnss_zedf9p) { @@ -1145,7 +1145,7 @@ uint8_t ZED::getMessageNumberByName(const char *msgName) //---------------------------------------- // Given the name of a message, find it, and return the rate //---------------------------------------- -uint8_t ZED::getMessageRateByName(const char *msgName) +uint8_t GNSS_ZED::getMessageRateByName(const char *msgName) { return (settings.ubxMessageRates[getMessageNumberByName(msgName)]); } @@ -1153,7 +1153,7 @@ uint8_t ZED::getMessageRateByName(const char *msgName) //---------------------------------------- // Returns two digits of milliseconds or zero if not online //---------------------------------------- -uint8_t ZED::getMillisecond() +uint8_t GNSS_ZED::getMillisecond() { return (_millisecond); } @@ -1161,7 +1161,7 @@ uint8_t ZED::getMillisecond() //---------------------------------------- // Returns minutes or zero if not online //---------------------------------------- -uint8_t ZED::getMinute() +uint8_t GNSS_ZED::getMinute() { return (_minute); } @@ -1169,7 +1169,7 @@ uint8_t ZED::getMinute() //---------------------------------------- // Returns month number or zero if not online //---------------------------------------- -uint8_t ZED::getMonth() +uint8_t GNSS_ZED::getMonth() { return (_month); } @@ -1177,7 +1177,7 @@ uint8_t ZED::getMonth() //---------------------------------------- // Returns nanoseconds or zero if not online //---------------------------------------- -uint32_t ZED::getNanosecond() +uint32_t GNSS_ZED::getNanosecond() { return (_nanosecond); } @@ -1186,7 +1186,7 @@ uint32_t ZED::getNanosecond() // Count the number of NAV2 messages with rates more than 0. Used for determining if we need the enable // the global NAV2 feature. //---------------------------------------- -uint8_t ZED::getNAV2MessageCount() +uint8_t GNSS_ZED::getNAV2MessageCount() { int enabledMessages = 0; int startOfBlock = 0; @@ -1214,7 +1214,7 @@ uint8_t ZED::getNAV2MessageCount() } //---------------------------------------- -uint32_t ZED::getRadioBaudRate() +uint32_t GNSS_ZED::getRadioBaudRate() { if (online.gnss) return _zed->getVal32(UBLOX_CFG_UART2_BAUDRATE); @@ -1224,7 +1224,7 @@ uint32_t ZED::getRadioBaudRate() //---------------------------------------- // Returns the seconds between measurements //---------------------------------------- -double ZED::getRateS() +double GNSS_ZED::getRateS() { // Because we may be in base mode, do not get freq from module, use settings instead float measurementFrequency = (1000.0 / settings.measurementRateMs) / settings.navigationRate; @@ -1234,13 +1234,13 @@ double ZED::getRateS() } //---------------------------------------- -const char * ZED::getRtcmDefaultString() +const char * GNSS_ZED::getRtcmDefaultString() { return ((char *)"1005/1074/1084/1094/1124 1Hz & 1230 0.1Hz"); } //---------------------------------------- -const char * ZED::getRtcmLowDataRateString() +const char * GNSS_ZED::getRtcmLowDataRateString() { return ((char *)"1074/1084/1094/1124 0.5Hz & 1005/1230 0.1Hz"); } @@ -1248,7 +1248,7 @@ const char * ZED::getRtcmLowDataRateString() //---------------------------------------- // Returns the number of satellites in view or zero if offline //---------------------------------------- -uint8_t ZED::getSatellitesInView() +uint8_t GNSS_ZED::getSatellitesInView() { return (_satellitesInView); } @@ -1256,7 +1256,7 @@ uint8_t ZED::getSatellitesInView() //---------------------------------------- // Returns seconds or zero if not online //---------------------------------------- -uint8_t ZED::getSecond() +uint8_t GNSS_ZED::getSecond() { return (_second); } @@ -1264,7 +1264,7 @@ uint8_t ZED::getSecond() //---------------------------------------- // Get the survey-in mean accuracy //---------------------------------------- -float ZED::getSurveyInMeanAccuracy() +float GNSS_ZED::getSurveyInMeanAccuracy() { static float svinMeanAccuracy = 0; static unsigned long lastCheck = 0; @@ -1285,7 +1285,7 @@ float ZED::getSurveyInMeanAccuracy() //---------------------------------------- // Return the number of seconds the survey-in process has been running //---------------------------------------- -int ZED::getSurveyInObservationTime() +int GNSS_ZED::getSurveyInObservationTime() { static uint16_t svinObservationTime = 0; static unsigned long lastCheck = 0; @@ -1306,7 +1306,7 @@ int ZED::getSurveyInObservationTime() //---------------------------------------- // Returns timing accuracy or zero if not online //---------------------------------------- -uint32_t ZED::getTimeAccuracy() +uint32_t GNSS_ZED::getTimeAccuracy() { return (_tAcc); } @@ -1314,13 +1314,13 @@ uint32_t ZED::getTimeAccuracy() //---------------------------------------- // Returns full year, ie 2023, not 23. //---------------------------------------- -uint16_t ZED::getYear() +uint16_t GNSS_ZED::getYear() { return (_year); } //---------------------------------------- -bool ZED::isBlocking() +bool GNSS_ZED::isBlocking() { return (false); } @@ -1328,7 +1328,7 @@ bool ZED::isBlocking() //---------------------------------------- // Date is confirmed once we have GNSS fix //---------------------------------------- -bool ZED::isConfirmedDate() +bool GNSS_ZED::isConfirmedDate() { return (_confirmedDate); } @@ -1336,7 +1336,7 @@ bool ZED::isConfirmedDate() //---------------------------------------- // Time is confirmed once we have GNSS fix //---------------------------------------- -bool ZED::isConfirmedTime() +bool GNSS_ZED::isConfirmedTime() { return (_confirmedTime); } @@ -1344,7 +1344,7 @@ bool ZED::isConfirmedTime() //---------------------------------------- // Return true if GNSS receiver has a higher quality DGPS fix than 3D //---------------------------------------- -bool ZED::isDgpsFixed() +bool GNSS_ZED::isDgpsFixed() { // Not supported return (false); @@ -1354,7 +1354,7 @@ bool ZED::isDgpsFixed() // Some functions (L-Band area frequency determination) merely need to know if we have a valid fix, not what type of fix // This function checks to see if the given platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool ZED::isFixed() +bool GNSS_ZED::isFixed() { // 0 = no fix // 1 = dead reckoning only @@ -1368,19 +1368,19 @@ bool ZED::isFixed() //---------------------------------------- // Used in tpISR() for time pulse synchronization //---------------------------------------- -bool ZED::isFullyResolved() +bool GNSS_ZED::isFullyResolved() { return (_fullyResolved); } //---------------------------------------- -bool ZED::isPppConverged() +bool GNSS_ZED::isPppConverged() { return (false); } //---------------------------------------- -bool ZED::isPppConverging() +bool GNSS_ZED::isPppConverging() { return (false); } @@ -1390,7 +1390,7 @@ bool ZED::isPppConverging() // know if we have an RTK Fix. This function checks to see if the given // platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool ZED::isRTKFix() +bool GNSS_ZED::isRTKFix() { // 0 = No RTK // 1 = RTK Float @@ -1403,7 +1403,7 @@ bool ZED::isRTKFix() // know if we have an RTK Float. This function checks to see if the // given platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool ZED::isRTKFloat() +bool GNSS_ZED::isRTKFloat() { // 0 = No RTK // 1 = RTK Float @@ -1414,7 +1414,7 @@ bool ZED::isRTKFloat() //---------------------------------------- // Determine if the survey-in operation is complete //---------------------------------------- -bool ZED::isSurveyInComplete() +bool GNSS_ZED::isSurveyInComplete() { if (online.gnss) return (_zed->getSurveyInValid(50)); @@ -1424,7 +1424,7 @@ bool ZED::isSurveyInComplete() //---------------------------------------- // Date will be valid if the RTC is reporting (regardless of GNSS fix) //---------------------------------------- -bool ZED::isValidDate() +bool GNSS_ZED::isValidDate() { return (_validDate); } @@ -1432,7 +1432,7 @@ bool ZED::isValidDate() //---------------------------------------- // Time will be valid if the RTC is reporting (regardless of GNSS fix) //---------------------------------------- -bool ZED::isValidTime() +bool GNSS_ZED::isValidTime() { return (_validTime); } @@ -1440,7 +1440,7 @@ bool ZED::isValidTime() //---------------------------------------- // Disable data output from the NEO //---------------------------------------- -bool ZED::lBandCommunicationDisable() +bool GNSS_ZED::lBandCommunicationDisable() { bool response = true; @@ -1480,7 +1480,7 @@ bool ZED::lBandCommunicationDisable() //---------------------------------------- // Enable data output from the NEO //---------------------------------------- -bool ZED::lBandCommunicationEnable() +bool GNSS_ZED::lBandCommunicationEnable() { /* Paul's Notes on (NEO-D9S) L-Band: @@ -1560,7 +1560,7 @@ bool ZED::lBandCommunicationEnable() } //---------------------------------------- -bool ZED::lock(void) +bool GNSS_ZED::lock(void) { if (!iAmLocked) { @@ -1582,20 +1582,20 @@ bool ZED::lock(void) } //---------------------------------------- -bool ZED::lockCreate(void) +bool GNSS_ZED::lockCreate(void) { return true; } //---------------------------------------- -void ZED::lockDelete(void) +void GNSS_ZED::lockDelete(void) { } //---------------------------------------- // Controls the constellations that are used to generate a fix and logged //---------------------------------------- -void ZED::menuConstellations() +void GNSS_ZED::menuConstellations() { while (1) { @@ -1650,7 +1650,7 @@ void ZED::menuConstellations() } //---------------------------------------- -void ZED::menuMessageBaseRtcm() +void GNSS_ZED::menuMessageBaseRtcm() { zedMenuMessagesSubtype(settings.ubxMessageRatesBase, "RTCM-Base"); } @@ -1658,7 +1658,7 @@ void ZED::menuMessageBaseRtcm() //---------------------------------------- // Control the messages that get broadcast over Bluetooth and logged (if enabled) //---------------------------------------- -void ZED::menuMessages() +void GNSS_ZED::menuMessages() { while (1) { @@ -1773,7 +1773,7 @@ void ZED::menuMessages() //---------------------------------------- // Print the module type and firmware version //---------------------------------------- -void ZED::printModuleInfo() +void GNSS_ZED::printModuleInfo() { systemPrintf("ZED-F9P firmware: %s\r\n", gnssFirmwareVersion); } @@ -1782,7 +1782,7 @@ void ZED::printModuleInfo() // Send correction data to the GNSS // Returns the number of correction data bytes written //---------------------------------------- -int ZED::pushRawData(uint8_t *dataToSend, int dataLength) +int GNSS_ZED::pushRawData(uint8_t *dataToSend, int dataLength) { if (online.gnss) return (_zed->pushRawData((uint8_t *)dataToSend, dataLength)); @@ -1790,7 +1790,7 @@ int ZED::pushRawData(uint8_t *dataToSend, int dataLength) } //---------------------------------------- -uint16_t ZED::rtcmBufferAvailable() +uint16_t GNSS_ZED::rtcmBufferAvailable() { if (online.gnss) return (_zed->rtcmBufferAvailable()); @@ -1800,7 +1800,7 @@ uint16_t ZED::rtcmBufferAvailable() //---------------------------------------- // If LBand is being used, ignore any RTCM that may come in from the GNSS //---------------------------------------- -void ZED::rtcmOnGnssDisable() +void GNSS_ZED::rtcmOnGnssDisable() { if (online.gnss) _zed->setUART2Input(COM_TYPE_UBX); // Set ZED's UART2 to input UBX (no RTCM) @@ -1809,14 +1809,14 @@ void ZED::rtcmOnGnssDisable() //---------------------------------------- // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver //---------------------------------------- -void ZED::rtcmOnGnssEnable() +void GNSS_ZED::rtcmOnGnssEnable() { if (online.gnss) _zed->setUART2Input(COM_TYPE_RTCM3); // Set the ZED's UART2 to input RTCM } //---------------------------------------- -uint16_t ZED::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +uint16_t GNSS_ZED::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) { if (online.gnss) return (_zed->extractRTCMBufferData(rtcmBuffer, rtcmBytesToRead)); @@ -1827,7 +1827,7 @@ uint16_t ZED::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) // Save the current configuration // Returns true when the configuration was saved and false upon failure //---------------------------------------- -bool ZED::saveConfiguration() +bool GNSS_ZED::saveConfiguration() { if (online.gnss == false) return false; @@ -1841,7 +1841,7 @@ bool ZED::saveConfiguration() // This just sets the GNSS side // Used during Bluetooth testing //---------------------------------------- -bool ZED::setBaudrate(uint32_t baudRate) +bool GNSS_ZED::setBaudrate(uint32_t baudRate) { if (online.gnss) return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, @@ -1854,7 +1854,7 @@ bool ZED::setBaudrate(uint32_t baudRate) // Band support varies between platforms and firmware versions // We open/close a complete set of 19 messages //---------------------------------------- -bool ZED::setConstellations() +bool GNSS_ZED::setConstellations() { if (online.gnss == false) return (false); @@ -1925,7 +1925,7 @@ bool ZED::setConstellations() } //---------------------------------------- -bool ZED::setDataBaudRate(uint32_t baud) +bool GNSS_ZED::setDataBaudRate(uint32_t baud) { if (online.gnss) return _zed->setVal32(UBLOX_CFG_UART1_BAUDRATE, baud); @@ -1935,7 +1935,7 @@ bool ZED::setDataBaudRate(uint32_t baud) //---------------------------------------- // Set the elevation in degrees //---------------------------------------- -bool ZED::setElevation(uint8_t elevationDegrees) +bool GNSS_ZED::setElevation(uint8_t elevationDegrees) { if (online.gnss) { @@ -1948,7 +1948,7 @@ bool ZED::setElevation(uint8_t elevationDegrees) //---------------------------------------- // Given a unique string, find first and last records containing that string in message array //---------------------------------------- -void ZED::setMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock) +void GNSS_ZED::setMessageOffsets(const ubxMsg *localMessage, const char *messageType, int &startOfBlock, int &endOfBlock) { char messageNamePiece[40]; // UBX_RTCM snprintf(messageNamePiece, sizeof(messageNamePiece), "%s", messageType); // Put UBX_ infront of type @@ -1978,7 +1978,7 @@ void ZED::setMessageOffsets(const ubxMsg *localMessage, const char *messageType, //---------------------------------------- // Given the name of a message, find it, and set the rate //---------------------------------------- -bool ZED::setMessageRateByName(const char *msgName, uint8_t msgRate) +bool GNSS_ZED::setMessageRateByName(const char *msgName, uint8_t msgRate) { for (int x = 0; x < MAX_UBX_MSG; x++) { @@ -1997,7 +1997,7 @@ bool ZED::setMessageRateByName(const char *msgName, uint8_t msgRate) // There are many messages so split into batches. VALSET is limited to 64 max per batch // Uses dummy newCfg and sendCfg values to be sure we open/close a complete set //---------------------------------------- -bool ZED::setMessages(int maxRetries) +bool GNSS_ZED::setMessages(int maxRetries) { bool success = false; @@ -2051,7 +2051,7 @@ bool ZED::setMessages(int maxRetries) // Enable all the valid messages for this platform over the USB port // Add 2 to every UART1 key. This is brittle and non-perfect, but works. //---------------------------------------- -bool ZED::setMessagesUsb(int maxRetries) +bool GNSS_ZED::setMessagesUsb(int maxRetries) { bool success = false; @@ -2095,7 +2095,7 @@ bool ZED::setMessagesUsb(int maxRetries) //---------------------------------------- // Set the minimum satellite signal level for navigation. //---------------------------------------- -bool ZED::setMinCnoRadio (uint8_t cnoValue) +bool GNSS_ZED::setMinCnoRadio (uint8_t cnoValue) { if (online.gnss) { @@ -2108,7 +2108,7 @@ bool ZED::setMinCnoRadio (uint8_t cnoValue) //---------------------------------------- // Set the dynamic model to use for RTK //---------------------------------------- -bool ZED::setModel(uint8_t modelNumber) +bool GNSS_ZED::setModel(uint8_t modelNumber) { if (online.gnss) { @@ -2119,7 +2119,7 @@ bool ZED::setModel(uint8_t modelNumber) } //---------------------------------------- -bool ZED::setRadioBaudRate(uint32_t baud) +bool GNSS_ZED::setRadioBaudRate(uint32_t baud) { if (online.gnss) return _zed->setVal32(UBLOX_CFG_UART2_BAUDRATE, baud); @@ -2132,7 +2132,7 @@ bool ZED::setRadioBaudRate(uint32_t baud) // navigationRate >= 1 && <= 127 // We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. //---------------------------------------- -bool ZED::setRate(double secondsBetweenSolutions) +bool GNSS_ZED::setRate(double secondsBetweenSolutions) { uint16_t measRate = 0; // Calculate these locally and then attempt to apply them to ZED at completion uint16_t navRate = 0; @@ -2203,7 +2203,7 @@ bool ZED::setRate(double secondsBetweenSolutions) } //---------------------------------------- -bool ZED::setTalkerGNGGA() +bool GNSS_ZED::setTalkerGNGGA() { if (online.gnss) { @@ -2218,7 +2218,7 @@ bool ZED::setTalkerGNGGA() //---------------------------------------- // Hotstart GNSS to try to get RTK lock //---------------------------------------- -bool ZED::softwareReset() +bool GNSS_ZED::softwareReset() { if (online.gnss == false) return false; @@ -2227,7 +2227,7 @@ bool ZED::softwareReset() } //---------------------------------------- -bool ZED::standby() +bool GNSS_ZED::standby() { return true; // TODO - this would be a perfect place for Save-On-Shutdown } @@ -2240,7 +2240,7 @@ bool ZED::standby() //---------------------------------------- void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; zed->storeHPdataRadio(ubxDataStruct); } @@ -2248,7 +2248,7 @@ void storeHPdata(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) //---------------------------------------- // Callback to save the high precision data //---------------------------------------- -void ZED::storeHPdataRadio(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) +void GNSS_ZED::storeHPdataRadio(UBX_NAV_HPPOSLLH_data_t *ubxDataStruct) { double latitude; double longitude; @@ -2279,7 +2279,7 @@ void storeMONHWdata(UBX_MON_HW_data_t *ubxDataStruct) //---------------------------------------- void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct) { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; zed->storePVTdataRadio(ubxDataStruct); } @@ -2287,7 +2287,7 @@ void storePVTdata(UBX_NAV_PVT_data_t *ubxDataStruct) //---------------------------------------- // Callback to save the PVT data //---------------------------------------- -void ZED::storePVTdataRadio(UBX_NAV_PVT_data_t *ubxDataStruct) +void GNSS_ZED::storePVTdataRadio(UBX_NAV_PVT_data_t *ubxDataStruct) { _altitude = ubxDataStruct->height / 1000.0; @@ -2369,7 +2369,7 @@ void storeTIMTPdata(UBX_TIM_TP_data_t *ubxDataStruct) // Slightly modified method for restarting survey-in from: // https://portal.u-blox.com/s/question/0D52p00009IsVoMCAV/restarting-surveyin-on-an-f9p //---------------------------------------- -bool ZED::surveyInReset() +bool GNSS_ZED::surveyInReset() { bool response = true; @@ -2411,7 +2411,7 @@ bool ZED::surveyInReset() // Start the survey-in operation // The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. //---------------------------------------- -bool ZED::surveyInStart() +bool GNSS_ZED::surveyInStart() { if (online.gnss == false) return (false); @@ -2472,7 +2472,7 @@ bool ZED::surveyInStart() } //---------------------------------------- -int ZED::ubxConstellationIDToIndex(int id) +int GNSS_ZED::ubxConstellationIDToIndex(int id) { for (int x = 0; x < MAX_UBX_CONSTELLATIONS; x++) { @@ -2484,7 +2484,7 @@ int ZED::ubxConstellationIDToIndex(int id) } //---------------------------------------- -void ZED::unlock(void) +void GNSS_ZED::unlock(void) { iAmLocked = false; } @@ -2492,7 +2492,7 @@ void ZED::unlock(void) //---------------------------------------- // Poll routine to update the GNSS state //---------------------------------------- -void ZED::update() +void GNSS_ZED::update() { if (online.gnss) { @@ -2502,7 +2502,7 @@ void ZED::update() } //---------------------------------------- -void ZED::updateCorrectionsSource(uint8_t source) +void GNSS_ZED::updateCorrectionsSource(uint8_t source) { if (!online.gnss) return; diff --git a/Firmware/RTK_Everywhere/MQTT_Client.ino b/Firmware/RTK_Everywhere/MQTT_Client.ino index 9ebe46753..da11c26ba 100644 --- a/Firmware/RTK_Everywhere/MQTT_Client.ino +++ b/Firmware/RTK_Everywhere/MQTT_Client.ino @@ -497,7 +497,7 @@ void mqttClientReceiveMessage(int messageSize) !inMainMenu) systemPrintf("Pushing %d bytes from %s topic to GNSS\r\n", mqttCount, topic); - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; zed->updateCorrectionsSource(0); // Set SOURCE to 0 (IP) if needed gnss->pushRawData(mqttData, mqttCount); diff --git a/Firmware/RTK_Everywhere/NVM.ino b/Firmware/RTK_Everywhere/NVM.ino index fd13adb2a..b64a51ed9 100644 --- a/Firmware/RTK_Everywhere/NVM.ino +++ b/Firmware/RTK_Everywhere/NVM.ino @@ -369,7 +369,7 @@ void recordSystemSettingsToFile(File *settingsFile) case tUbMsgRtb: { // Record message settings - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) @@ -1125,7 +1125,7 @@ bool parseLine(char *str) } break; case tUbMsgRtb: { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) diff --git a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino index e98642765..91089227b 100644 --- a/Firmware/RTK_Everywhere/PointPerfectLibrary.ino +++ b/Firmware/RTK_Everywhere/PointPerfectLibrary.ino @@ -42,7 +42,7 @@ void updatePplTask(void *e) // Note: this is almost certainly redundant. It would only be used if we // believe the PPL can do a better job generating corrections than the // ZED can internally using SPARTN direct. - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; zed->updateCorrectionsSource(1); gnss->pushRawData(pplRtcmBuffer, rtcmLength); diff --git a/Firmware/RTK_Everywhere/RTK_Everywhere.ino b/Firmware/RTK_Everywhere/RTK_Everywhere.ino index 96986f372..34949db99 100644 --- a/Firmware/RTK_Everywhere/RTK_Everywhere.ino +++ b/Firmware/RTK_Everywhere/RTK_Everywhere.ino @@ -338,7 +338,7 @@ int wifiOriginalMaxConnectionAttempts = wifiMaxConnectionAttempts; // Modified d // GNSS configuration - ZED-F9x //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= #include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 -#include "ZED.h" +#include "GNSS_ZED.h" GNSS * gnss; diff --git a/Firmware/RTK_Everywhere/menuCommands.ino b/Firmware/RTK_Everywhere/menuCommands.ino index 03ac9e461..c38700309 100644 --- a/Firmware/RTK_Everywhere/menuCommands.ino +++ b/Firmware/RTK_Everywhere/menuCommands.ino @@ -713,7 +713,7 @@ SettingValueResponse updateSettingWithValue(bool inCommands, const char *setting } break; case tUbMsgRtb: { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) @@ -1469,7 +1469,7 @@ void createSettingsString(char *newSettings) case tUbMsgRtb: { // Record message settings - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) @@ -2245,7 +2245,7 @@ SettingValueResponse getSettingValue(bool inCommands, const char *settingName, c } break; case tUbMsgRtb: { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < qualifier; x++) @@ -2742,7 +2742,7 @@ void commandList(bool inCommands, int i) break; case tUbMsgRtb: { // Record message settings - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < rtkSettingsEntries[i].qualifier; x++) diff --git a/Firmware/RTK_Everywhere/menuMessages.ino b/Firmware/RTK_Everywhere/menuMessages.ino index 47a366223..b70872795 100644 --- a/Firmware/RTK_Everywhere/menuMessages.ino +++ b/Firmware/RTK_Everywhere/menuMessages.ino @@ -203,7 +203,7 @@ void zedMenuMessagesSubtype(uint8_t *localMessageRate, const char *messageType) int endOfBlock = 0; int rtcmOffset = 0; // Used to offset messageSupported lookup - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; if (strcmp(messageType, "RTCM-Base") == 0) // The ubxMessageRatesBase array is 0 to MAX_UBX_MSG_RTCM - 1 { startOfBlock = 0; @@ -625,7 +625,7 @@ void checkGNSSArrayDefaults() defaultsApplied = true; // Reset Base rates to defaults - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; int firstRTCMRecord = zed->getMessageNumberByName("RTCM_1005"); for (int x = 0; x < MAX_UBX_MSG_RTCM; x++) settings.ubxMessageRatesBase[x] = ubxMessages[firstRTCMRecord + x].msgDefaultRate; @@ -780,7 +780,7 @@ void setLogTestFrequencyMessages(int rate, int messages) // Set messages setGNSSMessageRates(settings.ubxMessageRates, 0); // Turn off all messages - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; if (messages == 5) { zed->setMessageRateByName("NMEA_GGA", 1); diff --git a/Firmware/RTK_Everywhere/menuPP.ino b/Firmware/RTK_Everywhere/menuPP.ino index f44babd44..ea6babeb2 100644 --- a/Firmware/RTK_Everywhere/menuPP.ino +++ b/Firmware/RTK_Everywhere/menuPP.ino @@ -727,7 +727,7 @@ void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData) if (correctionLastSeen(CORR_LBAND)) { - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; zed->updateCorrectionsSource(1); // Set SOURCE to 1 (L-Band) if needed if (settings.debugCorrections == true && !inMainMenu) @@ -855,7 +855,7 @@ void beginLBand() response &= i2cLBand.sendCfgValset(); - ZED * zed = (ZED *)gnss; + GNSS_ZED * zed = (GNSS_ZED *)gnss; response &= zed->lBandCommunicationEnable(); if (response == false) From 8bf57f393e91d2889dfdc98a1fc6d9c4a1acde3c Mon Sep 17 00:00:00 2001 From: Lee Leahy Date: Tue, 1 Oct 2024 10:57:16 -1000 Subject: [PATCH 5/5] Implement the GNSS_UM980 class --- Firmware/RTK_Everywhere/Begin.ino | 2 +- .../RTK_Everywhere/{UM980.h => GNSS_UM980.h} | 12 +- .../{UM980.ino => GNSS_UM980.ino} | 212 +++++++++--------- Firmware/RTK_Everywhere/settings.h | 2 +- 4 files changed, 114 insertions(+), 114 deletions(-) rename Firmware/RTK_Everywhere/{UM980.h => GNSS_UM980.h} (98%) rename Firmware/RTK_Everywhere/{UM980.ino => GNSS_UM980.ino} (93%) diff --git a/Firmware/RTK_Everywhere/Begin.ino b/Firmware/RTK_Everywhere/Begin.ino index 2c609a9cf..f8c1148a5 100644 --- a/Firmware/RTK_Everywhere/Begin.ino +++ b/Firmware/RTK_Everywhere/Begin.ino @@ -190,7 +190,7 @@ void beginBoard() else if (productVariant == RTK_TORCH) { // Specify the GNSS radio - gnss = (GNSS *) new RTK_UM980(); + gnss = (GNSS *) new GNSS_UM980(); present.psram_2mb = true; present.gnss_um980 = true; diff --git a/Firmware/RTK_Everywhere/UM980.h b/Firmware/RTK_Everywhere/GNSS_UM980.h similarity index 98% rename from Firmware/RTK_Everywhere/UM980.h rename to Firmware/RTK_Everywhere/GNSS_UM980.h index c3a1c00ae..ed8468dac 100644 --- a/Firmware/RTK_Everywhere/UM980.h +++ b/Firmware/RTK_Everywhere/GNSS_UM980.h @@ -1,11 +1,11 @@ /*------------------------------------------------------------------------------ -UM980.h +GNSS_UM980.h - Declarations and definitions for the UM980 GNSS receiver and the RTK_UM980 class + Declarations and definitions for the UM980 GNSS receiver and the GNSS_UM980 class ------------------------------------------------------------------------------*/ -#ifndef _RTK_EVERYWHERE_UM980_H -#define _RTK_EVERYWHERE_UM980_H +#ifndef __GNSS_UM980_H__ +#define __GNSS_UM980_H__ #ifdef COMPILE_UM980 @@ -101,7 +101,7 @@ enum um980_Models UM980_DYN_MODEL_AUTOMOTIVE, }; -class RTK_UM980 : GNSS +class GNSS_UM980 : GNSS { private: @@ -467,4 +467,4 @@ class RTK_UM980 : GNSS }; #endif // COMPILE_UM980 -#endif // _RTK_EVERYWHERE_UM980_H +#endif // __GNSS_UM980_H__ diff --git a/Firmware/RTK_Everywhere/UM980.ino b/Firmware/RTK_Everywhere/GNSS_UM980.ino similarity index 93% rename from Firmware/RTK_Everywhere/UM980.ino rename to Firmware/RTK_Everywhere/GNSS_UM980.ino index eea2f52bd..347418ddd 100644 --- a/Firmware/RTK_Everywhere/UM980.ino +++ b/Firmware/RTK_Everywhere/GNSS_UM980.ino @@ -1,7 +1,7 @@ /*------------------------------------------------------------------------------ -UM980.ino +GNSS_UM980.ino - Implementation of the RTK_UM980 class + Implementation of the GNSS_UM980 class IM19 reads in binary+NMEA from the UM980 and passes out binary with tilt-corrected lat/long/alt to the ESP32. @@ -19,7 +19,7 @@ UM980.ino // If we have decryption keys, configure module // Note: don't check online.lband_neo here. We could be using ip corrections //---------------------------------------- -void RTK_UM980::applyPointPerfectKeys() +void GNSS_UM980::applyPointPerfectKeys() { // Taken care of in beginPPL() } @@ -27,7 +27,7 @@ void RTK_UM980::applyPointPerfectKeys() //---------------------------------------- // Set RTCM for base mode to defaults (1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz) //---------------------------------------- -void RTK_UM980::baseRtcmDefault() +void GNSS_UM980::baseRtcmDefault() { // Reset RTCM rates to defaults for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) @@ -37,7 +37,7 @@ void RTK_UM980::baseRtcmDefault() //---------------------------------------- // Reset to Low Bandwidth Link (1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz) //---------------------------------------- -void RTK_UM980::baseRtcmLowDataRate() +void GNSS_UM980::baseRtcmLowDataRate() { // Zero out everything for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) @@ -55,7 +55,7 @@ void RTK_UM980::baseRtcmLowDataRate() //---------------------------------------- // Connect to GNSS and identify particulars //---------------------------------------- -void RTK_UM980::begin() +void GNSS_UM980::begin() { // During identifyBoard(), the GNSS UART and DR pins are set @@ -122,7 +122,7 @@ void RTK_UM980::begin() // Setup the timepulse output on the PPS pin for external triggering // Setup TM2 time stamp input as need //---------------------------------------- -bool RTK_UM980::beginExternalEvent() +bool GNSS_UM980::beginExternalEvent() { // UM980 Event signal not exposed return (false); @@ -131,20 +131,20 @@ bool RTK_UM980::beginExternalEvent() //---------------------------------------- // Setup the timepulse output on the PPS pin for external triggering //---------------------------------------- -bool RTK_UM980::beginPPS() +bool GNSS_UM980::beginPPS() { // UM980 PPS signal not exposed return (false); } //---------------------------------------- -bool RTK_UM980::checkNMEARates() +bool GNSS_UM980::checkNMEARates() { return false; } //---------------------------------------- -bool RTK_UM980::checkPPPRates() +bool GNSS_UM980::checkPPPRates() { return false; } @@ -152,7 +152,7 @@ bool RTK_UM980::checkPPPRates() //---------------------------------------- // Configure specific aspects of the receiver for base mode //---------------------------------------- -bool RTK_UM980::configureBase() +bool GNSS_UM980::configureBase() { /* Disable all messages @@ -204,7 +204,7 @@ bool RTK_UM980::configureBase() } //---------------------------------------- -bool RTK_UM980::configureOnce() +bool GNSS_UM980::configureOnce() { /* Disable all message traffic @@ -284,7 +284,7 @@ bool RTK_UM980::configureOnce() //---------------------------------------- // Configure specific aspects of the receiver for NTP mode //---------------------------------------- -bool RTK_UM980::configureNtpMode() +bool GNSS_UM980::configureNtpMode() { return false; } @@ -294,7 +294,7 @@ bool RTK_UM980::configureNtpMode() // In general we check if the setting is incorrect before writing it. Otherwise, the set commands have, on rare // occasion, become corrupt. The worst is when the I2C port gets turned off or the I2C address gets borked. //---------------------------------------- -bool RTK_UM980::configureRadio() +bool GNSS_UM980::configureRadio() { // Skip configuring the UM980 if no new changes are necessary if (settings.updateGNSSSettings == false) @@ -324,7 +324,7 @@ bool RTK_UM980::configureRadio() //---------------------------------------- // Configure specific aspects of the receiver for rover mode //---------------------------------------- -bool RTK_UM980::configureRover() +bool GNSS_UM980::configureRover() { /* Disable all message traffic @@ -384,14 +384,14 @@ bool RTK_UM980::configureRover() } //---------------------------------------- -void RTK_UM980::debuggingDisable() +void GNSS_UM980::debuggingDisable() { if (online.gnss) _um980->disableDebugging(); } //---------------------------------------- -void RTK_UM980::debuggingEnable() +void GNSS_UM980::debuggingEnable() { if (online.gnss) { @@ -402,7 +402,7 @@ void RTK_UM980::debuggingEnable() //---------------------------------------- // Turn off all NMEA and RTCM -void RTK_UM980::disableAllOutput() +void GNSS_UM980::disableAllOutput() { if (settings.debugGnss) systemPrintln("UM980 disable output"); @@ -425,14 +425,14 @@ void RTK_UM980::disableAllOutput() //---------------------------------------- // Disable all output, then re-enable //---------------------------------------- -void RTK_UM980::disableRTCM() +void GNSS_UM980::disableRTCM() { disableAllOutput(); enableNMEA(); } //---------------------------------------- -void RTK_UM980::enableGgaForNtrip() +void GNSS_UM980::enableGgaForNtrip() { // TODO um980EnableGgaForNtrip(); } @@ -440,7 +440,7 @@ void RTK_UM980::enableGgaForNtrip() //---------------------------------------- // Turn on all the enabled NMEA messages on COM3 //---------------------------------------- -bool RTK_UM980::enableNMEA() +bool GNSS_UM980::enableNMEA() { bool response = true; bool gpggaEnabled = false; @@ -489,7 +489,7 @@ bool RTK_UM980::enableNMEA() //---------------------------------------- // Turn on all the enabled RTCM Base messages on COM3 //---------------------------------------- -bool RTK_UM980::enableRTCMBase() +bool GNSS_UM980::enableRTCMBase() { bool response = true; @@ -516,7 +516,7 @@ bool RTK_UM980::enableRTCMBase() //---------------------------------------- // Turn on all the enabled RTCM Rover messages on COM3 //---------------------------------------- -bool RTK_UM980::enableRTCMRover() +bool GNSS_UM980::enableRTCMRover() { bool response = true; bool rtcm1019Enabled = false; @@ -576,7 +576,7 @@ bool RTK_UM980::enableRTCMRover() // Enable RTCM 1230. This is the GLONASS bias sentence and is transmitted // even if there is no GPS fix. We use it to test serial output. //---------------------------------------- -bool RTK_UM980::enableRTCMTest() +bool GNSS_UM980::enableRTCMTest() { // There is no data port on devices with the UM980 return false; @@ -585,7 +585,7 @@ bool RTK_UM980::enableRTCMTest() //---------------------------------------- // Restore the GNSS to the factory settings //---------------------------------------- -void RTK_UM980::factoryReset() +void GNSS_UM980::factoryReset() { if (online.gnss) { @@ -603,14 +603,14 @@ void RTK_UM980::factoryReset() } //---------------------------------------- -uint16_t RTK_UM980::fileBufferAvailable() +uint16_t GNSS_UM980::fileBufferAvailable() { // TODO return(um980FileBufferAvailable()); return (0); } //---------------------------------------- -uint16_t RTK_UM980::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) +uint16_t GNSS_UM980::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRead) { // TODO return(um980FileBufferAvailable()); return (0); @@ -619,7 +619,7 @@ uint16_t RTK_UM980::fileBufferExtractData(uint8_t *fileBuffer, int fileBytesToRe //---------------------------------------- // Start the base using fixed coordinates //---------------------------------------- -bool RTK_UM980::fixedBaseStart() +bool GNSS_UM980::fixedBaseStart() { bool response = true; @@ -647,7 +647,7 @@ bool RTK_UM980::fixedBaseStart() //---------------------------------------- // Return the number of active/enabled messages //---------------------------------------- -uint8_t RTK_UM980::getActiveMessageCount() +uint8_t GNSS_UM980::getActiveMessageCount() { uint8_t count = 0; @@ -657,7 +657,7 @@ uint8_t RTK_UM980::getActiveMessageCount() } //---------------------------------------- -uint8_t RTK_UM980::getActiveNmeaMessageCount() +uint8_t GNSS_UM980::getActiveNmeaMessageCount() { uint8_t count = 0; @@ -669,7 +669,7 @@ uint8_t RTK_UM980::getActiveNmeaMessageCount() } //---------------------------------------- -uint8_t RTK_UM980::getActiveRtcmMessageCount() +uint8_t GNSS_UM980::getActiveRtcmMessageCount() { uint8_t count = 0; @@ -693,7 +693,7 @@ uint8_t RTK_UM980::getActiveRtcmMessageCount() //---------------------------------------- // Returns the altitude in meters or zero if the GNSS is offline //---------------------------------------- -double RTK_UM980::getAltitude() +double GNSS_UM980::getAltitude() { if (online.gnss) return (_um980->getAltitude()); @@ -703,7 +703,7 @@ double RTK_UM980::getAltitude() //---------------------------------------- // Returns the carrier solution or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getCarrierSolution() +uint8_t GNSS_UM980::getCarrierSolution() { if (online.gnss) // 0 = Solution computed @@ -715,7 +715,7 @@ uint8_t RTK_UM980::getCarrierSolution() } //---------------------------------------- -uint32_t RTK_UM980::getDataBaudRate() +uint32_t GNSS_UM980::getDataBaudRate() { return (0); // UM980 has no multiplexer } @@ -723,7 +723,7 @@ uint32_t RTK_UM980::getDataBaudRate() //---------------------------------------- // Returns the day number or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getDay() +uint8_t GNSS_UM980::getDay() { if (online.gnss) return (_um980->getDay()); @@ -733,7 +733,7 @@ uint8_t RTK_UM980::getDay() //---------------------------------------- // Return the number of milliseconds since GNSS data was last updated //---------------------------------------- -uint16_t RTK_UM980::getFixAgeMilliseconds() +uint16_t GNSS_UM980::getFixAgeMilliseconds() { if (online.gnss) return (_um980->getFixAgeMilliseconds()); @@ -743,7 +743,7 @@ uint16_t RTK_UM980::getFixAgeMilliseconds() //---------------------------------------- // Returns the fix type or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getFixType() +uint8_t GNSS_UM980::getFixType() { if (online.gnss) // 0 = None @@ -767,7 +767,7 @@ uint8_t RTK_UM980::getFixType() // Get the horizontal position accuracy // Returns the horizontal position accuracy or zero if offline //---------------------------------------- -float RTK_UM980::getHorizontalAccuracy() +float GNSS_UM980::getHorizontalAccuracy() { if (online.gnss) { @@ -791,7 +791,7 @@ float RTK_UM980::getHorizontalAccuracy() //---------------------------------------- // Returns the hours of 24 hour clock or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getHour() +uint8_t GNSS_UM980::getHour() { if (online.gnss) return (_um980->getHour()); @@ -799,7 +799,7 @@ uint8_t RTK_UM980::getHour() } //---------------------------------------- -const char * RTK_UM980::getId() +const char * GNSS_UM980::getId() { if (online.gnss) return (_um980->getID()); @@ -810,7 +810,7 @@ const char * RTK_UM980::getId() // Get the latitude value // Returns the latitude value or zero if not online //---------------------------------------- -double RTK_UM980::getLatitude() +double GNSS_UM980::getLatitude() { if (online.gnss) return (_um980->getLatitude()); @@ -820,7 +820,7 @@ double RTK_UM980::getLatitude() //---------------------------------------- // Query GNSS for current leap seconds //---------------------------------------- -uint8_t RTK_UM980::getLeapSeconds() +uint8_t GNSS_UM980::getLeapSeconds() { // TODO Need to find leap seconds in UM980 return (18); // Default to 18 @@ -831,7 +831,7 @@ uint8_t RTK_UM980::getLeapSeconds() // Outputs: // Returns the longitude value or zero if not online //---------------------------------------- -double RTK_UM980::getLongitude() +double GNSS_UM980::getLongitude() { if (online.gnss) return (_um980->getLongitude()); @@ -841,7 +841,7 @@ double RTK_UM980::getLongitude() //---------------------------------------- // Returns two digits of milliseconds or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getMillisecond() +uint8_t GNSS_UM980::getMillisecond() { if (online.gnss) return (_um980->getMillisecond()); @@ -851,7 +851,7 @@ uint8_t RTK_UM980::getMillisecond() //---------------------------------------- // Returns minutes or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getMinute() +uint8_t GNSS_UM980::getMinute() { if (online.gnss) return (_um980->getMinute()); @@ -861,7 +861,7 @@ uint8_t RTK_UM980::getMinute() //---------------------------------------- // Returns month number or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getMonth() +uint8_t GNSS_UM980::getMonth() { if (online.gnss) return (_um980->getMonth()); @@ -871,7 +871,7 @@ uint8_t RTK_UM980::getMonth() //---------------------------------------- // Returns nanoseconds or zero if not online //---------------------------------------- -uint32_t RTK_UM980::getNanosecond() +uint32_t GNSS_UM980::getNanosecond() { if (online.gnss) // UM980 does not have nanosecond, but it does have millisecond @@ -882,7 +882,7 @@ uint32_t RTK_UM980::getNanosecond() //---------------------------------------- // Given the name of an NMEA message, return the array number //---------------------------------------- -uint8_t RTK_UM980::getNmeaMessageNumberByName(const char *msgName) +uint8_t GNSS_UM980::getNmeaMessageNumberByName(const char *msgName) { for (int x = 0; x < MAX_UM980_NMEA_MSG; x++) { @@ -895,7 +895,7 @@ uint8_t RTK_UM980::getNmeaMessageNumberByName(const char *msgName) } //---------------------------------------- -uint32_t RTK_UM980::getRadioBaudRate() +uint32_t GNSS_UM980::getRadioBaudRate() { return (0); // UM980 has no multiplexer } @@ -903,19 +903,19 @@ uint32_t RTK_UM980::getRadioBaudRate() //---------------------------------------- // Returns the seconds between measurements //---------------------------------------- -double RTK_UM980::getRateS() +double GNSS_UM980::getRateS() { return (((double)settings.measurementRateMs) / 1000.0); } //---------------------------------------- -const char * RTK_UM980::getRtcmDefaultString() +const char * GNSS_UM980::getRtcmDefaultString() { return "1005/1074/1084/1094/1124 1Hz & 1033 0.1Hz"; } //---------------------------------------- -const char * RTK_UM980::getRtcmLowDataRateString() +const char * GNSS_UM980::getRtcmLowDataRateString() { return "1074/1084/1094/1124 0.5Hz & 1005/1033 0.1Hz"; } @@ -923,7 +923,7 @@ const char * RTK_UM980::getRtcmLowDataRateString() //---------------------------------------- // Given the name of an RTCM message, return the array number //---------------------------------------- -uint8_t RTK_UM980::getRtcmMessageNumberByName(const char *msgName) +uint8_t GNSS_UM980::getRtcmMessageNumberByName(const char *msgName) { for (int x = 0; x < MAX_UM980_RTCM_MSG; x++) { @@ -938,7 +938,7 @@ uint8_t RTK_UM980::getRtcmMessageNumberByName(const char *msgName) //---------------------------------------- // Returns the number of satellites in view or zero if offline //---------------------------------------- -uint8_t RTK_UM980::getSatellitesInView() +uint8_t GNSS_UM980::getSatellitesInView() { if (online.gnss) return (_um980->getSIV()); @@ -948,7 +948,7 @@ uint8_t RTK_UM980::getSatellitesInView() //---------------------------------------- // Returns seconds or zero if not online //---------------------------------------- -uint8_t RTK_UM980::getSecond() +uint8_t GNSS_UM980::getSecond() { if (online.gnss) return (_um980->getSecond()); @@ -958,7 +958,7 @@ uint8_t RTK_UM980::getSecond() //---------------------------------------- // Get the survey-in mean accuracy //---------------------------------------- -float RTK_UM980::getSurveyInMeanAccuracy() +float GNSS_UM980::getSurveyInMeanAccuracy() { // Not supported on the UM980 // Return the current HPA instead @@ -968,7 +968,7 @@ float RTK_UM980::getSurveyInMeanAccuracy() //---------------------------------------- // Return the number of seconds the survey-in process has been running //---------------------------------------- -int RTK_UM980::getSurveyInObservationTime() +int GNSS_UM980::getSurveyInObservationTime() { int elapsedSeconds = (millis() - _autoBaseStartTimer) / 1000; return (elapsedSeconds); @@ -977,7 +977,7 @@ int RTK_UM980::getSurveyInObservationTime() //---------------------------------------- // Returns timing accuracy or zero if not online //---------------------------------------- -uint32_t RTK_UM980::getTimeAccuracy() +uint32_t GNSS_UM980::getTimeAccuracy() { if (online.gnss) { @@ -997,7 +997,7 @@ uint32_t RTK_UM980::getTimeAccuracy() //---------------------------------------- // Returns full year, ie 2023, not 23. //---------------------------------------- -uint16_t RTK_UM980::getYear() +uint16_t GNSS_UM980::getYear() { if (online.gnss) return (_um980->getYear()); @@ -1008,7 +1008,7 @@ uint16_t RTK_UM980::getYear() // Returns true if the device is in Rover mode // Currently the only two modes are Rover or Base //---------------------------------------- -bool RTK_UM980::inRoverMode() +bool GNSS_UM980::inRoverMode() { // Determine which state we are in if (settings.lastState == STATE_BASE_NOT_STARTED) @@ -1018,7 +1018,7 @@ bool RTK_UM980::inRoverMode() } //---------------------------------------- -bool RTK_UM980::isBlocking() +bool GNSS_UM980::isBlocking() { if (online.gnss) return _um980->isBlocking(); @@ -1028,7 +1028,7 @@ bool RTK_UM980::isBlocking() //---------------------------------------- // Date is confirmed once we have GNSS fix //---------------------------------------- -bool RTK_UM980::isConfirmedDate() +bool GNSS_UM980::isConfirmedDate() { // UM980 doesn't have this feature. Check for valid date. return isValidDate(); @@ -1037,7 +1037,7 @@ bool RTK_UM980::isConfirmedDate() //---------------------------------------- // Time is confirmed once we have GNSS fix //---------------------------------------- -bool RTK_UM980::isConfirmedTime() +bool GNSS_UM980::isConfirmedTime() { // UM980 doesn't have this feature. Check for valid time. return isValidTime(); @@ -1046,7 +1046,7 @@ bool RTK_UM980::isConfirmedTime() //---------------------------------------- // Return true if GNSS receiver has a higher quality DGPS fix than 3D //---------------------------------------- -bool RTK_UM980::isDgpsFixed() +bool GNSS_UM980::isDgpsFixed() { if (online.gnss) // 17 = Pseudorange differential solution @@ -1058,7 +1058,7 @@ bool RTK_UM980::isDgpsFixed() // Some functions (L-Band area frequency determination) merely need to know if we have a valid fix, not what type of fix // This function checks to see if the given platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool RTK_UM980::isFixed() +bool GNSS_UM980::isFixed() { if (online.gnss) // 16 = 3D Fix (Single) @@ -1069,7 +1069,7 @@ bool RTK_UM980::isFixed() //---------------------------------------- // Used in tpISR() for time pulse synchronization //---------------------------------------- -bool RTK_UM980::isFullyResolved() +bool GNSS_UM980::isFullyResolved() { if (online.gnss) // UM980 does not have this feature directly. @@ -1085,7 +1085,7 @@ bool RTK_UM980::isFullyResolved() //---------------------------------------- // Return true if the GPGGA message is active //---------------------------------------- -bool RTK_UM980::isGgaActive() +bool GNSS_UM980::isGgaActive() { if (settings.um980MessageRatesNMEA[getNmeaMessageNumberByName("GPGGA")] > 0) return (true); @@ -1093,7 +1093,7 @@ bool RTK_UM980::isGgaActive() } //---------------------------------------- -bool RTK_UM980::isPppConverged() +bool GNSS_UM980::isPppConverged() { if (online.gnss) // 69 = Precision Point Positioning @@ -1102,7 +1102,7 @@ bool RTK_UM980::isPppConverged() } //---------------------------------------- -bool RTK_UM980::isPppConverging() +bool GNSS_UM980::isPppConverging() { if (online.gnss) // 68 = PPP solution converging @@ -1115,7 +1115,7 @@ bool RTK_UM980::isPppConverging() // know if we have an RTK Fix. This function checks to see if the given // platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool RTK_UM980::isRTKFix() +bool GNSS_UM980::isRTKFix() { if (online.gnss) // 50 = RTK Fixed (Narrow-lane fixed solution) @@ -1128,7 +1128,7 @@ bool RTK_UM980::isRTKFix() // know if we have an RTK Float. This function checks to see if the // given platform has reached sufficient fix type to be considered valid //---------------------------------------- -bool RTK_UM980::isRTKFloat() +bool GNSS_UM980::isRTKFloat() { if (online.gnss) // 34 = Narrow-land float solution @@ -1140,7 +1140,7 @@ bool RTK_UM980::isRTKFloat() //---------------------------------------- // Determine if the survey-in operation is complete //---------------------------------------- -bool RTK_UM980::isSurveyInComplete() +bool GNSS_UM980::isSurveyInComplete() { return (false); } @@ -1148,7 +1148,7 @@ bool RTK_UM980::isSurveyInComplete() //---------------------------------------- // Date will be valid if the RTC is reporting (regardless of GNSS fix) //---------------------------------------- -bool RTK_UM980::isValidDate() +bool GNSS_UM980::isValidDate() { if (online.gnss) // 0 = Invalid @@ -1161,7 +1161,7 @@ bool RTK_UM980::isValidDate() //---------------------------------------- // Time will be valid if the RTC is reporting (regardless of GNSS fix) //---------------------------------------- -bool RTK_UM980::isValidTime() +bool GNSS_UM980::isValidTime() { if (online.gnss) // 0 = valid @@ -1173,7 +1173,7 @@ bool RTK_UM980::isValidTime() //---------------------------------------- // Controls the constellations that are used to generate a fix and logged //---------------------------------------- -void RTK_UM980::menuConstellations() +void GNSS_UM980::menuConstellations() { while (1) { @@ -1225,7 +1225,7 @@ void RTK_UM980::menuConstellations() } //---------------------------------------- -void RTK_UM980::menuMessageBaseRtcm() +void GNSS_UM980::menuMessageBaseRtcm() { menuMessagesSubtype(settings.um980MessageRatesRTCMBase, "RTCMBase"); } @@ -1233,7 +1233,7 @@ void RTK_UM980::menuMessageBaseRtcm() //---------------------------------------- // Control the messages that get broadcast over Bluetooth and logged (if enabled) //---------------------------------------- -void RTK_UM980::menuMessages() +void GNSS_UM980::menuMessages() { while (1) { @@ -1296,7 +1296,7 @@ void RTK_UM980::menuMessages() // Given a sub type (ie "RTCM", "NMEA") present menu showing messages with this subtype // Controls the messages that get broadcast over Bluetooth and logged (if enabled) //---------------------------------------- -void RTK_UM980::menuMessagesSubtype(float *localMessageRate, const char *messageType) +void GNSS_UM980::menuMessagesSubtype(float *localMessageRate, const char *messageType) { while (1) { @@ -1406,7 +1406,7 @@ void RTK_UM980::menuMessagesSubtype(float *localMessageRate, const char *message //---------------------------------------- // Print the module type and firmware version //---------------------------------------- -void RTK_UM980::printModuleInfo() +void GNSS_UM980::printModuleInfo() { if (online.gnss) { @@ -1425,7 +1425,7 @@ void RTK_UM980::printModuleInfo() // Send data directly from ESP GNSS UART1 to UM980 UART3 // Returns the number of correction data bytes written //---------------------------------------- -int RTK_UM980::pushRawData(uint8_t *dataToSend, int dataLength) +int GNSS_UM980::pushRawData(uint8_t *dataToSend, int dataLength) { if (online.gnss) return (serialGNSS->write(dataToSend, dataLength)); @@ -1433,7 +1433,7 @@ int RTK_UM980::pushRawData(uint8_t *dataToSend, int dataLength) } //---------------------------------------- -uint16_t RTK_UM980::rtcmBufferAvailable() +uint16_t GNSS_UM980::rtcmBufferAvailable() { // TODO return(um980RtcmBufferAvailable()); return (0); @@ -1442,7 +1442,7 @@ uint16_t RTK_UM980::rtcmBufferAvailable() //---------------------------------------- // If LBand is being used, ignore any RTCM that may come in from the GNSS //---------------------------------------- -void RTK_UM980::rtcmOnGnssDisable() +void GNSS_UM980::rtcmOnGnssDisable() { // UM980 does not have a separate interface for RTCM } @@ -1450,13 +1450,13 @@ void RTK_UM980::rtcmOnGnssDisable() //---------------------------------------- // If L-Band is available, but encrypted, allow RTCM through other sources (radio, ESP-Now) to GNSS receiver //---------------------------------------- -void RTK_UM980::rtcmOnGnssEnable() +void GNSS_UM980::rtcmOnGnssEnable() { // UM980 does not have a separate interface for RTCM } //---------------------------------------- -uint16_t RTK_UM980::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) +uint16_t GNSS_UM980::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) { // TODO return(um980RtcmRead(rtcmBuffer, rtcmBytesToRead)); return (0); @@ -1466,7 +1466,7 @@ uint16_t RTK_UM980::rtcmRead(uint8_t *rtcmBuffer, int rtcmBytesToRead) // Save the current configuration // Returns true when the configuration was saved and false upon failure //---------------------------------------- -bool RTK_UM980::saveConfiguration() +bool GNSS_UM980::saveConfiguration() { if (online.gnss) return (_um980->saveConfiguration()); @@ -1478,7 +1478,7 @@ bool RTK_UM980::saveConfiguration() // This just sets the GNSS side // Used during Bluetooth testing //---------------------------------------- -bool RTK_UM980::setBaudrate(uint32_t baudRate) +bool GNSS_UM980::setBaudrate(uint32_t baudRate) { if (online.gnss) // Set the baud rate on COM3 of the UM980 @@ -1489,7 +1489,7 @@ bool RTK_UM980::setBaudrate(uint32_t baudRate) //---------------------------------------- // Set the baud rate on the GNSS port that interfaces between the ESP32 and the GNSS //---------------------------------------- -bool RTK_UM980::setBaudRateCOM3(uint32_t baudRate) +bool GNSS_UM980::setBaudRateCOM3(uint32_t baudRate) { if (online.gnss) return _um980->setPortBaudrate("COM3", baudRate); @@ -1501,7 +1501,7 @@ bool RTK_UM980::setBaudRateCOM3(uint32_t baudRate) // Band support varies between platforms and firmware versions // We open/close a complete set 19 messages //---------------------------------------- -bool RTK_UM980::setConstellations() +bool GNSS_UM980::setConstellations() { bool response = true; @@ -1533,7 +1533,7 @@ bool RTK_UM980::setConstellations() } //---------------------------------------- -bool RTK_UM980::setDataBaudRate(uint32_t baud) +bool GNSS_UM980::setDataBaudRate(uint32_t baud) { return false; // UM980 has no multiplexer } @@ -1541,7 +1541,7 @@ bool RTK_UM980::setDataBaudRate(uint32_t baud) //---------------------------------------- // Set the elevation in degrees //---------------------------------------- -bool RTK_UM980::setElevation(uint8_t elevationDegrees) +bool GNSS_UM980::setElevation(uint8_t elevationDegrees) { if (online.gnss) return _um980->setElevationAngle(elevationDegrees); @@ -1549,7 +1549,7 @@ bool RTK_UM980::setElevation(uint8_t elevationDegrees) } //---------------------------------------- -bool RTK_UM980::setHighAccuracyService(bool enableGalileoHas) +bool GNSS_UM980::setHighAccuracyService(bool enableGalileoHas) { bool result = true; @@ -1610,7 +1610,7 @@ bool RTK_UM980::setHighAccuracyService(bool enableGalileoHas) // There are many messages so split into batches. VALSET is limited to 64 max per batch // Uses dummy newCfg and sendCfg values to be sure we open/close a complete set //---------------------------------------- -bool RTK_UM980::setMessages(int maxRetries) +bool GNSS_UM980::setMessages(int maxRetries) { // We probably don't need this for the UM980 // TODO return(um980SetMessages(maxRetries)); @@ -1621,7 +1621,7 @@ bool RTK_UM980::setMessages(int maxRetries) // Enable all the valid messages for this platform over the USB port // Add 2 to every UART1 key. This is brittle and non-perfect, but works. //---------------------------------------- -bool RTK_UM980::setMessagesUsb(int maxRetries) +bool GNSS_UM980::setMessagesUsb(int maxRetries) { // We probably don't need this for the UM980 // TODO return(um980SetMessagesUsb(maxRetries)); @@ -1631,7 +1631,7 @@ bool RTK_UM980::setMessagesUsb(int maxRetries) //---------------------------------------- // Set the minimum satellite signal level for navigation. //---------------------------------------- -bool RTK_UM980::setMinCnoRadio (uint8_t cnoValue) +bool GNSS_UM980::setMinCnoRadio (uint8_t cnoValue) { if (online.gnss) { @@ -1644,7 +1644,7 @@ bool RTK_UM980::setMinCnoRadio (uint8_t cnoValue) //---------------------------------------- // Set the dynamic model to use for RTK //---------------------------------------- -bool RTK_UM980::setModel(uint8_t modelNumber) +bool GNSS_UM980::setModel(uint8_t modelNumber) { if (online.gnss) { @@ -1659,7 +1659,7 @@ bool RTK_UM980::setModel(uint8_t modelNumber) } //---------------------------------------- -bool RTK_UM980::setMultipathMitigation(bool enableMultipathMitigation) +bool GNSS_UM980::setMultipathMitigation(bool enableMultipathMitigation) { bool result = true; @@ -1695,7 +1695,7 @@ bool RTK_UM980::setMultipathMitigation(bool enableMultipathMitigation) } //---------------------------------------- -bool RTK_UM980::setRadioBaudRate(uint32_t baud) +bool GNSS_UM980::setRadioBaudRate(uint32_t baud) { return false; // UM980 has no multiplexer } @@ -1706,7 +1706,7 @@ bool RTK_UM980::setRadioBaudRate(uint32_t baud) // navigationRate >= 1 && <= 127 // We give preference to limiting a measurementRate to 30 or below due to reported problems with measRates above 30. //---------------------------------------- -bool RTK_UM980::setRate(double secondsBetweenSolutions) +bool GNSS_UM980::setRate(double secondsBetweenSolutions) { // The UM980 does not have a rate setting. Instead the report rate of // the GNSS messages can be set. For example, 0.5 is 2Hz, 0.2 is 5Hz. @@ -1758,7 +1758,7 @@ bool RTK_UM980::setRate(double secondsBetweenSolutions) } //---------------------------------------- -bool RTK_UM980::setTalkerGNGGA() +bool GNSS_UM980::setTalkerGNGGA() { // TODO um980SetTalkerGNGGA(); return false; @@ -1767,13 +1767,13 @@ bool RTK_UM980::setTalkerGNGGA() //---------------------------------------- // Hotstart GNSS to try to get RTK lock //---------------------------------------- -bool RTK_UM980::softwareReset() +bool GNSS_UM980::softwareReset() { return false; } //---------------------------------------- -bool RTK_UM980::standby() +bool GNSS_UM980::standby() { return true; } @@ -1782,7 +1782,7 @@ bool RTK_UM980::standby() // Slightly modified method for restarting survey-in from: // https://portal.u-blox.com/s/question/0D52p00009IsVoMCAV/restarting-surveyin-on-an-f9p //---------------------------------------- -bool RTK_UM980::surveyInReset() +bool GNSS_UM980::surveyInReset() { if (online.gnss) return (_um980->setModeRoverSurvey()); @@ -1793,7 +1793,7 @@ bool RTK_UM980::surveyInReset() // Start the survey-in operation // The ZED-F9P is slightly different than the NEO-M8P. See the Integration manual 3.5.8 for more info. //---------------------------------------- -bool RTK_UM980::surveyInStart() +bool GNSS_UM980::surveyInStart() { if (online.gnss) { @@ -1825,7 +1825,7 @@ bool RTK_UM980::surveyInStart() //---------------------------------------- void um980UnicoreHandler(uint8_t *buffer, int length) { - RTK_UM980 * um980 = (RTK_UM980 *)gnss; + GNSS_UM980 * um980 = (GNSS_UM980 *)gnss; um980->unicoreHandler(buffer, length); } @@ -1833,7 +1833,7 @@ void um980UnicoreHandler(uint8_t *buffer, int length) // If we have received serial data from the UM980 outside of the Unicore library (ie, from processUart1Message task) // we can pass data back into the Unicore library to allow it to update its own variables //---------------------------------------- -void RTK_UM980::unicoreHandler(uint8_t *buffer, int length) +void GNSS_UM980::unicoreHandler(uint8_t *buffer, int length) { _um980->unicoreHandler(buffer, length); } @@ -1841,7 +1841,7 @@ void RTK_UM980::unicoreHandler(uint8_t *buffer, int length) //---------------------------------------- // Poll routine to update the GNSS state //---------------------------------------- -void RTK_UM980::update() +void GNSS_UM980::update() { // We don't check serial data here; the gnssReadTask takes care of serial consumption } diff --git a/Firmware/RTK_Everywhere/settings.h b/Firmware/RTK_Everywhere/settings.h index 25c9a97aa..bc0a2c6a4 100644 --- a/Firmware/RTK_Everywhere/settings.h +++ b/Firmware/RTK_Everywhere/settings.h @@ -2,7 +2,7 @@ #define __SETTINGS_H__ #include "GNSS.h" -#include "UM980.h" //Structs of UM980 messages, needed for settings.h +#include "GNSS_UM980.h" //Structs of UM980 messages, needed for settings.h #include "mosaic.h" //Structs of mosaic messages, needed for settings.h #include