From 63d8b60f4271da1184179228a1680e474037c28f Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 10:36:11 +0100 Subject: [PATCH 01/11] Add flushCFGRATE. Ensure CFG RATE data is marked as stale by the set rate functions --- ...Example25_MeasurementAndNavigationRate.ino | 3 - keywords.txt | 1 + src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 76 ++++++++++--------- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 1 + 4 files changed, 43 insertions(+), 38 deletions(-) diff --git a/examples/Example25_MeasurementAndNavigationRate/Example25_MeasurementAndNavigationRate.ino b/examples/Example25_MeasurementAndNavigationRate/Example25_MeasurementAndNavigationRate.ino index c16f6ed..a67fc4e 100644 --- a/examples/Example25_MeasurementAndNavigationRate/Example25_MeasurementAndNavigationRate.ino +++ b/examples/Example25_MeasurementAndNavigationRate/Example25_MeasurementAndNavigationRate.ino @@ -81,9 +81,6 @@ void setup() while (1); } - // Another trick we can use is to mark the CFG RATE data as stale so we can be sure we read fresh data - myGNSS.packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0; // Mark all of the CFG RATE data as stale - // Read and print the updated measurement rate and navigation rate rate = myGNSS.getMeasurementRate(); //Get the measurement rate of this module diff --git a/keywords.txt b/keywords.txt index f320104..f0aeb72 100644 --- a/keywords.txt +++ b/keywords.txt @@ -388,6 +388,7 @@ setMeasurementRate KEYWORD2 getMeasurementRate KEYWORD2 setNavigationRate KEYWORD2 getNavigationRate KEYWORD2 +flushCFGRATE KEYWORD2 getGeometricDOP KEYWORD2 getPositionDOP KEYWORD2 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 2f71233..943cdeb 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -7387,38 +7387,25 @@ boolean SFE_UBLOX_GNSS::getNavigationFrequencyInternal(uint16_t maxWait) if (packetUBXCFGRATE == NULL) //Bail if the RAM allocation failed return (false); - if (packetUBXCFGRATE->automaticFlags.flags.bits.automatic && packetUBXCFGRATE->automaticFlags.flags.bits.implicitUpdate) - { - //The GPS is automatically reporting, we just check whether we got unread data - checkUbloxInternal(&packetCfg, UBX_CLASS_CFG, UBX_CFG_RATE); - return packetUBXCFGRATE->moduleQueried.moduleQueried.bits.all; - } - else if (packetUBXCFGRATE->automaticFlags.flags.bits.automatic && !packetUBXCFGRATE->automaticFlags.flags.bits.implicitUpdate) - { - //Someone else has to call checkUblox for us... - return (false); - } - else - { - //The GPS is not automatically reporting navigation rate so we have to poll explicitly - packetCfg.cls = UBX_CLASS_CFG; - packetCfg.id = UBX_CFG_RATE; - packetCfg.len = 0; - packetCfg.startingSpot = 0; - - //The data is parsed as part of processing the response - sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + // The CFG RATE message will never be produced automatically - that would be pointless. + // There is no setAutoCFGRATE function. We always need to poll explicitly. + packetCfg.cls = UBX_CLASS_CFG; + packetCfg.id = UBX_CFG_RATE; + packetCfg.len = 0; + packetCfg.startingSpot = 0; - if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) - return (true); + //The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); - if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) - { - return (true); - } + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); - return (false); + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + return (true); } + + return (false); } // PRIVATE: Allocate RAM for packetUBXCFGRATE and initialize it @@ -7431,10 +7418,10 @@ boolean SFE_UBLOX_GNSS::initPacketUBXCFGRATE() _debugSerial->println(F("initPacketUBXCFGRATE: PANIC! RAM allocation failed!")); return (false); } - packetUBXCFGRATE->automaticFlags.flags.all = 0; - packetUBXCFGRATE->callbackPointer = NULL; - packetUBXCFGRATE->callbackData = NULL; - packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0; + packetUBXCFGRATE->automaticFlags.flags.all = 0; // Redundant + packetUBXCFGRATE->callbackPointer = NULL; // Redundant + packetUBXCFGRATE->callbackData = NULL; // Redundant + packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0; // Mark all data as stale/read return (true); } @@ -9114,7 +9101,11 @@ boolean SFE_UBLOX_GNSS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait payloadCfg[0] = measurementRate & 0xFF; //measRate LSB payloadCfg[1] = measurementRate >> 8; //measRate MSB - return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + boolean result = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + + flushCFGRATE(); // Mark the polled measurement and navigation rate data as stale + + return (result); } //Get the rate at which the module is outputting nav solutions @@ -9155,7 +9146,11 @@ boolean SFE_UBLOX_GNSS::setMeasurementRate(uint16_t rate, uint16_t maxWait) payloadCfg[0] = rate & 0xFF; //measRate LSB payloadCfg[1] = rate >> 8; //measRate MSB - return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + boolean result = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + + flushCFGRATE(); // Mark the polled measurement and navigation rate data as stale + + return (result); } //Return the elapsed time between GNSS measurements in milliseconds, which defines the rate @@ -9190,7 +9185,11 @@ boolean SFE_UBLOX_GNSS::setNavigationRate(uint16_t rate, uint16_t maxWait) payloadCfg[2] = rate & 0xFF; //navRate LSB payloadCfg[3] = rate >> 8; //navRate MSB - return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + boolean result = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK + + flushCFGRATE(); // Mark the polled measurement and navigation rate data as stale + + return (result); } //Return the ratio between the number of measurements and the number of navigation solutions. Unit is cycles @@ -9208,6 +9207,13 @@ uint16_t SFE_UBLOX_GNSS::getNavigationRate(uint16_t maxWait) return (packetUBXCFGRATE->data.navRate); } +//Mark the CFG RATE data as read/stale +void SFE_UBLOX_GNSS::flushCFGRATE() +{ + if (packetUBXCFGRATE == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0; //Mark all datums as stale (read before) +} + // ***** DOP Helper Functions uint16_t SFE_UBLOX_GNSS::getGeometricDOP(uint16_t maxWait) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 8e8ab2d..c7c1421 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -936,6 +936,7 @@ class SFE_UBLOX_GNSS uint16_t getMeasurementRate(uint16_t maxWait = defaultMaxWait); //Return the elapsed time between GNSS measurements in milliseconds boolean setNavigationRate(uint16_t rate, uint16_t maxWait = defaultMaxWait); //Set the ratio between the number of measurements and the number of navigation solutions. Unit is cycles. Max is 127 uint16_t getNavigationRate(uint16_t maxWait = defaultMaxWait); //Return the ratio between the number of measurements and the number of navigation solutions. Unit is cycles + void flushCFGRATE(); // Mark the measurement and navigation rate data as stale - used by the set rate functions // Helper functions for DOP From cc2e75fa275ba22da5a719136ad0f82458106174 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 11:56:45 +0100 Subject: [PATCH 02/11] Add logNMEA and an example --- .../DataLoggingExample6_NMEA.ino | 242 ++++++++++++++++++ keywords.txt | 2 + src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 12 + src/SparkFun_u-blox_GNSS_Arduino_Library.h | 5 + 4 files changed, 261 insertions(+) create mode 100644 examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino diff --git a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino new file mode 100644 index 0000000..6101326 --- /dev/null +++ b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino @@ -0,0 +1,242 @@ +/* + Demonstrate how to log NMEA and UBX data simultaneously + By: Paul Clark + SparkFun Electronics + Date: April 13th, 2021 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to configure the u-blox GNSS to send PVT reports automatically + and log those and any incoming NMEA messages to SD card in UBX format + + ** Please note: NMEA logging relies upon processNMEA ** + ** You will not be able to log NMEA data automatically through the library if you overwrite processNMEA with your own function ** + + ** Please note: this example will only work on processors like the Artemis which have plenty of RAM available ** + + This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor + but can be adapted by changing the chip select pin and SPI definitions: + https://www.sparkfun.com/products/16829 + https://www.sparkfun.com/products/16401 + + Hardware Connections: + Please see: https://learn.sparkfun.com/tutorials/micromod-data-logging-carrier-board-hookup-guide + Insert the Artemis Processor into the MicroMod Data Logging Carrier Board and secure with the screw. + Connect your GNSS breakout to the Carrier Board using a Qwiic cable. + Connect an antenna to your GNSS board if required. + Insert a formatted micro-SD card into the socket on the Carrier Board. + Connect the Carrier Board to your computer using a USB-C cable. + Ensure you have the SparkFun Apollo3 boards installed: http://boardsmanager/All#SparkFun_Apollo3 + This code has been tested using version 1.2.1 of the Apollo3 boards on Arduino IDE 1.8.13. + Select "SparkFun Artemis MicroMod" as the board type. + Press upload to upload the code onto the Artemis. + Open the Serial Monitor at 115200 baud to see the output. + + To minimise I2C bus errors, it is a good idea to open the I2C pull-up split pad links on + both the MicroMod Data Logging Carrier Board and the u-blox module breakout. + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + ZOE-M8Q: https://www.sparkfun.com/products/15193 + SAM-M8Q: https://www.sparkfun.com/products/15210 +*/ + +#include +#include +#include //Needed for I2C to GNSS + +#include //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +File myFile; //File that all GNSS data is written to + +#define sdChipSelect CS //Primary SPI Chip Select is CS for the MicroMod Artemis Processor. Adjust for your processor if necessary. + +#define sdWriteSize 512 // Write data to the SD card in blocks of 512 bytes +#define fileBufferSize 16384 // Allocate 16KBytes of RAM for UBX message storage + +unsigned long lastPrint; // Record when the last Serial print took place +unsigned long bytesWritten = 0; // Record how many bytes have been written to SD card + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun u-blox Example"); + + pinMode(LED_BUILTIN, OUTPUT); // Flash LED_BUILTIN each time we write to the SD card + digitalWrite(LED_BUILTIN, LOW); + + Wire.begin(); // Start I2C communication + +#if defined(AM_PART_APOLLO3) + Wire.setPullups(0); // On the Artemis, we can disable the internal I2C pull-ups too to help reduce bus errors +#endif + + while (Serial.available()) // Make sure the Serial buffer is empty + { + Serial.read(); + } + + Serial.println(F("Press any key to start logging.")); + + while (!Serial.available()) // Wait for the user to press a key + { + ; // Do nothing + } + + delay(100); // Wait, just in case multiple characters were sent + + while (Serial.available()) // Empty the Serial buffer + { + Serial.read(); + } + + Serial.println("Initializing SD card..."); + + // See if the card is present and can be initialized: + if (!SD.begin(sdChipSelect)) + { + Serial.println("Card failed, or not present. Freezing..."); + // don't do anything more: + while (1); + } + Serial.println("SD card initialized."); + + // Create or open a file called "PVT_NMEA.ubx" on the SD card. + // If the file already exists, the new data is appended to the end of the file. + myFile = SD.open("PVT_NMEA.ubx", FILE_WRITE); + if(!myFile) + { + Serial.println(F("Failed to create UBX data file! Freezing...")); + while (1); + } + + //myGNSS.enableDebugging(); // Uncomment this line to enable lots of helpful GNSS debug messages on Serial + //myGNSS.enableDebugging(Serial, true); // Or, uncomment this line to enable only the important GNSS debug messages on Serial + + //myGNSS.disableUBX7Fcheck(); // RAWX data can legitimately contain 0x7F. Uncomment this line to disable the "7F" check in checkUbloxI2C + + // SD cards can occasionally 'hiccup' and a write takes much longer than usual. The buffer needs to be big enough + // to hold the backlog of data if/when this happens. + // getMaxFileBufferAvail will tell us the maximum number of bytes which the file buffer has contained. + myGNSS.setFileBufferSize(fileBufferSize); // setFileBufferSize must be called _before_ .begin + + if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing...")); + while (1); + } + + // Uncomment the next line if you want to reset your module back to the default settings with 1Hz navigation rate + // This will (re)enable the standard NMEA messages too + // This will also disable any "auto" UBX messages that were enabled and saved by other examples and reduce the load on the I2C bus + //myGNSS.factoryDefault(); delay(5000); + + myGNSS.setI2COutput(COM_TYPE_UBX | COM_TYPE_NMEA); //Set the I2C port to output both UBX and NMEA messages + + //myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Optional: save (only) the communications port settings to flash and BBR + + myGNSS.setNavigationFrequency(1); //Produce one navigation solution per second + + myGNSS.setAutoPVT(true, false); // Enable automatic NAV PVT messages: without callback; without implicit update + myGNSS.logNAVPVT(); // Enable NAV PVT data logging + + myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 1); // Ensure the GxGGA (Global positioning system fix data) message is enabled. Send every measurement. + myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C, 1); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every measurement. + myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C, 1); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every measurement. + myGNSS.logNMEA(); // Enable NMEA logging + + Serial.println(F("Press any key to stop logging.")); + + lastPrint = millis(); // Initialize lastPrint +} + +void loop() +{ + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + while (myGNSS.fileBufferAvailable() >= sdWriteSize) // Check to see if we have at least sdWriteSize waiting in the buffer + { + digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN each time we write to the SD card + + uint8_t myBuffer[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card + + myGNSS.extractFileBufferData((uint8_t *)&myBuffer, sdWriteSize); // Extract exactly sdWriteSize bytes from the UBX file buffer and put them into myBuffer + + myFile.write(myBuffer, sdWriteSize); // Write exactly sdWriteSize bytes from myBuffer to the ubxDataFile on the SD card + + bytesWritten += sdWriteSize; // Update bytesWritten + + // In case the SD writing is slow or there is a lot of data to write, keep checking for the arrival of new data + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + + digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off again + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + if (millis() > (lastPrint + 1000)) // Print bytesWritten once per second + { + Serial.print(F("The number of bytes written to SD card is ")); // Print how many bytes have been written to SD card + Serial.println(bytesWritten); + + uint16_t maxBufferBytes = myGNSS.getMaxFileBufferAvail(); // Get how full the file buffer has been (not how full it is now) + + //Serial.print(F("The maximum number of bytes which the file buffer has contained is: ")); // It is a fun thing to watch how full the buffer gets + //Serial.println(maxBufferBytes); + + if (maxBufferBytes > ((fileBufferSize / 5) * 4)) // Warn the user if fileBufferSize was more than 80% full + { + Serial.println(F("Warning: the file buffer has been over 80% full. Some data may have been lost.")); + } + + lastPrint = millis(); // Update lastPrint + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- + + if (Serial.available()) // Check if the user wants to stop logging + { + uint16_t remainingBytes = myGNSS.fileBufferAvailable(); // Check if there are any bytes remaining in the file buffer + + while (remainingBytes > 0) // While there is still data in the file buffer + { + digitalWrite(LED_BUILTIN, HIGH); // Flash LED_BUILTIN while we write to the SD card + + uint8_t myBuffer[sdWriteSize]; // Create our own buffer to hold the data while we write it to SD card + + uint16_t bytesToWrite = remainingBytes; // Write the remaining bytes to SD card sdWriteSize bytes at a time + if (bytesToWrite > sdWriteSize) + { + bytesToWrite = sdWriteSize; + } + + myGNSS.extractFileBufferData((uint8_t *)&myBuffer, bytesToWrite); // Extract bytesToWrite bytes from the UBX file buffer and put them into myBuffer + + myFile.write(myBuffer, bytesToWrite); // Write bytesToWrite bytes from myBuffer to the ubxDataFile on the SD card + + bytesWritten += bytesToWrite; // Update bytesWritten + + remainingBytes -= bytesToWrite; // Decrement remainingBytes + } + + digitalWrite(LED_BUILTIN, LOW); // Turn LED_BUILTIN off + + Serial.print(F("The total number of bytes written to SD card is ")); // Print how many bytes have been written to SD card + Serial.println(bytesWritten); + + myFile.close(); // Close the data file + + Serial.println(F("Logging stopped. Freezing...")); + while(1); // Do nothing more + } + + // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +} diff --git a/keywords.txt b/keywords.txt index f0aeb72..f4ba8e0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -382,6 +382,8 @@ initPacketUBXHNRPVT KEYWORD2 flushHNRPVT KEYWORD2 logHNRPVT KEYWORD2 +logNMEA KEYWORD2 + setNavigationFrequency KEYWORD2 getNavigationFrequency KEYWORD2 setMeasurementRate KEYWORD2 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 943cdeb..895f374 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -1259,6 +1259,10 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming) //If user has assigned an output port then pipe the characters there if (_nmeaOutputPort != NULL) _nmeaOutputPort->write(incoming); //Echo this byte to the serial port + + //If _logNMEA is true, attempt to store incoming in the file buffer + if (_logNMEA) + storeFileBytes((uint8_t *)&incoming, 1); } //We need to be able to identify an RTCM packet and then the length @@ -9073,6 +9077,14 @@ void SFE_UBLOX_GNSS::logHNRPVT(boolean enabled) packetUBXHNRPVT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** Helper Functions for NMEA Logging + +//Log NMEA data in file buffer - if it exists! User needs to call setFileBufferSize before .begin +void SFE_UBLOX_GNSS::logNMEA(boolean enabled) +{ + _logNMEA = enabled; +} + // ***** CFG RATE Helper Functions //Set the rate at which the module will give us an updated navigation solution diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index c7c1421..a392bef 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -928,6 +928,9 @@ class SFE_UBLOX_GNSS void flushHNRPVT(); //Mark all the data as read/stale void logHNRPVT(boolean enabled = true); // Log data to file buffer + // Helper function for NMEA logging + void logNMEA(boolean enabled = true); // Log NMEA data to file buffer + // Helper functions for CFG RATE boolean setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = defaultMaxWait); //Set the number of nav solutions sent per second @@ -1183,6 +1186,8 @@ class SFE_UBLOX_GNSS boolean ubx7FcheckDisabled = false; // Flag to indicate if the "7F" check should be ignored in checkUbloxI2C + boolean _logNMEA = false; // Flag to indicate if NMEA data should be added to the file buffer + //The packet buffers //These are pointed at from within the ubxPacket uint8_t payloadAck[2]; // Holds the requested ACK/NACK From 7cdb4b00ebc99973ec07cb30b1eee78bdab1fc79 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 11:57:16 +0100 Subject: [PATCH 03/11] VSC whitespace changes --- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index a392bef..06ea72c 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -580,15 +580,15 @@ class SFE_UBLOX_GNSS boolean setPortOutput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to output UBX, NMEA, RTCM3 or a combination thereof boolean setPortInput(uint8_t portID, uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure a given port to input UBX, NMEA, RTCM3 or a combination thereof - boolean setI2CAddress(uint8_t deviceAddress, uint16_t maxTime = defaultMaxWait); //Changes the I2C address of the u-blox module + boolean setI2CAddress(uint8_t deviceAddress, uint16_t maxTime = defaultMaxWait); //Changes the I2C address of the u-blox module void setSerialRate(uint32_t baudrate, uint8_t uartPort = COM_PORT_UART1, uint16_t maxTime = defaultMaxWait); //Changes the serial baud rate of the u-blox module, uartPort should be COM_PORT_UART1/2 boolean setI2COutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure I2C port to output UBX, NMEA, RTCM3 or a combination thereof - boolean setUART1Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART1 port to output UBX, NMEA, RTCM3 or a combination thereof - boolean setUART2Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART2 port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setUART1Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART1 port to output UBX, NMEA, RTCM3 or a combination thereof + boolean setUART2Output(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure UART2 port to output UBX, NMEA, RTCM3 or a combination thereof boolean setUSBOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure USB port to output UBX, NMEA, RTCM3 or a combination thereof boolean setSPIOutput(uint8_t comSettings, uint16_t maxWait = defaultMaxWait); //Configure SPI port to output UBX, NMEA, RTCM3 or a combination thereof - void setNMEAOutputPort(Stream &nmeaOutputPort); //Sets the internal variable for the port to direct NMEA characters to + void setNMEAOutputPort(Stream &nmeaOutputPort); //Sets the internal variable for the port to direct NMEA characters to //Reset to defaults From 1220ef204aa262a74635c761d4bdeeba8ed91b5a Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 15:30:20 +0100 Subject: [PATCH 04/11] Move logNMEA into process so it does not matter if processNMEA is overwritten --- .../DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino | 3 --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino index 6101326..80e2b62 100644 --- a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino +++ b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino @@ -9,9 +9,6 @@ This example shows how to configure the u-blox GNSS to send PVT reports automatically and log those and any incoming NMEA messages to SD card in UBX format - ** Please note: NMEA logging relies upon processNMEA ** - ** You will not be able to log NMEA data automatically through the library if you overwrite processNMEA with your own function ** - ** Please note: this example will only work on processors like the Artemis which have plenty of RAM available ** This code is intended to be run on the MicroMod Data Logging Carrier Board using the Artemis Processor diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 895f374..cfba10d 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -1243,6 +1243,10 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r } else if (currentSentence == NMEA) { + //If _logNMEA is true, attempt to store incoming in the file buffer + if (_logNMEA) + storeFileBytes(&incoming, 1); + processNMEA(incoming); //Process each NMEA character } else if (currentSentence == RTCM) @@ -1259,10 +1263,6 @@ void SFE_UBLOX_GNSS::processNMEA(char incoming) //If user has assigned an output port then pipe the characters there if (_nmeaOutputPort != NULL) _nmeaOutputPort->write(incoming); //Echo this byte to the serial port - - //If _logNMEA is true, attempt to store incoming in the file buffer - if (_logNMEA) - storeFileBytes((uint8_t *)&incoming, 1); } //We need to be able to identify an RTCM packet and then the length From e14ce7366c7d1a6a0bc066ebdb6ef7d014ac94de Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 19:17:31 +0100 Subject: [PATCH 05/11] Add set/getNMEALoggingMask. Allows selective logging of enabled NMEA messages --- .../DataLoggingExample6_NMEA.ino | 4 +- keywords.txt | 28 +++++- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 87 +++++++++++++++++-- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 81 +++++++++++++++-- 4 files changed, 183 insertions(+), 17 deletions(-) diff --git a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino index 80e2b62..3541b9c 100644 --- a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino +++ b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino @@ -144,7 +144,9 @@ void setup() myGNSS.enableNMEAMessage(UBX_NMEA_GGA, COM_PORT_I2C, 1); // Ensure the GxGGA (Global positioning system fix data) message is enabled. Send every measurement. myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C, 1); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every measurement. myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C, 1); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every measurement. - myGNSS.logNMEA(); // Enable NMEA logging + + myGNSS.setNMEALoggingMask(SFE_UBLOX_LOG_NMEA_ALL); // Enable logging of all enabled NMEA messages + //myGNSS.setNMEALoggingMask(SFE_UBLOX_LOG_NMEA_GGA | SFE_UBLOX_LOG_NMEA_GSA); // Or we can, for example, log only GxGGA and GxGSA. Ignore GxGSV Serial.println(F("Press any key to stop logging.")); diff --git a/keywords.txt b/keywords.txt index f4ba8e0..d32108a 100644 --- a/keywords.txt +++ b/keywords.txt @@ -382,7 +382,8 @@ initPacketUBXHNRPVT KEYWORD2 flushHNRPVT KEYWORD2 logHNRPVT KEYWORD2 -logNMEA KEYWORD2 +setNMEALoggingMask KEYWORD2 +getNMEALoggingMask KEYWORD2 setNavigationFrequency KEYWORD2 getNavigationFrequency KEYWORD2 @@ -546,16 +547,41 @@ UBX_NMEA_GLQ LITERAL1 UBX_NMEA_GNQ LITERAL1 UBX_NMEA_GNS LITERAL1 UBX_NMEA_GPQ LITERAL1 +UBX_NMEA_GQQ LITERAL1 UBX_NMEA_GRS LITERAL1 UBX_NMEA_GSA LITERAL1 UBX_NMEA_GST LITERAL1 UBX_NMEA_GSV LITERAL1 +UBX_NMEA_RLM LITERAL1 UBX_NMEA_RMC LITERAL1 UBX_NMEA_TXT LITERAL1 UBX_NMEA_VLW LITERAL1 UBX_NMEA_VTG LITERAL1 UBX_NMEA_ZDA LITERAL1 +SFE_UBLOX_LOG_NMEA_ALL LITERAL1 +SFE_UBLOX_LOG_NMEA_DTM LITERAL1 +SFE_UBLOX_LOG_NMEA_GAQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GBQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GBS LITERAL1 +SFE_UBLOX_LOG_NMEA_GGA LITERAL1 +SFE_UBLOX_LOG_NMEA_GLL LITERAL1 +SFE_UBLOX_LOG_NMEA_GLQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GNQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GNS LITERAL1 +SFE_UBLOX_LOG_NMEA_GPQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GQQ LITERAL1 +SFE_UBLOX_LOG_NMEA_GRS LITERAL1 +SFE_UBLOX_LOG_NMEA_GSA LITERAL1 +SFE_UBLOX_LOG_NMEA_GST LITERAL1 +SFE_UBLOX_LOG_NMEA_GSV LITERAL1 +SFE_UBLOX_LOG_NMEA_RLM LITERAL1 +SFE_UBLOX_LOG_NMEA_RMC LITERAL1 +SFE_UBLOX_LOG_NMEA_TXT LITERAL1 +SFE_UBLOX_LOG_NMEA_VLW LITERAL1 +SFE_UBLOX_LOG_NMEA_VTG LITERAL1 +SFE_UBLOX_LOG_NMEA_ZDA LITERAL1 + UBX_NAV_ATT LITERAL1 UBX_NAV_CLOCK LITERAL1 UBX_NAV_DOP LITERAL1 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index cfba10d..8427687 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -50,6 +50,8 @@ SFE_UBLOX_GNSS::SFE_UBLOX_GNSS(void) pinMode((uint8_t)debugPin, OUTPUT); digitalWrite((uint8_t)debugPin, HIGH); } + + _logNMEA.all = 0; // Default to logging no NMEA messages } //Stop all automatic message processing. Free all used RAM @@ -1020,6 +1022,7 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r } else if (incoming == '$') { + nmeaByteCounter = 0; // Reset the NMEA byte counter currentSentence = NMEA; } else if (incoming == 0xD3) //RTCM frames start with 0xD3 @@ -1241,13 +1244,48 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r //Finally, increment the frame counter ubxFrameCounter++; } - else if (currentSentence == NMEA) + else if (currentSentence == NMEA) // Process incoming NMEA mesages. Selectively log if desired. { - //If _logNMEA is true, attempt to store incoming in the file buffer - if (_logNMEA) - storeFileBytes(&incoming, 1); + if ((nmeaByteCounter == 0) && (incoming != '$')) + { + currentSentence = NONE; //Something went wrong. Reset. (Almost certainly redundant!) + } + else if ((nmeaByteCounter == 1) && (incoming != 'G')) + { + currentSentence = NONE; //Something went wrong. Reset. + } + else if ((nmeaByteCounter >= 0) && (nmeaByteCounter <= 5)) + { + nmeaAddressField[nmeaByteCounter] = incoming; // Store the start character and NMEA address field + } + + if (nmeaByteCounter == 5) + { + // We've just received the end of the address field. Check if it is selected for logging + if (logThisNMEA()) + { + storeFileBytes(&nmeaAddressField[0], 6); // Add start character and address field to the file buffer + } + } - processNMEA(incoming); //Process each NMEA character + if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer? + { + if (logThisNMEA()) + storeFileBytes(&incoming, 1); // Add incoming to the file buffer + } + + if (incoming == '*') + nmeaByteCounter = -5; // We are expecting * plus two checksum bytes plus CR and LF + + nmeaByteCounter++; // Increment the byte counter + + if (nmeaByteCounter == maxNMEAByteCount) // Check if we have processed too many bytes + currentSentence = NONE; //Something went wrong. Reset. + + if (nmeaByteCounter == 0) // Check if we are done + currentSentence = NONE; // All done! + + processNMEA(incoming); //Process each and every NMEA character } else if (currentSentence == RTCM) { @@ -1255,6 +1293,34 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r } } +// PRIVATE: Return true if we should log this NMEA message +boolean SFE_UBLOX_GNSS::logThisNMEA() +{ + if (_logNMEA.bits.all == 1) return (true); + if ((nmeaAddressField[3] == 'D') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'M') && (_logNMEA.bits.UBX_NMEA_DTM == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GAQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GBQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GBS == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GGA == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L') && (_logNMEA.bits.UBX_NMEA_GLL == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GLQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GNQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GNS == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GPQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GQQ == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GRS == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GSA == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T') && (_logNMEA.bits.UBX_NMEA_GST == 1)) return (true); + if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V') && (_logNMEA.bits.UBX_NMEA_GSV == 1)) return (true); + if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'M') && (_logNMEA.bits.UBX_NMEA_RLM == 1)) return (true); + if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'M') && (nmeaAddressField[5] == 'C') && (_logNMEA.bits.UBX_NMEA_RMC == 1)) return (true); + if ((nmeaAddressField[3] == 'T') && (nmeaAddressField[4] == 'X') && (nmeaAddressField[5] == 'T') && (_logNMEA.bits.UBX_NMEA_TXT == 1)) return (true); + if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'W') && (_logNMEA.bits.UBX_NMEA_VLW == 1)) return (true); + if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'G') && (_logNMEA.bits.UBX_NMEA_VTG == 1)) return (true); + if ((nmeaAddressField[3] == 'Z') && (nmeaAddressField[4] == 'D') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_ZDA == 1)) return (true); + return (false); +} + //This is the default or generic NMEA processor. We're only going to pipe the data to serial port so we can see it. //User could overwrite this function to pipe characters to nmea.process(c) of tinyGPS or MicroNMEA //Or user could pipe each character to a buffer, radio, etc. @@ -9079,10 +9145,15 @@ void SFE_UBLOX_GNSS::logHNRPVT(boolean enabled) // ***** Helper Functions for NMEA Logging -//Log NMEA data in file buffer - if it exists! User needs to call setFileBufferSize before .begin -void SFE_UBLOX_GNSS::logNMEA(boolean enabled) +// Log selected NMEA messages to file buffer - if the messages are enabled and if the file buffer exists +// User needs to call setFileBufferSize before .begin +void SFE_UBLOX_GNSS::setNMEALoggingMask(uint32_t messages) +{ + _logNMEA.all = messages; +} +uint32_t SFE_UBLOX_GNSS::getNMEALoggingMask() { - _logNMEA = enabled; + return (_logNMEA.all); } // ***** CFG RATE Helper Functions diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 06ea72c..20153c1 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -99,6 +99,66 @@ typedef enum SFE_UBLOX_PACKET_PACKETAUTO } sfe_ublox_packet_buffer_e; +// Define a struct to allow selective logging of NMEA messages +typedef struct +{ + union + { + uint32_t all; + struct + { + uint32_t all : 1; + uint32_t UBX_NMEA_DTM : 1; + uint32_t UBX_NMEA_GAQ : 1; + uint32_t UBX_NMEA_GBQ : 1; + uint32_t UBX_NMEA_GBS : 1; + uint32_t UBX_NMEA_GGA : 1; + uint32_t UBX_NMEA_GLL : 1; + uint32_t UBX_NMEA_GLQ : 1; + uint32_t UBX_NMEA_GNQ : 1; + uint32_t UBX_NMEA_GNS : 1; + uint32_t UBX_NMEA_GPQ : 1; + uint32_t UBX_NMEA_GQQ : 1; + uint32_t UBX_NMEA_GRS : 1; + uint32_t UBX_NMEA_GSA : 1; + uint32_t UBX_NMEA_GST : 1; + uint32_t UBX_NMEA_GSV : 1; + uint32_t UBX_NMEA_RLM : 1; + uint32_t UBX_NMEA_RMC : 1; + uint32_t UBX_NMEA_TXT : 1; + uint32_t UBX_NMEA_VLW : 1; + uint32_t UBX_NMEA_VTG : 1; + uint32_t UBX_NMEA_ZDA : 1; + } bits; + }; +} sfe_ublox_nmea_logging_t; +// Define an enum to make it easy to enable/disable selected NMEA messages for logging +typedef enum +{ + SFE_UBLOX_LOG_NMEA_ALL = 0x00000001, + SFE_UBLOX_LOG_NMEA_DTM = 0x00000002, + SFE_UBLOX_LOG_NMEA_GAQ = 0x00000004, + SFE_UBLOX_LOG_NMEA_GBQ = 0x00000008, + SFE_UBLOX_LOG_NMEA_GBS = 0x00000010, + SFE_UBLOX_LOG_NMEA_GGA = 0x00000020, + SFE_UBLOX_LOG_NMEA_GLL = 0x00000040, + SFE_UBLOX_LOG_NMEA_GLQ = 0x00000080, + SFE_UBLOX_LOG_NMEA_GNQ = 0x00000100, + SFE_UBLOX_LOG_NMEA_GNS = 0x00000200, + SFE_UBLOX_LOG_NMEA_GPQ = 0x00000400, + SFE_UBLOX_LOG_NMEA_GQQ = 0x00000800, + SFE_UBLOX_LOG_NMEA_GRS = 0x00001000, + SFE_UBLOX_LOG_NMEA_GSA = 0x00002000, + SFE_UBLOX_LOG_NMEA_GST = 0x00004000, + SFE_UBLOX_LOG_NMEA_GSV = 0x00008000, + SFE_UBLOX_LOG_NMEA_RLM = 0x00010000, + SFE_UBLOX_LOG_NMEA_RMC = 0x00020000, + SFE_UBLOX_LOG_NMEA_TXT = 0x00040000, + SFE_UBLOX_LOG_NMEA_VLW = 0x00080000, + SFE_UBLOX_LOG_NMEA_VTG = 0x00100000, + SFE_UBLOX_LOG_NMEA_ZDA = 0x00200000 +} sfe_ublox_nmea_logging_selective_e; + //Registers const uint8_t UBX_SYNCH_1 = 0xB5; const uint8_t UBX_SYNCH_2 = 0x62; @@ -159,7 +219,7 @@ const uint8_t UBX_CFG_VALSET = 0x8A; //Used for config of higher version u-blox //Class: NMEA //The following are used to enable NMEA messages. Descriptions come from the NMEA messages overview in the ZED-F9P Interface Description -const uint8_t UBX_NMEA_MSB = 0xF0; //All NMEA enable commands have 0xF0 as MSB +const uint8_t UBX_NMEA_MSB = 0xF0; //All NMEA enable commands have 0xF0 as MSB. Equal to UBX_CLASS_NMEA const uint8_t UBX_NMEA_DTM = 0x0A; //GxDTM (datum reference) const uint8_t UBX_NMEA_GAQ = 0x45; //GxGAQ (poll a standard message (if the current talker ID is GA)) const uint8_t UBX_NMEA_GBQ = 0x44; //GxGBQ (poll a standard message (if the current Talker ID is GB)) @@ -169,11 +229,13 @@ const uint8_t UBX_NMEA_GLL = 0x01; //GxGLL (latitude and long, whith time of pos const uint8_t UBX_NMEA_GLQ = 0x43; //GxGLQ (poll a standard message (if the current Talker ID is GL)) const uint8_t UBX_NMEA_GNQ = 0x42; //GxGNQ (poll a standard message (if the current Talker ID is GN)) const uint8_t UBX_NMEA_GNS = 0x0D; //GxGNS (GNSS fix data) -const uint8_t UBX_NMEA_GPQ = 0x040; //GxGPQ (poll a standard message (if the current Talker ID is GP)) +const uint8_t UBX_NMEA_GPQ = 0x40; //GxGPQ (poll a standard message (if the current Talker ID is GP)) +const uint8_t UBX_NMEA_GQQ = 0x47; //GxGQQ (poll a standard message (if the current Talker ID is GQ)) const uint8_t UBX_NMEA_GRS = 0x06; //GxGRS (GNSS range residuals) const uint8_t UBX_NMEA_GSA = 0x02; //GxGSA (GNSS DOP and Active satellites) const uint8_t UBX_NMEA_GST = 0x07; //GxGST (GNSS Pseudo Range Error Statistics) const uint8_t UBX_NMEA_GSV = 0x03; //GxGSV (GNSS satellites in view) +const uint8_t UBX_NMEA_RLM = 0x0B; //GxRMC (Return link message (RLM)) const uint8_t UBX_NMEA_RMC = 0x04; //GxRMC (Recommended minimum data) const uint8_t UBX_NMEA_TXT = 0x41; //GxTXT (text transmission) const uint8_t UBX_NMEA_VLW = 0x0F; //GxVLW (dual ground/water distance) @@ -928,8 +990,9 @@ class SFE_UBLOX_GNSS void flushHNRPVT(); //Mark all the data as read/stale void logHNRPVT(boolean enabled = true); // Log data to file buffer - // Helper function for NMEA logging - void logNMEA(boolean enabled = true); // Log NMEA data to file buffer + // Helper functions for NMEA logging + void setNMEALoggingMask(uint32_t messages = SFE_UBLOX_LOG_NMEA_ALL); // Log selected NMEA messages to file buffer - if enabled + uint32_t getNMEALoggingMask(); // Return which NMEA messages are selected for logging to the file buffer - if enabled // Helper functions for CFG RATE @@ -1186,7 +1249,7 @@ class SFE_UBLOX_GNSS boolean ubx7FcheckDisabled = false; // Flag to indicate if the "7F" check should be ignored in checkUbloxI2C - boolean _logNMEA = false; // Flag to indicate if NMEA data should be added to the file buffer + sfe_ublox_nmea_logging_t _logNMEA; // Flags to indicate which NMEA messages should be added to the file buffer for logging //The packet buffers //These are pointed at from within the ubxPacket @@ -1218,11 +1281,15 @@ class SFE_UBLOX_GNSS unsigned long lastCheck = 0; - uint16_t ubxFrameCounter; //It counts all UBX frame. [Fixed header(2bytes), CLS(1byte), ID(1byte), length(2bytes), payload(x bytes), checksums(2bytes)] - + uint16_t ubxFrameCounter; //Count all UBX frame bytes. [Fixed header(2bytes), CLS(1byte), ID(1byte), length(2bytes), payload(x bytes), checksums(2bytes)] uint8_t rollingChecksumA; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes uint8_t rollingChecksumB; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes + int16_t nmeaByteCounter; //Count all NMEA message bytes. + const int16_t maxNMEAByteCount = 1024; // Abort NMEA message reception if nmeaByteCounter exceeds this + uint8_t nmeaAddressField[6]; // NMEA Address Field - includes the start character (*) + boolean logThisNMEA(); // Return true if we should log this NMEA message + uint16_t rtcmLen = 0; // Flag to prevent reentry into checkCallbacks From 700406956a50787aeafa11ed026c506640baa17f Mon Sep 17 00:00:00 2001 From: PaulZC Date: Tue, 13 Apr 2021 21:02:15 +0100 Subject: [PATCH 06/11] Add get/setProcessNMEAMask to allow selection of which messages are passed to processNMEA --- .../DataLoggingExample6_NMEA.ino | 4 +- .../Example2_NMEAParsing.ino | 10 +- keywords.txt | 46 ++++----- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 95 +++++++++++++++---- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 65 +++++++------ 5 files changed, 145 insertions(+), 75 deletions(-) diff --git a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino index 3541b9c..fcf00c1 100644 --- a/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino +++ b/examples/Data_Logging/DataLoggingExample6_NMEA/DataLoggingExample6_NMEA.ino @@ -145,8 +145,8 @@ void setup() myGNSS.enableNMEAMessage(UBX_NMEA_GSA, COM_PORT_I2C, 1); // Ensure the GxGSA (GNSS DOP and Active satellites) message is enabled. Send every measurement. myGNSS.enableNMEAMessage(UBX_NMEA_GSV, COM_PORT_I2C, 1); // Ensure the GxGSV (GNSS satellites in view) message is enabled. Send every measurement. - myGNSS.setNMEALoggingMask(SFE_UBLOX_LOG_NMEA_ALL); // Enable logging of all enabled NMEA messages - //myGNSS.setNMEALoggingMask(SFE_UBLOX_LOG_NMEA_GGA | SFE_UBLOX_LOG_NMEA_GSA); // Or we can, for example, log only GxGGA and GxGSA. Ignore GxGSV + myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_ALL); // Enable logging of all enabled NMEA messages + //myGNSS.setNMEALoggingMask(SFE_UBLOX_FILTER_NMEA_GGA | SFE_UBLOX_FILTER_NMEA_GSA); // Or we can, for example, log only GxGGA & GxGSA and ignore GxGSV Serial.println(F("Press any key to stop logging.")); diff --git a/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino b/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino index 35c2001..48b691b 100644 --- a/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino +++ b/examples/Example2_NMEAParsing/Example2_NMEAParsing.ino @@ -45,6 +45,10 @@ void setup() Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing.")); while (1); } + + myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_ALL); // Make sure the library is passing all NMEA messages to processNMEA + + myGNSS.setProcessNMEAMask(SFE_UBLOX_FILTER_NMEA_GGA); // Or, we can be kind to MicroNMEA and _only_ pass the GGA messages to it } void loop() @@ -60,12 +64,12 @@ void loop() Serial.println(latitude_mdeg / 1000000., 6); Serial.print("Longitude (deg): "); Serial.println(longitude_mdeg / 1000000., 6); + + nmea.clear(); // Clear the MicroNMEA storage to make sure we are getting fresh data } else { - Serial.print("No Fix - "); - Serial.print("Num. satellites: "); - Serial.println(nmea.getNumSatellites()); + Serial.println("Waiting for fresh data"); } delay(250); //Don't pound too hard on the I2C bus diff --git a/keywords.txt b/keywords.txt index d32108a..81b2fd7 100644 --- a/keywords.txt +++ b/keywords.txt @@ -384,6 +384,8 @@ logHNRPVT KEYWORD2 setNMEALoggingMask KEYWORD2 getNMEALoggingMask KEYWORD2 +setProcessNMEAMask KEYWORD2 +getProcessNMEAMask KEYWORD2 setNavigationFrequency KEYWORD2 getNavigationFrequency KEYWORD2 @@ -559,28 +561,28 @@ UBX_NMEA_VLW LITERAL1 UBX_NMEA_VTG LITERAL1 UBX_NMEA_ZDA LITERAL1 -SFE_UBLOX_LOG_NMEA_ALL LITERAL1 -SFE_UBLOX_LOG_NMEA_DTM LITERAL1 -SFE_UBLOX_LOG_NMEA_GAQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GBQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GBS LITERAL1 -SFE_UBLOX_LOG_NMEA_GGA LITERAL1 -SFE_UBLOX_LOG_NMEA_GLL LITERAL1 -SFE_UBLOX_LOG_NMEA_GLQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GNQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GNS LITERAL1 -SFE_UBLOX_LOG_NMEA_GPQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GQQ LITERAL1 -SFE_UBLOX_LOG_NMEA_GRS LITERAL1 -SFE_UBLOX_LOG_NMEA_GSA LITERAL1 -SFE_UBLOX_LOG_NMEA_GST LITERAL1 -SFE_UBLOX_LOG_NMEA_GSV LITERAL1 -SFE_UBLOX_LOG_NMEA_RLM LITERAL1 -SFE_UBLOX_LOG_NMEA_RMC LITERAL1 -SFE_UBLOX_LOG_NMEA_TXT LITERAL1 -SFE_UBLOX_LOG_NMEA_VLW LITERAL1 -SFE_UBLOX_LOG_NMEA_VTG LITERAL1 -SFE_UBLOX_LOG_NMEA_ZDA LITERAL1 +SFE_UBLOX_FILTER_NMEA_ALL LITERAL1 +SFE_UBLOX_FILTER_NMEA_DTM LITERAL1 +SFE_UBLOX_FILTER_NMEA_GAQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GBQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GBS LITERAL1 +SFE_UBLOX_FILTER_NMEA_GGA LITERAL1 +SFE_UBLOX_FILTER_NMEA_GLL LITERAL1 +SFE_UBLOX_FILTER_NMEA_GLQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GNQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GNS LITERAL1 +SFE_UBLOX_FILTER_NMEA_GPQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GQQ LITERAL1 +SFE_UBLOX_FILTER_NMEA_GRS LITERAL1 +SFE_UBLOX_FILTER_NMEA_GSA LITERAL1 +SFE_UBLOX_FILTER_NMEA_GST LITERAL1 +SFE_UBLOX_FILTER_NMEA_GSV LITERAL1 +SFE_UBLOX_FILTER_NMEA_RLM LITERAL1 +SFE_UBLOX_FILTER_NMEA_RMC LITERAL1 +SFE_UBLOX_FILTER_NMEA_TXT LITERAL1 +SFE_UBLOX_FILTER_NMEA_VLW LITERAL1 +SFE_UBLOX_FILTER_NMEA_VTG LITERAL1 +SFE_UBLOX_FILTER_NMEA_ZDA LITERAL1 UBX_NAV_ATT LITERAL1 UBX_NAV_CLOCK LITERAL1 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 8427687..7083036 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -51,7 +51,8 @@ SFE_UBLOX_GNSS::SFE_UBLOX_GNSS(void) digitalWrite((uint8_t)debugPin, HIGH); } - _logNMEA.all = 0; // Default to logging no NMEA messages + _logNMEA.all = 0; // Default to passing no NMEA messages to the file buffer + _processNMEA.all = SFE_UBLOX_FILTER_NMEA_ALL; // Default to passing all NMEA messages to process NMEA } //Stop all automatic message processing. Free all used RAM @@ -1266,12 +1267,24 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r { storeFileBytes(&nmeaAddressField[0], 6); // Add start character and address field to the file buffer } + // Check if it should be passed to processNMEA + if (processThisNMEA()) + { + processNMEA(nmeaAddressField[0]); //Process the start character and address field + processNMEA(nmeaAddressField[1]); + processNMEA(nmeaAddressField[2]); + processNMEA(nmeaAddressField[3]); + processNMEA(nmeaAddressField[4]); + processNMEA(nmeaAddressField[5]); + } } - if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer? + if ((nmeaByteCounter > 5) || (nmeaByteCounter < 0)) // Should we add incoming to the file buffer and/or pass it to processNMEA? { if (logThisNMEA()) storeFileBytes(&incoming, 1); // Add incoming to the file buffer + if (processThisNMEA()) + processNMEA(incoming); // Pass incoming to processNMEA } if (incoming == '*') @@ -1284,8 +1297,6 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r if (nmeaByteCounter == 0) // Check if we are done currentSentence = NONE; // All done! - - processNMEA(incoming); //Process each and every NMEA character } else if (currentSentence == RTCM) { @@ -1293,25 +1304,28 @@ void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t r } } -// PRIVATE: Return true if we should log this NMEA message +// PRIVATE: Return true if we should add this NMEA message to the file buffer for logging boolean SFE_UBLOX_GNSS::logThisNMEA() { if (_logNMEA.bits.all == 1) return (true); if ((nmeaAddressField[3] == 'D') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'M') && (_logNMEA.bits.UBX_NMEA_DTM == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GAQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GBQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GBS == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GGA == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L') && (_logNMEA.bits.UBX_NMEA_GLL == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GLQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GNQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GNS == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GPQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GQQ == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GRS == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GSA == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T') && (_logNMEA.bits.UBX_NMEA_GST == 1)) return (true); - if ((nmeaAddressField[3] == 'G') && (nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V') && (_logNMEA.bits.UBX_NMEA_GSV == 1)) return (true); + if (nmeaAddressField[3] == 'G') + { + if ((nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GAQ == 1)) return (true); + if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GBQ == 1)) return (true); + if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GBS == 1)) return (true); + if ((nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GGA == 1)) return (true); + if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L') && (_logNMEA.bits.UBX_NMEA_GLL == 1)) return (true); + if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GLQ == 1)) return (true); + if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GNQ == 1)) return (true); + if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GNS == 1)) return (true); + if ((nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GPQ == 1)) return (true); + if ((nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q') && (_logNMEA.bits.UBX_NMEA_GQQ == 1)) return (true); + if ((nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S') && (_logNMEA.bits.UBX_NMEA_GRS == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A') && (_logNMEA.bits.UBX_NMEA_GSA == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T') && (_logNMEA.bits.UBX_NMEA_GST == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V') && (_logNMEA.bits.UBX_NMEA_GSV == 1)) return (true); + } if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'M') && (_logNMEA.bits.UBX_NMEA_RLM == 1)) return (true); if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'M') && (nmeaAddressField[5] == 'C') && (_logNMEA.bits.UBX_NMEA_RMC == 1)) return (true); if ((nmeaAddressField[3] == 'T') && (nmeaAddressField[4] == 'X') && (nmeaAddressField[5] == 'T') && (_logNMEA.bits.UBX_NMEA_TXT == 1)) return (true); @@ -1321,6 +1335,37 @@ boolean SFE_UBLOX_GNSS::logThisNMEA() return (false); } +// PRIVATE: Return true if we should pass this NMEA message to processNMEA +boolean SFE_UBLOX_GNSS::processThisNMEA() +{ + if (_processNMEA.bits.all == 1) return (true); + if ((nmeaAddressField[3] == 'D') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'M') && (_processNMEA.bits.UBX_NMEA_DTM == 1)) return (true); + if (nmeaAddressField[3] == 'G') + { + if ((nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GAQ == 1)) return (true); + if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GBQ == 1)) return (true); + if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S') && (_processNMEA.bits.UBX_NMEA_GBS == 1)) return (true); + if ((nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A') && (_processNMEA.bits.UBX_NMEA_GGA == 1)) return (true); + if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L') && (_processNMEA.bits.UBX_NMEA_GLL == 1)) return (true); + if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GLQ == 1)) return (true); + if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GNQ == 1)) return (true); + if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S') && (_processNMEA.bits.UBX_NMEA_GNS == 1)) return (true); + if ((nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GPQ == 1)) return (true); + if ((nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q') && (_processNMEA.bits.UBX_NMEA_GQQ == 1)) return (true); + if ((nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S') && (_processNMEA.bits.UBX_NMEA_GRS == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A') && (_processNMEA.bits.UBX_NMEA_GSA == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T') && (_processNMEA.bits.UBX_NMEA_GST == 1)) return (true); + if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V') && (_processNMEA.bits.UBX_NMEA_GSV == 1)) return (true); + } + if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'M') && (_processNMEA.bits.UBX_NMEA_RLM == 1)) return (true); + if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'M') && (nmeaAddressField[5] == 'C') && (_processNMEA.bits.UBX_NMEA_RMC == 1)) return (true); + if ((nmeaAddressField[3] == 'T') && (nmeaAddressField[4] == 'X') && (nmeaAddressField[5] == 'T') && (_processNMEA.bits.UBX_NMEA_TXT == 1)) return (true); + if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'W') && (_processNMEA.bits.UBX_NMEA_VLW == 1)) return (true); + if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'G') && (_processNMEA.bits.UBX_NMEA_VTG == 1)) return (true); + if ((nmeaAddressField[3] == 'Z') && (nmeaAddressField[4] == 'D') && (nmeaAddressField[5] == 'A') && (_processNMEA.bits.UBX_NMEA_ZDA == 1)) return (true); + return (false); +} + //This is the default or generic NMEA processor. We're only going to pipe the data to serial port so we can see it. //User could overwrite this function to pipe characters to nmea.process(c) of tinyGPS or MicroNMEA //Or user could pipe each character to a buffer, radio, etc. @@ -9143,7 +9188,7 @@ void SFE_UBLOX_GNSS::logHNRPVT(boolean enabled) packetUBXHNRPVT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } -// ***** Helper Functions for NMEA Logging +// ***** Helper Functions for NMEA Logging / Processing // Log selected NMEA messages to file buffer - if the messages are enabled and if the file buffer exists // User needs to call setFileBufferSize before .begin @@ -9156,6 +9201,16 @@ uint32_t SFE_UBLOX_GNSS::getNMEALoggingMask() return (_logNMEA.all); } +// Pass selected NMEA messages to processNMEA +void SFE_UBLOX_GNSS::setProcessNMEAMask(uint32_t messages) +{ + _processNMEA.all = messages; +} +uint32_t SFE_UBLOX_GNSS::getProcessNMEAMask() +{ + return (_processNMEA.all); +} + // ***** CFG RATE Helper Functions //Set the rate at which the module will give us an updated navigation solution diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 20153c1..ca58844 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -99,7 +99,9 @@ typedef enum SFE_UBLOX_PACKET_PACKETAUTO } sfe_ublox_packet_buffer_e; -// Define a struct to allow selective logging of NMEA messages +// Define a struct to allow selective logging / processing of NMEA messages +// Set the individual bits to pass the NMEA messages to the file buffer and/or processNMEA +// Setting bits.all will pass all messages to the file buffer and processNMEA typedef struct { union @@ -131,33 +133,34 @@ typedef struct uint32_t UBX_NMEA_ZDA : 1; } bits; }; -} sfe_ublox_nmea_logging_t; -// Define an enum to make it easy to enable/disable selected NMEA messages for logging +} sfe_ublox_nmea_filtering_t; + +// Define an enum to make it easy to enable/disable selected NMEA messages for logging / processing typedef enum { - SFE_UBLOX_LOG_NMEA_ALL = 0x00000001, - SFE_UBLOX_LOG_NMEA_DTM = 0x00000002, - SFE_UBLOX_LOG_NMEA_GAQ = 0x00000004, - SFE_UBLOX_LOG_NMEA_GBQ = 0x00000008, - SFE_UBLOX_LOG_NMEA_GBS = 0x00000010, - SFE_UBLOX_LOG_NMEA_GGA = 0x00000020, - SFE_UBLOX_LOG_NMEA_GLL = 0x00000040, - SFE_UBLOX_LOG_NMEA_GLQ = 0x00000080, - SFE_UBLOX_LOG_NMEA_GNQ = 0x00000100, - SFE_UBLOX_LOG_NMEA_GNS = 0x00000200, - SFE_UBLOX_LOG_NMEA_GPQ = 0x00000400, - SFE_UBLOX_LOG_NMEA_GQQ = 0x00000800, - SFE_UBLOX_LOG_NMEA_GRS = 0x00001000, - SFE_UBLOX_LOG_NMEA_GSA = 0x00002000, - SFE_UBLOX_LOG_NMEA_GST = 0x00004000, - SFE_UBLOX_LOG_NMEA_GSV = 0x00008000, - SFE_UBLOX_LOG_NMEA_RLM = 0x00010000, - SFE_UBLOX_LOG_NMEA_RMC = 0x00020000, - SFE_UBLOX_LOG_NMEA_TXT = 0x00040000, - SFE_UBLOX_LOG_NMEA_VLW = 0x00080000, - SFE_UBLOX_LOG_NMEA_VTG = 0x00100000, - SFE_UBLOX_LOG_NMEA_ZDA = 0x00200000 -} sfe_ublox_nmea_logging_selective_e; + SFE_UBLOX_FILTER_NMEA_ALL = 0x00000001, + SFE_UBLOX_FILTER_NMEA_DTM = 0x00000002, + SFE_UBLOX_FILTER_NMEA_GAQ = 0x00000004, + SFE_UBLOX_FILTER_NMEA_GBQ = 0x00000008, + SFE_UBLOX_FILTER_NMEA_GBS = 0x00000010, + SFE_UBLOX_FILTER_NMEA_GGA = 0x00000020, + SFE_UBLOX_FILTER_NMEA_GLL = 0x00000040, + SFE_UBLOX_FILTER_NMEA_GLQ = 0x00000080, + SFE_UBLOX_FILTER_NMEA_GNQ = 0x00000100, + SFE_UBLOX_FILTER_NMEA_GNS = 0x00000200, + SFE_UBLOX_FILTER_NMEA_GPQ = 0x00000400, + SFE_UBLOX_FILTER_NMEA_GQQ = 0x00000800, + SFE_UBLOX_FILTER_NMEA_GRS = 0x00001000, + SFE_UBLOX_FILTER_NMEA_GSA = 0x00002000, + SFE_UBLOX_FILTER_NMEA_GST = 0x00004000, + SFE_UBLOX_FILTER_NMEA_GSV = 0x00008000, + SFE_UBLOX_FILTER_NMEA_RLM = 0x00010000, + SFE_UBLOX_FILTER_NMEA_RMC = 0x00020000, + SFE_UBLOX_FILTER_NMEA_TXT = 0x00040000, + SFE_UBLOX_FILTER_NMEA_VLW = 0x00080000, + SFE_UBLOX_FILTER_NMEA_VTG = 0x00100000, + SFE_UBLOX_FILTER_NMEA_ZDA = 0x00200000 +} sfe_ublox_nmea_filtering_e; //Registers const uint8_t UBX_SYNCH_1 = 0xB5; @@ -991,9 +994,13 @@ class SFE_UBLOX_GNSS void logHNRPVT(boolean enabled = true); // Log data to file buffer // Helper functions for NMEA logging - void setNMEALoggingMask(uint32_t messages = SFE_UBLOX_LOG_NMEA_ALL); // Log selected NMEA messages to file buffer - if enabled + void setNMEALoggingMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Add selected NMEA messages to file buffer - if enabled. Default to adding ALL messages to the file buffer uint32_t getNMEALoggingMask(); // Return which NMEA messages are selected for logging to the file buffer - if enabled + // Helper functions to control which NMEA messages are passed to processNMEA + void setProcessNMEAMask(uint32_t messages = SFE_UBLOX_FILTER_NMEA_ALL); // Control which NMEA messages are passed to processNMEA. Default to passing ALL messages + uint32_t getProcessNMEAMask(); // Return which NMEA messages are passed to processNMEA + // Helper functions for CFG RATE boolean setNavigationFrequency(uint8_t navFreq, uint16_t maxWait = defaultMaxWait); //Set the number of nav solutions sent per second @@ -1249,7 +1256,8 @@ class SFE_UBLOX_GNSS boolean ubx7FcheckDisabled = false; // Flag to indicate if the "7F" check should be ignored in checkUbloxI2C - sfe_ublox_nmea_logging_t _logNMEA; // Flags to indicate which NMEA messages should be added to the file buffer for logging + sfe_ublox_nmea_filtering_t _logNMEA; // Flags to indicate which NMEA messages should be added to the file buffer for logging + sfe_ublox_nmea_filtering_t _processNMEA; // Flags to indicate which NMEA messages should be passed to processNMEA //The packet buffers //These are pointed at from within the ubxPacket @@ -1289,6 +1297,7 @@ class SFE_UBLOX_GNSS const int16_t maxNMEAByteCount = 1024; // Abort NMEA message reception if nmeaByteCounter exceeds this uint8_t nmeaAddressField[6]; // NMEA Address Field - includes the start character (*) boolean logThisNMEA(); // Return true if we should log this NMEA message + boolean processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA uint16_t rtcmLen = 0; From 039004ef02dfca25f966a62089eef76b73c426a7 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Wed, 14 Apr 2021 10:07:52 +0100 Subject: [PATCH 07/11] Correct maxNMEAByteCount --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 2 +- src/SparkFun_u-blox_GNSS_Arduino_Library.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 7083036..6218af5 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -52,7 +52,7 @@ SFE_UBLOX_GNSS::SFE_UBLOX_GNSS(void) } _logNMEA.all = 0; // Default to passing no NMEA messages to the file buffer - _processNMEA.all = SFE_UBLOX_FILTER_NMEA_ALL; // Default to passing all NMEA messages to process NMEA + _processNMEA.all = SFE_UBLOX_FILTER_NMEA_ALL; // Default to passing all NMEA messages to processNMEA } //Stop all automatic message processing. Free all used RAM diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index ca58844..a1b22a7 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -1289,12 +1289,12 @@ class SFE_UBLOX_GNSS unsigned long lastCheck = 0; - uint16_t ubxFrameCounter; //Count all UBX frame bytes. [Fixed header(2bytes), CLS(1byte), ID(1byte), length(2bytes), payload(x bytes), checksums(2bytes)] + uint16_t ubxFrameCounter; //Count all UBX frame bytes. [Fixed header(2bytes), CLS(1byte), ID(1byte), length(2bytes), payload(x bytes), checksums(2bytes)] uint8_t rollingChecksumA; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes uint8_t rollingChecksumB; //Rolls forward as we receive incoming bytes. Checked against the last two A/B checksum bytes - int16_t nmeaByteCounter; //Count all NMEA message bytes. - const int16_t maxNMEAByteCount = 1024; // Abort NMEA message reception if nmeaByteCounter exceeds this + int8_t nmeaByteCounter; //Count all NMEA message bytes. + const int8_t maxNMEAByteCount = 82; // Abort NMEA message reception if nmeaByteCounter exceeds this (https://en.wikipedia.org/wiki/NMEA_0183#Message_structure) uint8_t nmeaAddressField[6]; // NMEA Address Field - includes the start character (*) boolean logThisNMEA(); // Return true if we should log this NMEA message boolean processThisNMEA(); // Return true if we should pass this NMEA message to processNMEA From c351fb5b76788b37e3e2dc23047ed9bc6a1e3121 Mon Sep 17 00:00:00 2001 From: UT2UH Date: Wed, 14 Apr 2021 19:33:31 +0300 Subject: [PATCH 08/11] UBX-NAV-TIMELS support added for leap second event --- .../Example28_GetLeapSecondInfo.ino | 143 ++++++++++++++++++ keywords.txt | 7 + src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 112 ++++++++++++++ src/SparkFun_u-blox_GNSS_Arduino_Library.h | 24 +++ src/u-blox_structs.h | 51 +++++++ 5 files changed, 337 insertions(+) create mode 100644 examples/Example28_GetLeapSecondInfo/Example28_GetLeapSecondInfo.ino diff --git a/examples/Example28_GetLeapSecondInfo/Example28_GetLeapSecondInfo.ino b/examples/Example28_GetLeapSecondInfo/Example28_GetLeapSecondInfo.ino new file mode 100644 index 0000000..a96f27c --- /dev/null +++ b/examples/Example28_GetLeapSecondInfo/Example28_GetLeapSecondInfo.ino @@ -0,0 +1,143 @@ +/* + Getting leap second event info as SNTP Leap Indicator, time to a leap second event and the number of leap seconds since GPS epoch + By: UT2UH + Date: April 14th, 2021 + License: MIT. See license file for more information but you can + basically do whatever you want with this code. + + This example shows how to query a u-blox module for the leap second event info to cast to SNTP Leap Indicator enumeration. + We also turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic dramatically. + + Leave NMEA parsing behind. Now you can simply ask the module for the datums you want! + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + NEO-M8P RTK: https://www.sparkfun.com/products/15005 + SAM-M8Q: https://www.sparkfun.com/products/15106 + + Hardware Connections: + Plug a Qwiic cable into the GNSS and a BlackBoard + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Open the serial monitor at 115200 baud to see the output +*/ + +#include //Needed for I2C to GNSS + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS +SFE_UBLOX_GNSS myGNSS; + +typedef enum { + LI_NO_WARNING, //Time leaping not scheduled + LI_LAST_MINUTE_61_SEC, //Last minute has 61 seconds + LI_LAST_MINUTE_59_SEC, //Last minute has 59 seconds + LI_ALARM_CONDITION //The NTP server's clock not synchronized +} ntp_LI_e; + + +long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module. + +void setup() +{ + Serial.begin(115200); + while (!Serial) + ; //Wait for user to open terminal + Serial.println("SparkFun u-blox Example"); + + Wire.begin(); + + if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing.")); + while (1) + ; + } + + // Uncomment the next line if you need to completely reset your module + //myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts + + myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise) + myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR + + Serial.println("Compare Unix Epoch given with reference one from https://www.epochconverter.com/"); + +} + +void loop() +{ + //Query module only every second. Doing it more often will just cause I2C traffic. + //The module only responds when a new position is available + if (millis() - lastTime > 1000) + { + lastTime = millis(); //Update the timer + + // getUnixEpoch marks the PVT data as stale so you will get Unix time and PVT time on alternate seconds + + uint32_t us; //microseconds returned by getUnixEpoch() + uint32_t epoch = myGNSS.getUnixEpoch(); + Serial.print("Unix Epoch rounded: "); + Serial.print(epoch, DEC); + epoch = myGNSS.getUnixEpoch(us); + Serial.print(" Exact Unix Epoch: "); + Serial.print(epoch, DEC); + Serial.print(" micros: "); + Serial.println(us, DEC); + int32_t timeToLeapSecEvent; + ntp_LI_e leapIndicator = (ntp_LI_e)myGNSS.getLeapIndicator(timeToLeapSecEvent); + Serial.print("NTP LI: "); + Serial.print(leapIndicator, DEC); + switch (leapIndicator){ + case LI_NO_WARNING: + Serial.print(" - No event scheduled"); + break; + case LI_LAST_MINUTE_61_SEC: + Serial.print(" - last minute will end at 23:60"); + break; + case LI_LAST_MINUTE_59_SEC: + Serial.print(" - last minute will end at 23:58"); + break; + case LI_ALARM_CONDITION: + default: + Serial.print(" - Unknown (clock not synchronized)"); + break; + } + Serial.print(". Time to the next leap second event: "); + Serial.println(timeToLeapSecEvent, DEC); + + sfe_ublox_ls_src_e leapSecSource; + Serial.print("Leap seconds since GPS Epoch (Jan 6th, 1980): "); + Serial.print(myGNSS.getCurrentLeapSeconds(leapSecSource), DEC); + switch (leapSecSource){ + case SFE_UBLOX_LS_SRC_DEFAULT: + Serial.print(" - hardcoded"); + break; + case SFE_UBLOX_LS_SRC_GLONASS: + Serial.print(" - derived from GPS and GLONASS time difference"); + break; + case SFE_UBLOX_LS_SRC_GPS: + Serial.print(" - according to GPS"); + break; + case SFE_UBLOX_LS_SRC_SBAS: + Serial.print(" - according to SBAS"); + break; + case SFE_UBLOX_LS_SRC_BEIDOU: + Serial.print(" - according to BeiDou"); + break; + case SFE_UBLOX_LS_SRC_GALILEO: + Serial.print(" - according to Galileo"); + break; + case SFE_UBLOX_LS_SRC_AIDED: + Serial.print(" - last minute will end at 23:58"); + break; + case SFE_UBLOX_LS_SRC_CONFIGURED: + Serial.print(" - as configured)"); + break; + case SFE_UBLOX_LS_SRC_UNKNOWN: + default: + Serial.print(" - source unknown"); + break; + } + Serial.println(); + } + Serial.println(); +} \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index f4ba8e0..cd258e0 100644 --- a/keywords.txt +++ b/keywords.txt @@ -28,6 +28,7 @@ UBX_NAV_HPPOSECEF_data_t KEYWORD1 UBX_NAV_HPPOSLLH_data_t KEYWORD1 UBX_NAV_CLOCK_data_t KEYWORD1 UBX_NAV_RELPOSNED_data_t KEYWORD1 +UBX_NAV_TIMELS_data_t KEYWORD1 UBX_RXM_SFRBX_data_t KEYWORD1 UBX_RXM_RAWX_data_t KEYWORD1 @@ -264,6 +265,11 @@ initPacketUBXNAVCLOCK KEYWORD2 flushNAVCLOCK KEYWORD2 logNAVCLOCK KEYWORD2 +getLeapSecondEvent KEYWORD2 +getLeapIndicator KEYWORD2 +getCurrentLeapSeconds KEYWORD2 +initPacketUBXNAVTIMELS KEYWORD2 + getSurveyStatus KEYWORD2 initPacketUBXNAVSVIN KEYWORD2 @@ -569,6 +575,7 @@ UBX_NAV_RELPOSNED LITERAL1 UBX_NAV_RESETODO LITERAL1 UBX_NAV_STATUS LITERAL1 UBX_NAV_SVIN LITERAL1 +UBX_NAV_TIMELS LITERAL1 UBX_NAV_VELECEF LITERAL1 UBX_NAV_VELNED LITERAL1 diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index cfba10d..22f97ac 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -789,6 +789,9 @@ boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) case UBX_NAV_CLOCK: if (packetUBXNAVCLOCK != NULL) result = true; break; + case UBX_NAV_TIMELS: + if (packetUBXNAVTIMELS != NULL) result = true; + break; case UBX_NAV_SVIN: if (packetUBXNAVSVIN != NULL) result = true; break; @@ -916,6 +919,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) case UBX_NAV_CLOCK: maxSize = UBX_NAV_CLOCK_LEN; break; + case UBX_NAV_TIMELS: + maxSize = UBX_NAV_TIMELS_LEN; + break; case UBX_NAV_SVIN: maxSize = UBX_NAV_SVIN_LEN; break; @@ -1949,6 +1955,26 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) } } } + else if (msg->id == UBX_NAV_TIMELS && msg->len == UBX_NAV_TIMELS_LEN) + { + //Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXNAVTIMELS != NULL) + { + packetUBXNAVTIMELS->data.iTOW = extractLong(msg, 0); + packetUBXNAVTIMELS->data.version = extractByte(msg, 4); + packetUBXNAVTIMELS->data.srcOfCurrLs = extractByte(msg, 8); + packetUBXNAVTIMELS->data.currLs = extractSignedChar(msg, 9); + packetUBXNAVTIMELS->data.srcOfLsChange = extractByte(msg, 10); + packetUBXNAVTIMELS->data.lsChange = extractSignedChar(msg, 11); + packetUBXNAVTIMELS->data.timeToLsEvent = extractSignedLong(msg, 12); + packetUBXNAVTIMELS->data.dateOfLsGpsWn = extractInt(msg, 16); + packetUBXNAVTIMELS->data.dateOfLsGpsDn = extractInt(msg, 18); + packetUBXNAVTIMELS->data.valid = extractSignedChar(msg, 23); + + //Mark all datums as fresh (not read before) + packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; + } + } else if (msg->id == UBX_NAV_SVIN && msg->len == UBX_NAV_SVIN_LEN) { //Parse various byte fields into storage - but only if we have memory allocated for it @@ -6862,6 +6888,53 @@ void SFE_UBLOX_GNSS::logNAVCLOCK(boolean enabled) packetUBXNAVCLOCK->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } +// ***** NAV TIMELS automatic support + +//Reads leap second event information and sets the global variables +//for future leap second change and number of leap seconds since GPS epoch +//Returns true if commands was successful +boolean SFE_UBLOX_GNSS::getLeapSecondEvent(uint16_t maxWait) +{ + if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data + if (packetUBXNAVTIMELS == NULL) // Abort if the RAM allocation failed + return (false); + + packetCfg.cls = UBX_CLASS_NAV; + packetCfg.id = UBX_NAV_TIMELS; + packetCfg.len = 0; + packetCfg.startingSpot = 0; + + //The data is parsed as part of processing the response + sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); + + if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED) + return (true); + + if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) + { + return (true); + } + + return (false); +} + +// PRIVATE: Allocate RAM for packetUBXNAVTIMELS and initialize it +boolean SFE_UBLOX_GNSS::initPacketUBXNAVTIMELS() +{ + packetUBXNAVTIMELS = new UBX_NAV_TIMELS_t; //Allocate RAM for the main struct + if (packetUBXNAVTIMELS == NULL) + { + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXNAVTIMELS: PANIC! RAM allocation failed!")); + return (false); + } + packetUBXNAVTIMELS->automaticFlags.flags.all = 0; + packetUBXNAVTIMELS->callbackPointer = NULL; + packetUBXNAVTIMELS->callbackData = NULL; + packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0; + return (true); +} + // ***** NAV SVIN automatic support //Reads survey in status and sets the global variables @@ -10178,6 +10251,45 @@ float SFE_UBLOX_GNSS::getSurveyInMeanAccuracy(uint16_t maxWait) // Returned as m return (((float)tempFloat) / 10000.0); //Convert 0.1mm to m } +// ***** TIMELS Helper Functions + +uint8_t SFE_UBLOX_GNSS::getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait) +{ + if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data + if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed + return 3; + + if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid == false) + getLeapSecondEvent(maxWait); + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid = false; //Since we are about to give this to user, mark this data as stale + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.lsChange = false; + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.timeToLsEvent = false; + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false; + timeToLsEvent = packetUBXNAVTIMELS->data.timeToLsEvent; + // returns NTP Leap Indicator + // 0 -no warning + // 1 -last minute of the day has 61 seconds + // 2 -last minute of the day has 59 seconds + // 3 -unknown (clock unsynchronized) + return ((boolean)packetUBXNAVTIMELS->data.valid ? (uint8_t)(packetUBXNAVTIMELS->data.lsChange == -1 ? 2 : packetUBXNAVTIMELS->data.lsChange) : 3); +} + +int8_t SFE_UBLOX_GNSS::getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait) +{ + if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data + if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed + return false; + + if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid == false) + getLeapSecondEvent(maxWait); + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid = false; //Since we are about to give this to user, mark this data as stale + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.srcOfCurrLs = false; + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.currLs = false; + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false; + source = ((sfe_ublox_ls_src_e)packetUBXNAVTIMELS->data.srcOfCurrLs); + return ((int8_t)packetUBXNAVTIMELS->data.currLs); +} + // ***** RELPOSNED Helper Functions and automatic support float SFE_UBLOX_GNSS::getRelPosN(uint16_t maxWait) // Returned as m diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index 06ea72c..69970fd 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -407,6 +407,20 @@ enum sfe_ublox_gnss_ids_e SFE_UBLOX_GNSS_ID_GLONASS }; +// The GNSS identifiers of leap second event info source - used by UBX-NAV-TIMELS +enum sfe_ublox_ls_src_e +{ + SFE_UBLOX_LS_SRC_DEFAULT, + SFE_UBLOX_LS_SRC_GLONASS, + SFE_UBLOX_LS_SRC_GPS, + SFE_UBLOX_LS_SRC_SBAS, + SFE_UBLOX_LS_SRC_BEIDOU, + SFE_UBLOX_LS_SRC_GALILEO, + SFE_UBLOX_LS_SRC_AIDED, + SFE_UBLOX_LS_SRC_CONFIGURED, + SFE_UBLOX_LS_SRC_UNKNOWN = 255 +}; + #ifndef MAX_PAYLOAD_SIZE // v2.0: keep this for backwards-compatibility, but this is largely superseded by setPacketCfgPayloadSize #define MAX_PAYLOAD_SIZE 256 //We need ~220 bytes for getProtocolVersion on most ublox modules @@ -800,6 +814,9 @@ class SFE_UBLOX_GNSS // Add "auto" support for NAV SVIN - to avoid needing 'global' storage boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status + // Add "auto" support for NAV TIMELS - to avoid needing 'global' storage + boolean getLeapSecondEvent(uint16_t maxWait); //Reads leap second event info + boolean getRELPOSNED(uint16_t maxWait = defaultMaxWait); //Get Relative Positioning Information of the NED frame boolean setAutoRELPOSNED(boolean enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic RELPOSNED reports boolean setAutoRELPOSNED(boolean enabled, boolean implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic RELPOSNED, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update @@ -1032,6 +1049,11 @@ class SFE_UBLOX_GNSS uint16_t getSurveyInObservationTime(uint16_t maxWait = defaultMaxWait); // Truncated to 65535 seconds float getSurveyInMeanAccuracy(uint16_t maxWait = defaultMaxWait); // Returned as m + // Helper functions for TIMELS + + uint8_t getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait = defaultMaxWait); + int8_t getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait = defaultMaxWait); + // Helper functions for RELPOSNED float getRelPosN(uint16_t maxWait = defaultMaxWait); // Returned as m @@ -1084,6 +1106,7 @@ class SFE_UBLOX_GNSS UBX_NAV_HPPOSECEF_t *packetUBXNAVHPPOSECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_HPPOSLLH_t *packetUBXNAVHPPOSLLH = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_CLOCK_t *packetUBXNAVCLOCK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_NAV_TIMELS_t *packetUBXNAVTIMELS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_SVIN_t *packetUBXNAVSVIN = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_NAV_RELPOSNED_t *packetUBXNAVRELPOSNED = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1157,6 +1180,7 @@ class SFE_UBLOX_GNSS boolean initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it boolean initPacketUBXNAVHPPOSLLH(); // Allocate RAM for packetUBXNAVHPPOSLLH and initialize it boolean initPacketUBXNAVCLOCK(); // Allocate RAM for packetUBXNAVCLOCK and initialize it + boolean initPacketUBXNAVTIMELS(); // Allocate RAM for packetUBXNAVTIMELS and initialize it boolean initPacketUBXNAVSVIN(); // Allocate RAM for packetUBXNAVSVIN and initialize it boolean initPacketUBXNAVRELPOSNED(); // Allocate RAM for packetUBXNAVRELPOSNED and initialize it boolean initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 50611bf..574baa2 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -854,6 +854,57 @@ typedef struct UBX_NAV_CLOCK_data_t *callbackData; } UBX_NAV_CLOCK_t; +// UBX-NAV-TIMELS (0x01 0x26): Leap second event information +const uint16_t UBX_NAV_TIMELS_LEN = 24; + +typedef struct +{ + uint32_t iTOW; // GPS time of week of the navigation epoch: ms + uint8_t version; // Message version (0x00 for this version) + uint8_t reserved1[3]; + uint8_t srcOfCurrLs; //Information source for the current number of leap seconds + int8_t currLs; //Current number of leap seconds since start of GPS (Jan 6, 1980), s + uint8_t srcOfLsChange; //Information source for the future leap second event + int8_t lsChange; //Future leap second change if one is scheduled, +1, 0, -1s + int32_t timeToLsEvent; //Num of secs until the next or from the last leap second, s + uint16_t dateOfLsGpsWn; //GPS week num (WN) of the next or the last leap second event + uint16_t dateOfLsGpsDn; //GPS day of week num (DN) for the next or last leap second event + uint8_t reserved2[3]; + int8_t valid; // Validity flag, 1 = valid, otherwise 0 +} UBX_NAV_TIMELS_data_t; + +typedef struct +{ + union + { + uint32_t all; + struct + { + uint32_t all : 1; + + uint32_t iTOW : 1; + uint32_t version : 1; + uint32_t srcOfCurrLs : 1; + uint32_t currLs : 1; + uint32_t srcOfLsChange : 1; + uint32_t lsChange : 1; + uint32_t timeToLsEvent : 1; + uint32_t dateOfLsGpsWn : 1; + uint32_t dateOfLsGpsDn : 1; + uint32_t valid : 1; + } bits; + } moduleQueried; +} UBX_NAV_TIMELS_moduleQueried_t; + +typedef struct +{ + ubxAutomaticFlags automaticFlags; + UBX_NAV_TIMELS_data_t data; + UBX_NAV_TIMELS_moduleQueried_t moduleQueried; + void (*callbackPointer)(UBX_NAV_TIMELS_data_t); + UBX_NAV_TIMELS_data_t *callbackData; +} UBX_NAV_TIMELS_t; + // UBX-NAV-SVIN (0x01 0x3B): Survey-in data const uint16_t UBX_NAV_SVIN_LEN = 40; From 103d7b5412aaea46ba34dd4c936934b816f0bcde Mon Sep 17 00:00:00 2001 From: UT2UH Date: Wed, 14 Apr 2021 21:44:36 +0300 Subject: [PATCH 09/11] TIMELS RAM deleted on 'end' --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index c9f2cb8..1d2bc91 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -85,6 +85,12 @@ void SFE_UBLOX_GNSS::end(void) delete[] currentGeofenceParams; currentGeofenceParams = NULL; // Redundant? } + + if (packetUBXNAVTIMELS != NULL) + { + delete[] packetUBXNAVTIMELS; + packetUBXNAVTIMELS = NULL; // Redundant? + } if (packetUBXNAVPOSECEF != NULL) { From 5c7298c37de8d006ead9e1772e0e2d7ea6814001 Mon Sep 17 00:00:00 2001 From: UT2UH Date: Thu, 15 Apr 2021 12:28:54 +0300 Subject: [PATCH 10/11] UBX-NAV-TIMELS validity bits fixes --- src/SparkFun_u-blox_GNSS_Arduino_Library.cpp | 12 ++++++------ src/u-blox_structs.h | 13 +++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index 1d2bc91..fcd8cfb 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -2086,7 +2086,7 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) packetUBXNAVTIMELS->data.timeToLsEvent = extractSignedLong(msg, 12); packetUBXNAVTIMELS->data.dateOfLsGpsWn = extractInt(msg, 16); packetUBXNAVTIMELS->data.dateOfLsGpsDn = extractInt(msg, 18); - packetUBXNAVTIMELS->data.valid = extractSignedChar(msg, 23); + packetUBXNAVTIMELS->data.valid.all = extractSignedChar(msg, 23); //Mark all datums as fresh (not read before) packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; @@ -10391,9 +10391,9 @@ uint8_t SFE_UBLOX_GNSS::getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWai if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed return 3; - if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid == false) + if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent == false) getLeapSecondEvent(maxWait); - packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid = false; //Since we are about to give this to user, mark this data as stale + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent = false; //Since we are about to give this to user, mark this data as stale packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.lsChange = false; packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.timeToLsEvent = false; packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false; @@ -10403,7 +10403,7 @@ uint8_t SFE_UBLOX_GNSS::getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWai // 1 -last minute of the day has 61 seconds // 2 -last minute of the day has 59 seconds // 3 -unknown (clock unsynchronized) - return ((boolean)packetUBXNAVTIMELS->data.valid ? (uint8_t)(packetUBXNAVTIMELS->data.lsChange == -1 ? 2 : packetUBXNAVTIMELS->data.lsChange) : 3); + return ((boolean)packetUBXNAVTIMELS->data.valid.bits.validTimeToLsEvent ? (uint8_t)(packetUBXNAVTIMELS->data.lsChange == -1 ? 2 : packetUBXNAVTIMELS->data.lsChange) : 3); } int8_t SFE_UBLOX_GNSS::getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait) @@ -10412,9 +10412,9 @@ int8_t SFE_UBLOX_GNSS::getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_ if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed return false; - if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid == false) + if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs == false) getLeapSecondEvent(maxWait); - packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.valid = false; //Since we are about to give this to user, mark this data as stale + packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs = false; //Since we are about to give this to user, mark this data as stale packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.srcOfCurrLs = false; packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.currLs = false; packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false; diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 574baa2..a8a8300 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -870,7 +870,15 @@ typedef struct uint16_t dateOfLsGpsWn; //GPS week num (WN) of the next or the last leap second event uint16_t dateOfLsGpsDn; //GPS day of week num (DN) for the next or last leap second event uint8_t reserved2[3]; - int8_t valid; // Validity flag, 1 = valid, otherwise 0 + union + { + uint8_t all; + struct + { + uint8_t validCurrLs : 1; // 1 = Valid current number of leap seconds value + uint8_t validTimeToLsEvent : 1; // 1 = Valid time to next leap second event or from the last leap second event if no future event scheduled + } bits; + } valid; } UBX_NAV_TIMELS_data_t; typedef struct @@ -891,7 +899,8 @@ typedef struct uint32_t timeToLsEvent : 1; uint32_t dateOfLsGpsWn : 1; uint32_t dateOfLsGpsDn : 1; - uint32_t valid : 1; + uint32_t validCurrLs : 1; + uint32_t validTimeToLsEvent : 1; } bits; } moduleQueried; } UBX_NAV_TIMELS_moduleQueried_t; From 36fe93e4e2db465d1c20cd4498b875300cbdf366 Mon Sep 17 00:00:00 2001 From: PaulZC Date: Mon, 26 Apr 2021 10:27:04 +0100 Subject: [PATCH 11/11] v2.0.6 --- library.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library.properties b/library.properties index 1e27608..1baf2ae 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS Arduino Library -version=2.0.5 +version=2.0.6 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C and Serial Communication with u-blox GNSS modules