/* This is a library written for the u-blox ZED-F9P and NEO-M8P-2 SparkFun sells these at its website: www.sparkfun.com Do you like this library? Help support SparkFun. Buy a board! https://www.sparkfun.com/products/16481 https://www.sparkfun.com/products/15136 https://www.sparkfun.com/products/15005 https://www.sparkfun.com/products/15733 https://www.sparkfun.com/products/15193 https://www.sparkfun.com/products/15210 Original version by Nathan Seidle @ SparkFun Electronics, September 6th, 2018 v2.0 rework by Paul Clark @ SparkFun Electronics, December 31st, 2020 This library handles configuring and handling the responses from a u-blox GPS module. Works with most modules from u-blox including the Zed-F9P, NEO-M8P-2, NEO-M9N, ZOE-M8Q, SAM-M8Q, and many others. https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library Development environment specifics: Arduino IDE 1.8.13 SparkFun code, firmware, and software is released under the MIT License(http://opensource.org/licenses/MIT). The MIT License (MIT) Copyright (c) 2016 SparkFun Electronics Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "SparkFun_u-blox_GNSS_Arduino_Library.h" SFE_UBLOX_GNSS::SFE_UBLOX_GNSS(void) { // Constructor if (debugPin >= 0) { pinMode((uint8_t)debugPin, OUTPUT); digitalWrite((uint8_t)debugPin, HIGH); } _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 processNMEA // Support for platforms like ESP32 which do not support multiple I2C restarts // If _i2cStopRestart is true, endTransmission will always use a stop. If false, a restart will be used where needed. #if defined(ARDUINO_ARCH_ESP32) _i2cStopRestart = true; // Always use a stop #else _i2cStopRestart = false; // Use a restart where needed #endif } SFE_UBLOX_GNSS::~SFE_UBLOX_GNSS(void) { // Destructor end(); // Delete all allocated memory - excluding payloadCfg, payloadAuto and spiBuffer if (payloadCfg != NULL) { delete[] payloadCfg; // Created with new[] payloadCfg = NULL; // Redundant? } if (payloadAuto != NULL) { delete[] payloadAuto; // Created with new[] payloadAuto = NULL; // Redundant? } if (spiBuffer != NULL) { delete[] spiBuffer; // Created with new[] spiBuffer = NULL; // Redundant? } } // Stop all automatic message processing. Free all used RAM void SFE_UBLOX_GNSS::end(void) { // Note: payloadCfg is not deleted // Note: payloadAuto is not deleted // Note: spiBuffer is not deleted if (ubxFileBuffer != NULL) // Check if RAM has been allocated for the file buffer { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("end: the file buffer has been deleted. You will need to call setFileBufferSize before .begin to create a new one.")); } #endif delete[] ubxFileBuffer; // Created with new[] ubxFileBuffer = NULL; // Redundant? fileBufferSize = 0; // Reset file buffer size. User will have to call setFileBufferSize again fileBufferMaxAvail = 0; } if (moduleSWVersion != NULL) { delete moduleSWVersion; // Created with new moduleSWVersion_t moduleSWVersion = NULL; // Redundant? } if (currentGeofenceParams != NULL) { delete currentGeofenceParams; // Created with new geofenceParams_t currentGeofenceParams = NULL; // Redundant? } if (packetUBXNAVTIMELS != NULL) { delete packetUBXNAVTIMELS; // Created with new UBX_NAV_TIMELS_t packetUBXNAVTIMELS = NULL; // Redundant? } if (packetUBXNAVPOSECEF != NULL) { if (packetUBXNAVPOSECEF->callbackData != NULL) { delete packetUBXNAVPOSECEF->callbackData; // Created with new UBX_NAV_POSECEF_data_t } delete packetUBXNAVPOSECEF; // Created with new UBX_NAV_POSECEF_t packetUBXNAVPOSECEF = NULL; // Redundant? } if (packetUBXNAVSTATUS != NULL) { if (packetUBXNAVSTATUS->callbackData != NULL) { delete packetUBXNAVSTATUS->callbackData; } delete packetUBXNAVSTATUS; packetUBXNAVSTATUS = NULL; // Redundant? } if (packetUBXNAVDOP != NULL) { if (packetUBXNAVDOP->callbackData != NULL) { delete packetUBXNAVDOP->callbackData; } delete packetUBXNAVDOP; packetUBXNAVDOP = NULL; // Redundant? } if (packetUBXNAVATT != NULL) { if (packetUBXNAVATT->callbackData != NULL) { delete packetUBXNAVATT->callbackData; } delete packetUBXNAVATT; packetUBXNAVATT = NULL; // Redundant? } if (packetUBXNAVPVT != NULL) { if (packetUBXNAVPVT->callbackData != NULL) { delete packetUBXNAVPVT->callbackData; #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("end: packetUBXNAVPVT->callbackData has been deleted")); } #endif } delete packetUBXNAVPVT; packetUBXNAVPVT = NULL; // Redundant? #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("end: packetUBXNAVPVT has been deleted")); } #endif } if (packetUBXNAVODO != NULL) { if (packetUBXNAVODO->callbackData != NULL) { delete packetUBXNAVODO->callbackData; } delete packetUBXNAVODO; packetUBXNAVODO = NULL; // Redundant? } if (packetUBXNAVVELECEF != NULL) { if (packetUBXNAVVELECEF->callbackData != NULL) { delete packetUBXNAVVELECEF->callbackData; } delete packetUBXNAVVELECEF; packetUBXNAVVELECEF = NULL; // Redundant? } if (packetUBXNAVVELNED != NULL) { if (packetUBXNAVVELNED->callbackData != NULL) { delete packetUBXNAVVELNED->callbackData; } delete packetUBXNAVVELNED; packetUBXNAVVELNED = NULL; // Redundant? } if (packetUBXNAVHPPOSECEF != NULL) { if (packetUBXNAVHPPOSECEF->callbackData != NULL) { delete packetUBXNAVHPPOSECEF->callbackData; } delete packetUBXNAVHPPOSECEF; packetUBXNAVHPPOSECEF = NULL; // Redundant? } if (packetUBXNAVHPPOSLLH != NULL) { if (packetUBXNAVHPPOSLLH->callbackData != NULL) { delete packetUBXNAVHPPOSLLH->callbackData; } delete packetUBXNAVHPPOSLLH; packetUBXNAVHPPOSLLH = NULL; // Redundant? } if (packetUBXNAVPVAT != NULL) { if (packetUBXNAVPVAT->callbackData != NULL) { delete packetUBXNAVPVAT->callbackData; } delete packetUBXNAVPVAT; packetUBXNAVPVAT = NULL; // Redundant? } if (packetUBXNAVTIMEUTC != NULL) { if (packetUBXNAVTIMEUTC->callbackData != NULL) { delete packetUBXNAVTIMEUTC->callbackData; } delete packetUBXNAVTIMEUTC; packetUBXNAVTIMEUTC = NULL; // Redundant? } if (packetUBXNAVCLOCK != NULL) { if (packetUBXNAVCLOCK->callbackData != NULL) { delete packetUBXNAVCLOCK->callbackData; } delete packetUBXNAVCLOCK; packetUBXNAVCLOCK = NULL; // Redundant? } if (packetUBXNAVSVIN != NULL) { if (packetUBXNAVSVIN->callbackData != NULL) { delete packetUBXNAVSVIN->callbackData; } delete packetUBXNAVSVIN; packetUBXNAVSVIN = NULL; // Redundant? } if (packetUBXNAVSAT != NULL) { if (packetUBXNAVSAT->callbackData != NULL) { delete packetUBXNAVSAT->callbackData; } delete packetUBXNAVSAT; packetUBXNAVSAT = NULL; // Redundant? } if (packetUBXNAVRELPOSNED != NULL) { if (packetUBXNAVRELPOSNED->callbackData != NULL) { delete packetUBXNAVRELPOSNED->callbackData; } delete packetUBXNAVRELPOSNED; packetUBXNAVRELPOSNED = NULL; // Redundant? } if (packetUBXNAVAOPSTATUS != NULL) { if (packetUBXNAVAOPSTATUS->callbackData != NULL) { delete packetUBXNAVAOPSTATUS->callbackData; } delete packetUBXNAVAOPSTATUS; packetUBXNAVAOPSTATUS = NULL; // Redundant? } if (packetUBXNAVEOE != NULL) { if (packetUBXNAVEOE->callbackData != NULL) { delete packetUBXNAVEOE->callbackData; } delete packetUBXNAVEOE; packetUBXNAVEOE = NULL; // Redundant? } if (packetUBXRXMPMP != NULL) { if (packetUBXRXMPMP->callbackData != NULL) { delete packetUBXRXMPMP->callbackData; } delete packetUBXRXMPMP; packetUBXRXMPMP = NULL; // Redundant? } if (packetUBXRXMPMPmessage != NULL) { if (packetUBXRXMPMPmessage->callbackData != NULL) { delete packetUBXRXMPMPmessage->callbackData; } delete packetUBXRXMPMPmessage; packetUBXRXMPMPmessage = NULL; // Redundant? } if (packetUBXRXMQZSSL6message != NULL) { if (packetUBXRXMQZSSL6message->callbackData != NULL) { delete[] packetUBXRXMQZSSL6message->callbackData; } delete packetUBXRXMQZSSL6message; packetUBXRXMQZSSL6message = NULL; // Redundant? } if (packetUBXRXMCOR != NULL) { if (packetUBXRXMCOR->callbackData != NULL) { delete packetUBXRXMCOR->callbackData; } delete packetUBXRXMCOR; packetUBXRXMCOR = NULL; // Redundant? } if (packetUBXRXMSFRBX != NULL) { if (packetUBXRXMSFRBX->callbackData != NULL) { delete packetUBXRXMSFRBX->callbackData; } delete packetUBXRXMSFRBX; packetUBXRXMSFRBX = NULL; // Redundant? } if (packetUBXRXMRAWX != NULL) { if (packetUBXRXMRAWX->callbackData != NULL) { delete packetUBXRXMRAWX->callbackData; } delete packetUBXRXMRAWX; packetUBXRXMRAWX = NULL; // Redundant? } if (packetUBXCFGRATE != NULL) { delete packetUBXCFGRATE; packetUBXCFGRATE = NULL; // Redundant? } if (packetUBXTIMTM2 != NULL) { if (packetUBXTIMTM2->callbackData != NULL) { delete packetUBXTIMTM2->callbackData; } delete packetUBXTIMTM2; packetUBXTIMTM2 = NULL; // Redundant? } if (packetUBXESFALG != NULL) { if (packetUBXESFALG->callbackData != NULL) { delete packetUBXESFALG->callbackData; } delete packetUBXESFALG; packetUBXESFALG = NULL; // Redundant? } if (packetUBXESFSTATUS != NULL) { if (packetUBXESFSTATUS->callbackData != NULL) { delete packetUBXESFSTATUS->callbackData; } delete packetUBXESFSTATUS; packetUBXESFSTATUS = NULL; // Redundant? } if (packetUBXESFINS != NULL) { if (packetUBXESFINS->callbackData != NULL) { delete packetUBXESFINS->callbackData; } delete packetUBXESFINS; packetUBXESFINS = NULL; // Redundant? } if (packetUBXESFMEAS != NULL) { if (packetUBXESFMEAS->callbackData != NULL) { delete packetUBXESFMEAS->callbackData; } delete packetUBXESFMEAS; packetUBXESFMEAS = NULL; // Redundant? } if (packetUBXESFRAW != NULL) { if (packetUBXESFRAW->callbackData != NULL) { delete packetUBXESFRAW->callbackData; } delete packetUBXESFRAW; packetUBXESFRAW = NULL; // Redundant? } if (packetUBXMGAACK != NULL) { delete packetUBXMGAACK; packetUBXMGAACK = NULL; // Redundant? } if (packetUBXMGADBD != NULL) { delete packetUBXMGADBD; packetUBXMGADBD = NULL; // Redundant? } if (packetUBXHNRATT != NULL) { if (packetUBXHNRATT->callbackData != NULL) { delete packetUBXHNRATT->callbackData; } delete packetUBXHNRATT; packetUBXHNRATT = NULL; // Redundant? } if (packetUBXHNRINS != NULL) { if (packetUBXHNRINS->callbackData != NULL) { delete packetUBXHNRINS->callbackData; } delete packetUBXHNRINS; packetUBXHNRINS = NULL; // Redundant? } if (packetUBXHNRPVT != NULL) { if (packetUBXHNRPVT->callbackData != NULL) { delete packetUBXHNRPVT->callbackData; } delete packetUBXHNRPVT; packetUBXHNRPVT = NULL; // Redundant? } #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if (storageNMEAGPGGA != NULL) { if (storageNMEAGPGGA->callbackCopy != NULL) { delete storageNMEAGPGGA->callbackCopy; } delete storageNMEAGPGGA; storageNMEAGPGGA = NULL; // Redundant? } if (storageNMEAGNGGA != NULL) { if (storageNMEAGNGGA->callbackCopy != NULL) { delete storageNMEAGNGGA->callbackCopy; } delete storageNMEAGNGGA; storageNMEAGNGGA = NULL; // Redundant? } if (storageNMEAGPVTG != NULL) { if (storageNMEAGPVTG->callbackCopy != NULL) { delete storageNMEAGPVTG->callbackCopy; } delete storageNMEAGPVTG; storageNMEAGPVTG = NULL; // Redundant? } if (storageNMEAGNVTG != NULL) { if (storageNMEAGNVTG->callbackCopy != NULL) { delete storageNMEAGNVTG->callbackCopy; } delete storageNMEAGNVTG; storageNMEAGNVTG = NULL; // Redundant? } if (storageNMEAGPRMC != NULL) { if (storageNMEAGPRMC->callbackCopy != NULL) { delete storageNMEAGPRMC->callbackCopy; } delete storageNMEAGPRMC; storageNMEAGPRMC = NULL; // Redundant? } if (storageNMEAGNRMC != NULL) { if (storageNMEAGNRMC->callbackCopy != NULL) { delete storageNMEAGNRMC->callbackCopy; } delete storageNMEAGNRMC; storageNMEAGNRMC = NULL; // Redundant? } if (storageNMEAGPZDA != NULL) { if (storageNMEAGPZDA->callbackCopy != NULL) { delete storageNMEAGPZDA->callbackCopy; } delete storageNMEAGPZDA; storageNMEAGPZDA = NULL; // Redundant? } if (storageNMEAGNZDA != NULL) { if (storageNMEAGNZDA->callbackCopy != NULL) { delete storageNMEAGNZDA->callbackCopy; } delete storageNMEAGNZDA; storageNMEAGNZDA = NULL; // Redundant? } #endif } // Allow the user to change packetCfgPayloadSize. Handy if you want to process big messages like RAWX // This can be called before .begin if required / desired bool SFE_UBLOX_GNSS::setPacketCfgPayloadSize(size_t payloadSize) { bool success = true; if ((payloadSize == 0) && (payloadCfg != NULL)) { // Zero payloadSize? Dangerous! But we'll free the memory anyway... delete[] payloadCfg; // Created with new[] payloadCfg = NULL; // Redundant? packetCfg.payload = payloadCfg; packetCfgPayloadSize = payloadSize; if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setPacketCfgPayloadSize: Zero payloadSize!")); } else if (payloadCfg == NULL) // Memory has not yet been allocated - so use new { payloadCfg = new uint8_t[payloadSize]; packetCfg.payload = payloadCfg; if (payloadCfg == NULL) { success = false; packetCfgPayloadSize = 0; if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setPacketCfgPayloadSize: RAM alloc failed!")); } else packetCfgPayloadSize = payloadSize; } else // Memory has already been allocated - so resize { uint8_t *newPayload = new uint8_t[payloadSize]; if (newPayload == NULL) // Check if the alloc was successful { success = false; // Report failure. Don't change payloadCfg, packetCfg.payload or packetCfgPayloadSize if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setPacketCfgPayloadSize: RAM resize failed!")); } else { memcpy(newPayload, payloadCfg, payloadSize <= packetCfgPayloadSize ? payloadSize : packetCfgPayloadSize); // Copy as much existing data as we can delete[] payloadCfg; // Free payloadCfg. Created with new[] payloadCfg = newPayload; // Point to the newPayload packetCfg.payload = payloadCfg; // Update the packet pointer packetCfgPayloadSize = payloadSize; // Update the packet payload size } } return (success); } // Return the number of free bytes remaining in packetCfgPayload size_t SFE_UBLOX_GNSS::getPacketCfgSpaceRemaining() { return (packetCfgPayloadSize - packetCfg.len); } // Initialize the I2C port bool SFE_UBLOX_GNSS::begin(TwoWire &wirePort, uint8_t deviceAddress, uint16_t maxWait, bool assumeSuccess) { commType = COMM_TYPE_I2C; _i2cPort = &wirePort; // Grab which port the user wants us to use _signsOfLife = false; // Clear the _signsOfLife flag. It will be set true if valid traffic is seen. // We expect caller to begin their I2C port, with the speed of their choice external to the library // But if they forget, we start the hardware here. // We're moving away from the practice of starting Wire hardware in a library. This is to avoid cross platform issues. // ie, there are some platforms that don't handle multiple starts to the wire hardware. Also, every time you start the wire // hardware the clock speed reverts back to 100kHz regardless of previous Wire.setClocks(). //_i2cPort->begin(); _gpsI2Caddress = deviceAddress; // Store the I2C address from user // New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already) if (packetCfgPayloadSize == 0) setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE); // New in v2.0: allocate memory for the file buffer - if required. (The user should have called setFileBufferSize already) createFileBuffer(); // Call isConnected up to three times - tests on the NEO-M8U show the CFG RATE poll occasionally being ignored bool connected = isConnected(maxWait); if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - second attempt")); } #endif connected = isConnected(maxWait); } if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - third attempt")); } #endif connected = isConnected(maxWait); } if ((!connected) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate. { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: third attempt failed. Assuming success...")); } #endif return (true); } return (connected); } // Initialize the Serial port bool SFE_UBLOX_GNSS::begin(Stream &serialPort, uint16_t maxWait, bool assumeSuccess) { commType = COMM_TYPE_SERIAL; _serialPort = &serialPort; // Grab which port the user wants us to use _signsOfLife = false; // Clear the _signsOfLife flag. It will be set true if valid traffic is seen. // New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already) if (packetCfgPayloadSize == 0) setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE); // New in v2.0: allocate memory for the file buffer - if required. (The user should have called setFileBufferSize already) createFileBuffer(); // Get rid of any stale serial data already in the processor's RX buffer while (_serialPort->available()) _serialPort->read(); // If assumeSuccess is true, the user must really want begin to succeed. So, let's empty the module's serial transmit buffer too! // Keep discarding new serial data until we see a gap of 2ms - hopefully indicating that the module's TX buffer is empty. if (assumeSuccess) { unsigned long startTime = millis(); unsigned long lastActivity = startTime; bool keepGoing = true; while (keepGoing && (millis() < (startTime + (unsigned long)maxWait))) { while (_serialPort->available()) // Discard any new data { _serialPort->read(); lastActivity = millis(); } if (millis() > (lastActivity + (unsigned long)2)) // Check if we have seen no new data for at least 2ms keepGoing = false; } } // Call isConnected up to three times - tests on the NEO-M8U show the CFG RATE poll occasionally being ignored bool connected = isConnected(maxWait); if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - second attempt")); } #endif connected = isConnected(maxWait); } if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - third attempt")); } #endif connected = isConnected(maxWait); } if ((!connected) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate. { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: third attempt failed. Assuming success...")); } #endif return (true); } return (connected); } // Initialize for SPI bool SFE_UBLOX_GNSS::begin(SPIClass &spiPort, uint8_t csPin, uint32_t spiSpeed, uint16_t maxWait, bool assumeSuccess) { commType = COMM_TYPE_SPI; _spiPort = &spiPort; _csPin = csPin; _spiSpeed = spiSpeed; _signsOfLife = false; // Clear the _signsOfLife flag. It will be set true if valid traffic is seen. // Initialize the chip select pin pinMode(_csPin, OUTPUT); digitalWrite(_csPin, HIGH); // New in v2.0: allocate memory for the packetCfg payload here - if required. (The user may have called setPacketCfgPayloadSize already) if (packetCfgPayloadSize == 0) setPacketCfgPayloadSize(MAX_PAYLOAD_SIZE); createFileBuffer(); // Create the SPI buffer if (spiBuffer == NULL) // Memory has not yet been allocated - so use new { spiBuffer = new uint8_t[getSpiTransactionSize()]; } if (spiBuffer == NULL) { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("begin (SPI): memory allocation failed for SPI Buffer!")); return (false); } } else { // Initialize/clear the SPI buffer - fill it with 0xFF as this is what is received from the UBLOX module if there's no data to be processed for (uint8_t i = 0; i < getSpiTransactionSize(); i++) { spiBuffer[i] = 0xFF; } } // Call isConnected up to three times - tests on the NEO-M8U show the CFG RATE poll occasionally being ignored bool connected = isConnected(maxWait); if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - second attempt")); } #endif connected = isConnected(maxWait); } if (!connected) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: isConnected - third attempt")); } #endif connected = isConnected(maxWait); } if ((!connected) && assumeSuccess && _signsOfLife) // Advanced users can assume success if required. Useful if the port is outputting messages at high navigation rate. { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("begin: third attempt failed. Assuming success...")); } #endif return (true); } return (connected); } // Allow the user to change I2C polling wait (the minimum interval between I2C data requests - to avoid pounding the bus) // i2cPollingWait defaults to 100ms and is adjusted automatically when setNavigationFrequency() // or setHNRNavigationRate() are called. But if the user is using callbacks, it might be advantageous // to be able to set the polling wait manually. void SFE_UBLOX_GNSS::setI2CpollingWait(uint8_t newPollingWait_ms) { i2cPollingWait = newPollingWait_ms; } // Allow the user to change SPI polling wait // (the minimum interval between SPI data requests when no data is available - to avoid pounding the bus) void SFE_UBLOX_GNSS::setSPIpollingWait(uint8_t newPollingWait_ms) { spiPollingWait = newPollingWait_ms; } // Sets the global size for I2C transactions // Most platforms use 32 bytes (the default) but this allows users to increase the transaction // size if the platform supports it // Note: If the transaction size is set larger than the platforms buffer size, bad things will happen. void SFE_UBLOX_GNSS::setI2CTransactionSize(uint8_t transactionSize) { if (transactionSize < 8) transactionSize = 8; // Ensure transactionSize is at least 8 bytes otherwise sendI2cCommand will have problems! i2cTransactionSize = transactionSize; } uint8_t SFE_UBLOX_GNSS::getI2CTransactionSize(void) { return (i2cTransactionSize); } // Sets the global size for the SPI buffer/transactions. // Call this **before** begin()! // Note: if the buffer size is too small, incoming characters may be lost if the message sent // is larger than this buffer. If too big, you may run out of SRAM on constrained architectures! void SFE_UBLOX_GNSS::setSpiTransactionSize(uint8_t transactionSize) { if (spiBuffer == NULL) { spiTransactionSize = transactionSize; } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("setSpiTransactionSize: you need to call setSpiTransactionSize _before_ begin!")); } #endif } } uint8_t SFE_UBLOX_GNSS::getSpiTransactionSize(void) { return (spiTransactionSize); } // Sets the size of maxNMEAByteCount void SFE_UBLOX_GNSS::setMaxNMEAByteCount(int8_t newMax) { maxNMEAByteCount = newMax; } int8_t SFE_UBLOX_GNSS::getMaxNMEAByteCount(void) { return (maxNMEAByteCount); } // Returns true if I2C device ack's bool SFE_UBLOX_GNSS::isConnected(uint16_t maxWait) { if (commType == COMM_TYPE_I2C) { _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); if (_i2cPort->endTransmission() != 0) return false; // Sensor did not ack } // Query port configuration to see whether we get a meaningful response // We could simply request the config for any port but, just for giggles, let's request the config for most appropriate port if (commType == COMM_TYPE_I2C) return (getPortSettingsInternal(COM_PORT_I2C, maxWait)); else if (commType == COMM_TYPE_SERIAL) return (getPortSettingsInternal(COM_PORT_UART1, maxWait)); // Could be UART2 - but this is just a response check else // if (commType == COMM_TYPE_SPI) return (getPortSettingsInternal(COM_PORT_SPI, maxWait)); } // Enable or disable the printing of sent/response HEX values. // Use this in conjunction with 'Transport Logging' from the Universal Reader Assistant to see what they're doing that we're not void SFE_UBLOX_GNSS::enableDebugging(Stream &debugPort, bool printLimitedDebug) { _debugSerial = &debugPort; // Grab which port the user wants us to use for debugging if (printLimitedDebug == false) { _printDebug = true; // Should we print the commands we send? Good for debugging } else { _printLimitedDebug = true; // Should we print limited debug messages? Good for debugging high navigation rates } } void SFE_UBLOX_GNSS::disableDebugging(void) { _printDebug = false; // Turn off extra print statements _printLimitedDebug = false; } // Safely print messages void SFE_UBLOX_GNSS::debugPrint(char *message) { if (_printDebug == true) { _debugSerial->print(message); } } // Safely print messages void SFE_UBLOX_GNSS::debugPrintln(char *message) { if (_printDebug == true) { _debugSerial->println(message); } } const char *SFE_UBLOX_GNSS::statusString(sfe_ublox_status_e stat) { switch (stat) { case SFE_UBLOX_STATUS_SUCCESS: return "Success"; break; case SFE_UBLOX_STATUS_FAIL: return "General Failure"; break; case SFE_UBLOX_STATUS_CRC_FAIL: return "CRC Fail"; break; case SFE_UBLOX_STATUS_TIMEOUT: return "Timeout"; break; case SFE_UBLOX_STATUS_COMMAND_NACK: return "Command not acknowledged (NACK)"; break; case SFE_UBLOX_STATUS_OUT_OF_RANGE: return "Out of range"; break; case SFE_UBLOX_STATUS_INVALID_ARG: return "Invalid Arg"; break; case SFE_UBLOX_STATUS_INVALID_OPERATION: return "Invalid operation"; break; case SFE_UBLOX_STATUS_MEM_ERR: return "Memory Error"; break; case SFE_UBLOX_STATUS_HW_ERR: return "Hardware Error"; break; case SFE_UBLOX_STATUS_DATA_SENT: return "Data Sent"; break; case SFE_UBLOX_STATUS_DATA_RECEIVED: return "Data Received"; break; case SFE_UBLOX_STATUS_I2C_COMM_FAILURE: return "I2C Comm Failure"; break; case SFE_UBLOX_STATUS_DATA_OVERWRITTEN: return "Data Packet Overwritten"; break; default: return "Unknown Status"; break; } return "None"; } // Check for the arrival of new I2C/Serial/SPI data // Allow the user to disable the "7F" check (e.g.) when logging RAWX data void SFE_UBLOX_GNSS::disableUBX7Fcheck(bool disabled) { ubx7FcheckDisabled = disabled; } // Called regularly to check for available bytes on the user' specified port bool SFE_UBLOX_GNSS::checkUblox(uint8_t requestedClass, uint8_t requestedID) { return checkUbloxInternal(&packetCfg, requestedClass, requestedID); } // PRIVATE: Called regularly to check for available bytes on the user' specified port bool SFE_UBLOX_GNSS::checkUbloxInternal(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { if (commType == COMM_TYPE_I2C) return (checkUbloxI2C(incomingUBX, requestedClass, requestedID)); else if (commType == COMM_TYPE_SERIAL) return (checkUbloxSerial(incomingUBX, requestedClass, requestedID)); else if (commType == COMM_TYPE_SPI) return (checkUbloxSpi(incomingUBX, requestedClass, requestedID)); return false; } // Polls I2C for data, passing any new bytes to process() // Returns true if new bytes are available bool SFE_UBLOX_GNSS::checkUbloxI2C(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { if (millis() - lastCheck >= i2cPollingWait) { // Get the number of bytes available from the module uint16_t bytesAvailable = 0; _i2cPort->beginTransmission(_gpsI2Caddress); _i2cPort->write(0xFD); // 0xFD (MSB) and 0xFE (LSB) are the registers that contain number of bytes available uint8_t i2cError = _i2cPort->endTransmission(false); // Always send a restart command. Do not release the bus. ESP32 supports this. if (i2cError != 0) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("checkUbloxI2C: I2C error: endTransmission returned ")); _debugSerial->println(i2cError); } #endif return (false); // Sensor did not ACK } // Forcing requestFrom to use a restart would be unwise. If bytesAvailable is zero, we want to surrender the bus. uint8_t bytesReturned = _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, static_cast<uint8_t>(2)); if (bytesReturned != 2) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("checkUbloxI2C: I2C error: requestFrom 0xFD returned ")); _debugSerial->println(bytesReturned); } #endif return (false); // Sensor did not return 2 bytes } else // if (_i2cPort->available()) { uint8_t msb = _i2cPort->read(); uint8_t lsb = _i2cPort->read(); // if (lsb == 0xFF) // { // //I believe this is a u-blox bug. Device should never present an 0xFF. // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging // { // _debugSerial->print(F("checkUbloxI2C: u-blox bug? Length lsb is 0xFF. i2cPollingWait is ")); // _debugSerial->println(i2cPollingWait); // } // if (debugPin >= 0) // { // digitalWrite((uint8_t)debugPin, LOW); // delay(10); // digitalWrite((uint8_t)debugPin, HIGH); // } // lastCheck = millis(); //Put off checking to avoid I2C bus traffic // return (false); // } // if (msb == 0xFF) // { // //I believe this is a u-blox bug. Device should never present an 0xFF. // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging // { // _debugSerial->print(F("checkUbloxI2C: u-blox bug? Length msb is 0xFF. i2cPollingWait is ")); // _debugSerial->println(i2cPollingWait); // } // if (debugPin >= 0) // { // digitalWrite((uint8_t)debugPin, LOW); // delay(10); // digitalWrite((uint8_t)debugPin, HIGH); // } // lastCheck = millis(); //Put off checking to avoid I2C bus traffic // return (false); // } bytesAvailable = (uint16_t)msb << 8 | lsb; } if (bytesAvailable == 0) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("checkUbloxI2C: OK, zero bytes available")); } #endif lastCheck = millis(); // Put off checking to avoid I2C bus traffic return (false); } // Check for undocumented bit error. We found this doing logic scans. // This error is rare but if we incorrectly interpret the first bit of the two 'data available' bytes as 1 // then we have far too many bytes to check. May be related to I2C setup time violations: https://github.com/sparkfun/SparkFun_Ublox_Arduino_Library/issues/40 if (bytesAvailable & ((uint16_t)1 << 15)) { // Clear the MSbit bytesAvailable &= ~((uint16_t)1 << 15); // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging // { // _debugSerial->print(F("checkUbloxI2C: Bytes available error: ")); // _debugSerial->println(bytesAvailable); // if (debugPin >= 0) // { // digitalWrite((uint8_t)debugPin, LOW); // delay(10); // digitalWrite((uint8_t)debugPin, HIGH); // } // } } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (bytesAvailable > 100) { if (_printDebug == true) { _debugSerial->print(F("checkUbloxI2C: Large packet of ")); _debugSerial->print(bytesAvailable); _debugSerial->println(F(" bytes received")); } } else { if (_printDebug == true) { _debugSerial->print(F("checkUbloxI2C: Reading ")); _debugSerial->print(bytesAvailable); _debugSerial->println(F(" bytes")); } } #endif while (bytesAvailable) { // From the u-blox integration manual: // "There are two forms of DDC read transfer. The "random access" form includes a peripheral register // address and thus allows any register to be read. The second "current address" form omits the // register address. If this second form is used, then an address pointer in the receiver is used to // determine which register to read. This address pointer will increment after each read unless it // is already pointing at register 0xFF, the highest addressable register, in which case it remains // unaltered." // This means that after reading bytesAvailable from 0xFD and 0xFE, the address pointer will already be // pointing at 0xFF, so we do not need to write it here. The next four lines can be commented. //_i2cPort->beginTransmission(_gpsI2Caddress); //_i2cPort->write(0xFF); //0xFF is the register to read data from // if (_i2cPort->endTransmission(false) != 0) //Send a restart command. Do not release bus. // return (false); //Sensor did not ACK // Limit to 32 bytes or whatever the buffer limit is for given platform uint16_t bytesToRead = bytesAvailable; // 16-bit if (bytesToRead > i2cTransactionSize) // Limit for i2cTransactionSize is 8-bit bytesToRead = i2cTransactionSize; // TRY_AGAIN: // Here it would be desireable to use a restart where possible / supported, but only if there will be multiple reads. // However, if an individual requestFrom fails, we could end up leaving the bus hanging. // On balance, it is probably safest to not use restarts here. uint8_t bytesReturned = _i2cPort->requestFrom((uint8_t)_gpsI2Caddress, (uint8_t)bytesToRead); if ((uint16_t)bytesReturned == bytesToRead) { for (uint16_t x = 0; x < bytesToRead; x++) { uint8_t incoming = _i2cPort->read(); // Grab the actual character // Check to see if the first read is 0x7F. If it is, the module is not ready to respond. Stop, wait, and try again. // Note: the integration manual says: //"If there is no data awaiting transmission from the receiver, then this register will deliver the value 0xFF, // which cannot be the first byte of a valid message." // But it can be the first byte waiting to be read from the buffer if we have already read part of the message. // Therefore I think this check needs to be commented. // if (x == 0) // { // if ((incoming == 0x7F) && (ubx7FcheckDisabled == false)) // { // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging // { // _debugSerial->println(F("checkUbloxU2C: u-blox error, module not ready with data (7F error)")); // } // delay(5); //In logic analyzation, the module starting responding after 1.48ms // if (debugPin >= 0) // { // digitalWrite((uint8_t)debugPin, LOW); // delay(10); // digitalWrite((uint8_t)debugPin, HIGH); // } // goto TRY_AGAIN; // } // } process(incoming, incomingUBX, requestedClass, requestedID); // Process this valid character } } else return (false); // Sensor did not respond bytesAvailable -= bytesToRead; } } return (true); } // end checkUbloxI2C() // Checks Serial for data, passing any new bytes to process() bool SFE_UBLOX_GNSS::checkUbloxSerial(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { while (_serialPort->available()) { process(_serialPort->read(), incomingUBX, requestedClass, requestedID); } return (true); } // end checkUbloxSerial() // Checks SPI for data, passing any new bytes to process() bool SFE_UBLOX_GNSS::checkUbloxSpi(ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { // Process the contents of the SPI buffer if not empty! for (uint8_t i = 0; i < spiBufferIndex; i++) { process(spiBuffer[i], incomingUBX, requestedClass, requestedID); } spiBufferIndex = 0; _spiPort->beginTransaction(SPISettings(_spiSpeed, MSBFIRST, SPI_MODE0)); digitalWrite(_csPin, LOW); uint8_t byteReturned = _spiPort->transfer(0xFF); // Note to future self: I think the 0xFF check might cause problems when attempting to process (e.g.) RAWX data // which could legitimately contain 0xFF within the data stream. But the currentSentence check will certainly help! // If we are not receiving a sentence (currentSentence == NONE) and the byteReturned is 0xFF, // i.e. the module has no data for us, then delay for if ((byteReturned == 0xFF) && (currentSentence == SFE_UBLOX_SENTENCE_TYPE_NONE)) { digitalWrite(_csPin, HIGH); _spiPort->endTransaction(); delay(spiPollingWait); return (true); } while ((byteReturned != 0xFF) || (currentSentence != SFE_UBLOX_SENTENCE_TYPE_NONE)) { process(byteReturned, incomingUBX, requestedClass, requestedID); byteReturned = _spiPort->transfer(0xFF); } digitalWrite(_csPin, HIGH); _spiPort->endTransaction(); return (true); } // end checkUbloxSpi() // PRIVATE: Check if we have storage allocated for an incoming "automatic" message bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) { bool result = false; switch (Class) { case UBX_CLASS_NAV: { switch (ID) { case UBX_NAV_POSECEF: if (packetUBXNAVPOSECEF != NULL) result = true; break; case UBX_NAV_STATUS: if (packetUBXNAVSTATUS != NULL) result = true; break; case UBX_NAV_DOP: if (packetUBXNAVDOP != NULL) result = true; break; case UBX_NAV_ATT: if (packetUBXNAVATT != NULL) result = true; break; case UBX_NAV_PVT: if (packetUBXNAVPVT != NULL) result = true; break; case UBX_NAV_ODO: if (packetUBXNAVODO != NULL) result = true; break; case UBX_NAV_VELECEF: if (packetUBXNAVVELECEF != NULL) result = true; break; case UBX_NAV_VELNED: if (packetUBXNAVVELNED != NULL) result = true; break; case UBX_NAV_HPPOSECEF: if (packetUBXNAVHPPOSECEF != NULL) result = true; break; case UBX_NAV_HPPOSLLH: if (packetUBXNAVHPPOSLLH != NULL) result = true; break; case UBX_NAV_PVAT: if (packetUBXNAVPVAT != NULL) result = true; break; case UBX_NAV_TIMEUTC: if (packetUBXNAVTIMEUTC != NULL) result = true; break; 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; case UBX_NAV_SAT: if (packetUBXNAVSAT != NULL) result = true; break; case UBX_NAV_RELPOSNED: if (packetUBXNAVRELPOSNED != NULL) result = true; break; case UBX_NAV_AOPSTATUS: if (packetUBXNAVAOPSTATUS != NULL) result = true; break; case UBX_NAV_EOE: if (packetUBXNAVEOE != NULL) result = true; break; } } break; case UBX_CLASS_RXM: { switch (ID) { case UBX_RXM_SFRBX: if (packetUBXRXMSFRBX != NULL) result = true; break; case UBX_RXM_RAWX: if (packetUBXRXMRAWX != NULL) result = true; break; case UBX_RXM_PMP: if ((packetUBXRXMPMP != NULL) || (packetUBXRXMPMPmessage != NULL)) result = true; break; case UBX_RXM_QZSSL6: if (packetUBXRXMQZSSL6message != NULL) result = true; break; case UBX_RXM_COR: if (packetUBXRXMCOR != NULL) result = true; break; } } break; case UBX_CLASS_CFG: { switch (ID) { case UBX_CFG_PRT: if (packetUBXCFGPRT != NULL) result = true; break; case UBX_CFG_RATE: if (packetUBXCFGRATE != NULL) result = true; break; } } break; case UBX_CLASS_TIM: { switch (ID) { case UBX_TIM_TM2: if (packetUBXTIMTM2 != NULL) result = true; break; } } break; case UBX_CLASS_ESF: { switch (ID) { case UBX_ESF_ALG: if (packetUBXESFALG != NULL) result = true; break; case UBX_ESF_INS: if (packetUBXESFINS != NULL) result = true; break; case UBX_ESF_MEAS: if (packetUBXESFMEAS != NULL) result = true; break; case UBX_ESF_RAW: if (packetUBXESFRAW != NULL) result = true; break; case UBX_ESF_STATUS: if (packetUBXESFSTATUS != NULL) result = true; break; } } break; case UBX_CLASS_MGA: { switch (ID) { case UBX_MGA_ACK_DATA0: if (packetUBXMGAACK != NULL) result = true; break; case UBX_MGA_DBD: if (packetUBXMGADBD != NULL) result = true; break; } } break; case UBX_CLASS_HNR: { switch (ID) { case UBX_HNR_PVT: if (packetUBXHNRPVT != NULL) result = true; break; case UBX_HNR_ATT: if (packetUBXHNRATT != NULL) result = true; break; case UBX_HNR_INS: if (packetUBXHNRINS != NULL) result = true; break; } } break; } return (result); } // PRIVATE: Calculate how much RAM is needed to store the payload for a given automatic message uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) { uint16_t maxSize = 0; switch (Class) { case UBX_CLASS_NAV: { switch (ID) { case UBX_NAV_POSECEF: maxSize = UBX_NAV_POSECEF_LEN; break; case UBX_NAV_STATUS: maxSize = UBX_NAV_STATUS_LEN; break; case UBX_NAV_DOP: maxSize = UBX_NAV_DOP_LEN; break; case UBX_NAV_ATT: maxSize = UBX_NAV_ATT_LEN; break; case UBX_NAV_PVT: maxSize = UBX_NAV_PVT_LEN; break; case UBX_NAV_ODO: maxSize = UBX_NAV_ODO_LEN; break; case UBX_NAV_VELECEF: maxSize = UBX_NAV_VELECEF_LEN; break; case UBX_NAV_VELNED: maxSize = UBX_NAV_VELNED_LEN; break; case UBX_NAV_HPPOSECEF: maxSize = UBX_NAV_HPPOSECEF_LEN; break; case UBX_NAV_HPPOSLLH: maxSize = UBX_NAV_HPPOSLLH_LEN; break; case UBX_NAV_PVAT: maxSize = UBX_NAV_PVAT_LEN; break; case UBX_NAV_TIMEUTC: maxSize = UBX_NAV_TIMEUTC; break; 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; case UBX_NAV_SAT: maxSize = UBX_NAV_SAT_MAX_LEN; break; case UBX_NAV_RELPOSNED: maxSize = UBX_NAV_RELPOSNED_LEN_F9; break; case UBX_NAV_AOPSTATUS: maxSize = UBX_NAV_AOPSTATUS_LEN; break; case UBX_NAV_EOE: maxSize = UBX_NAV_EOE_LEN; break; } } break; case UBX_CLASS_RXM: { switch (ID) { case UBX_RXM_SFRBX: maxSize = UBX_RXM_SFRBX_MAX_LEN; break; case UBX_RXM_RAWX: maxSize = UBX_RXM_RAWX_MAX_LEN; break; case UBX_RXM_PMP: maxSize = UBX_RXM_PMP_MAX_LEN; break; case UBX_RXM_QZSSL6: maxSize = UBX_RXM_QZSSL6_MAX_LEN; break; case UBX_RXM_COR: maxSize = UBX_RXM_COR_LEN; break; } } break; case UBX_CLASS_CFG: { switch (ID) { case UBX_CFG_PRT: maxSize = UBX_CFG_PRT_LEN; break; case UBX_CFG_RATE: maxSize = UBX_CFG_RATE_LEN; break; } } break; case UBX_CLASS_TIM: { switch (ID) { case UBX_TIM_TM2: maxSize = UBX_TIM_TM2_LEN; break; } } break; case UBX_CLASS_ESF: { switch (ID) { case UBX_ESF_ALG: maxSize = UBX_ESF_ALG_LEN; break; case UBX_ESF_INS: maxSize = UBX_ESF_INS_LEN; break; case UBX_ESF_MEAS: maxSize = UBX_ESF_MEAS_MAX_LEN; break; case UBX_ESF_RAW: maxSize = UBX_ESF_RAW_MAX_LEN; break; case UBX_ESF_STATUS: maxSize = UBX_ESF_STATUS_MAX_LEN; break; } } break; case UBX_CLASS_MGA: { switch (ID) { case UBX_MGA_ACK_DATA0: maxSize = UBX_MGA_ACK_DATA0_LEN; break; case UBX_MGA_DBD: maxSize = UBX_MGA_DBD_LEN; // UBX_MGA_DBD_LEN is actually a maximum length. The packets could be shorter than this. break; } } break; case UBX_CLASS_HNR: { switch (ID) { case UBX_HNR_PVT: maxSize = UBX_HNR_PVT_LEN; break; case UBX_HNR_ATT: maxSize = UBX_HNR_ATT_LEN; break; case UBX_HNR_INS: maxSize = UBX_HNR_INS_LEN; break; } } break; } return (maxSize); } // Processes NMEA and UBX binary sentences one byte at a time // Take a given byte and file it into the proper array void SFE_UBLOX_GNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { if (_outputPort != NULL) _outputPort->write(incoming); // Echo this byte to the serial port if ((currentSentence == SFE_UBLOX_SENTENCE_TYPE_NONE) || (currentSentence == SFE_UBLOX_SENTENCE_TYPE_NMEA)) { if (incoming == UBX_SYNCH_1) // UBX binary frames start with 0xB5, aka μ { // This is the start of a binary sentence. Reset flags. // We still don't know the response class ubxFrameCounter = 0; currentSentence = SFE_UBLOX_SENTENCE_TYPE_UBX; // Reset the packetBuf.counter even though we will need to reset it again when ubxFrameCounter == 2 packetBuf.counter = 0; ignoreThisPayload = false; // We should not ignore this payload - yet // Store data in packetBuf until we know if we have a requested class and ID match activePacketBuffer = SFE_UBLOX_PACKET_PACKETBUF; } else if (incoming == '$') { nmeaByteCounter = 0; // Reset the NMEA byte counter currentSentence = SFE_UBLOX_SENTENCE_TYPE_NMEA; } else if (incoming == 0xD3) // RTCM frames start with 0xD3 { rtcmFrameCounter = 0; currentSentence = SFE_UBLOX_SENTENCE_TYPE_RTCM; } else { // This character is unknown or we missed the previous start of a sentence } } // Depending on the sentence, pass the character to the individual processor if (currentSentence == SFE_UBLOX_SENTENCE_TYPE_UBX) { // Decide what type of response this is if ((ubxFrameCounter == 0) && (incoming != UBX_SYNCH_1)) // ISO 'μ' currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // Something went wrong. Reset. else if ((ubxFrameCounter == 1) && (incoming != UBX_SYNCH_2)) // ASCII 'b' currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // Something went wrong. Reset. // Note to future self: // There may be some duplication / redundancy in the next few lines as processUBX will also // load information into packetBuf, but we'll do it here too for clarity else if (ubxFrameCounter == 2) // Class { // Record the class in packetBuf until we know what to do with it packetBuf.cls = incoming; // (Duplication) rollingChecksumA = 0; // Reset our rolling checksums here (not when we receive the 0xB5) rollingChecksumB = 0; packetBuf.counter = 0; // Reset the packetBuf.counter (again) packetBuf.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // Reset the packet validity (redundant?) packetBuf.startingSpot = incomingUBX->startingSpot; // Copy the startingSpot } else if (ubxFrameCounter == 3) // ID { // Record the ID in packetBuf until we know what to do with it packetBuf.id = incoming; // (Duplication) // We can now identify the type of response // If the packet we are receiving is not an ACK then check for a class and ID match if (packetBuf.cls != UBX_CLASS_ACK) { // This is not an ACK so check for a class and ID match if ((packetBuf.cls == requestedClass) && (packetBuf.id == requestedID)) { // This is not an ACK and we have a class and ID match // So start diverting data into incomingUBX (usually packetCfg) activePacketBuffer = SFE_UBLOX_PACKET_PACKETCFG; incomingUBX->cls = packetBuf.cls; // Copy the class and ID into incomingUBX (usually packetCfg) incomingUBX->id = packetBuf.id; incomingUBX->counter = packetBuf.counter; // Copy over the .counter too } // This is not an ACK and we do not have a complete class and ID match // So let's check if this is an "automatic" message which has its own storage defined else if (checkAutomatic(packetBuf.cls, packetBuf.id)) { // This is not the message we were expecting but it has its own storage and so we should process it anyway. // We'll try to use packetAuto to buffer the message (so it can't overwrite anything in packetCfg). // We need to allocate memory for the packetAuto payload (payloadAuto) - and delete it once // reception is complete. uint16_t maxPayload = getMaxPayloadSize(packetBuf.cls, packetBuf.id); // Calculate how much RAM we need if (maxPayload == 0) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("process: getMaxPayloadSize returned ZERO!! Class: 0x")); _debugSerial->print(packetBuf.cls); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(packetBuf.id); } #endif } if (payloadAuto != NULL) // Check if memory is already allocated - this should be impossible! { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("process: memory is already allocated for payloadAuto! Deleting...")); } #endif delete[] payloadAuto; // Created with new[] payloadAuto = NULL; // Redundant? packetAuto.payload = payloadAuto; } payloadAuto = new uint8_t[maxPayload]; // Allocate RAM for payloadAuto packetAuto.payload = payloadAuto; if (payloadAuto == NULL) // Check if the alloc failed { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("process: memory allocation failed for \"automatic\" message: Class: 0x")); _debugSerial->print(packetBuf.cls, HEX); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(packetBuf.id, HEX); _debugSerial->println(F("process: \"automatic\" message could overwrite data")); } #endif // The RAM allocation failed so fall back to using incomingUBX (usually packetCfg) even though we risk overwriting data activePacketBuffer = SFE_UBLOX_PACKET_PACKETCFG; incomingUBX->cls = packetBuf.cls; // Copy the class and ID into incomingUBX (usually packetCfg) incomingUBX->id = packetBuf.id; incomingUBX->counter = packetBuf.counter; // Copy over the .counter too } else { // The RAM allocation was successful so we start diverting data into packetAuto and process it activePacketBuffer = SFE_UBLOX_PACKET_PACKETAUTO; packetAuto.cls = packetBuf.cls; // Copy the class and ID into packetAuto packetAuto.id = packetBuf.id; packetAuto.counter = packetBuf.counter; // Copy over the .counter too packetAuto.startingSpot = packetBuf.startingSpot; // And the starting spot? (Probably redundant) #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("process: incoming \"automatic\" message: Class: 0x")); _debugSerial->print(packetBuf.cls, HEX); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(packetBuf.id, HEX); } #endif } } else { // This is not an ACK and we do not have a class and ID match // so we should keep diverting data into packetBuf and ignore the payload ignoreThisPayload = true; } } else { // This is an ACK so it is to early to do anything with it // We need to wait until we have received the length and data bytes // So we should keep diverting data into packetBuf } } else if (ubxFrameCounter == 4) // Length LSB { // We should save the length in packetBuf even if activePacketBuffer == SFE_UBLOX_PACKET_PACKETCFG packetBuf.len = incoming; // (Duplication) } else if (ubxFrameCounter == 5) // Length MSB { // We should save the length in packetBuf even if activePacketBuffer == SFE_UBLOX_PACKET_PACKETCFG packetBuf.len |= incoming << 8; // (Duplication) } else if (ubxFrameCounter == 6) // This should be the first byte of the payload unless .len is zero { if (packetBuf.len == 0) // Check if length is zero (hopefully this is impossible!) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("process: ZERO LENGTH packet received: Class: 0x")); _debugSerial->print(packetBuf.cls, HEX); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(packetBuf.id, HEX); } #endif // If length is zero (!) this will be the first byte of the checksum so record it packetBuf.checksumA = incoming; } else { // The length is not zero so record this byte in the payload packetBuf.payload[0] = incoming; } } else if (ubxFrameCounter == 7) // This should be the second byte of the payload unless .len is zero or one { if (packetBuf.len == 0) // Check if length is zero (hopefully this is impossible!) { // If length is zero (!) this will be the second byte of the checksum so record it packetBuf.checksumB = incoming; } else if (packetBuf.len == 1) // Check if length is one { // The length is one so this is the first byte of the checksum packetBuf.checksumA = incoming; } else // Length is >= 2 so this must be a payload byte { packetBuf.payload[1] = incoming; } // Now that we have received two payload bytes, we can check for a matching ACK/NACK if ((activePacketBuffer == SFE_UBLOX_PACKET_PACKETBUF) // If we are not already processing a data packet && (packetBuf.cls == UBX_CLASS_ACK) // and if this is an ACK/NACK && (packetBuf.payload[0] == requestedClass) // and if the class matches && (packetBuf.payload[1] == requestedID)) // and if the ID matches { if (packetBuf.len == 2) // Check if .len is 2 { // Then this is a matching ACK so copy it into packetAck activePacketBuffer = SFE_UBLOX_PACKET_PACKETACK; packetAck.cls = packetBuf.cls; packetAck.id = packetBuf.id; packetAck.len = packetBuf.len; packetAck.counter = packetBuf.counter; packetAck.payload[0] = packetBuf.payload[0]; packetAck.payload[1] = packetBuf.payload[1]; } else // Length is not 2 (hopefully this is impossible!) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("process: ACK received with .len != 2: Class: 0x")); _debugSerial->print(packetBuf.payload[0], HEX); _debugSerial->print(F(" ID: 0x")); _debugSerial->print(packetBuf.payload[1], HEX); _debugSerial->print(F(" len: ")); _debugSerial->println(packetBuf.len); } #endif } } } // Divert incoming into the correct buffer if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETACK) processUBX(incoming, &packetAck, requestedClass, requestedID); else if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETCFG) processUBX(incoming, incomingUBX, requestedClass, requestedID); else if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETBUF) processUBX(incoming, &packetBuf, requestedClass, requestedID); else // if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETAUTO) processUBX(incoming, &packetAuto, requestedClass, requestedID); // Finally, increment the frame counter ubxFrameCounter++; } else if (currentSentence == SFE_UBLOX_SENTENCE_TYPE_NMEA) // Process incoming NMEA mesages. Selectively log if desired. { if ((nmeaByteCounter == 0) && (incoming != '$')) { currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // Something went wrong. Reset. (Almost certainly redundant!) } else if ((nmeaByteCounter == 1) && (incoming != 'G')) { currentSentence = SFE_UBLOX_SENTENCE_TYPE_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) { if (!_signsOfLife) // If _signsOfLife is not already true, set _signsOfLife to true if the NMEA header is valid { _signsOfLife = isNMEAHeaderValid(); } #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA // Check if we have automatic storage for this message if (isThisNMEAauto()) { uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data uint8_t nmeaMaxLength = getNMEAMaxLength(); *lengthPtr = 6; // Set the working copy length memset(nmeaPtr, 0, nmeaMaxLength); // Clear the working copy memcpy(nmeaPtr, &nmeaAddressField[0], 6); // Copy the start character and address field into the working copy } else #endif { // if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging // { // _debugSerial->println(F("process: non-auto NMEA message")); // } } // 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 } // 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 and/or pass it to processNMEA? { #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if (isThisNMEAauto()) { uint8_t *lengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length uint8_t *nmeaPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data uint8_t nmeaMaxLength = getNMEAMaxLength(); if (*lengthPtr < nmeaMaxLength) { *(nmeaPtr + *lengthPtr) = incoming; // Store the character *lengthPtr = *lengthPtr + 1; // Increment the length if (*lengthPtr == nmeaMaxLength) { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("process: NMEA buffer is full!")); } } } } #endif if (logThisNMEA()) storeFileBytes(&incoming, 1); // Add incoming to the file buffer if (processThisNMEA()) processNMEA(incoming); // Pass incoming to processNMEA } 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 = SFE_UBLOX_SENTENCE_TYPE_NONE; // Something went wrong. Reset. if (nmeaByteCounter == 0) // Check if we are done { #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if (isThisNMEAauto()) { uint8_t *workingLengthPtr = getNMEAWorkingLengthPtr(); // Get a pointer to the working copy length uint8_t *workingNMEAPtr = getNMEAWorkingNMEAPtr(); // Get a pointer to the working copy NMEA data uint8_t nmeaMaxLength = getNMEAMaxLength(); // Check the checksum: the checksum is the exclusive-OR of all characters between the $ and the * uint8_t nmeaChecksum = 0; uint8_t charsChecked = 1; // Start after the $ uint8_t thisChar = '\0'; while ((charsChecked < (nmeaMaxLength - 1)) && (charsChecked < ((*workingLengthPtr) - 4)) && (thisChar != '*')) { thisChar = *(workingNMEAPtr + charsChecked); // Get a char from the working copy if (thisChar != '*') // Ex-or the char into the checksum - but not if it is the '*' nmeaChecksum ^= thisChar; charsChecked++; // Increment the counter } if (thisChar == '*') // Make sure we found the * { uint8_t expectedChecksum1 = (nmeaChecksum >> 4) + '0'; if (expectedChecksum1 >= ':') // Handle Hex correctly expectedChecksum1 += 'A' - ':'; uint8_t expectedChecksum2 = (nmeaChecksum & 0x0F) + '0'; if (expectedChecksum2 >= ':') // Handle Hex correctly expectedChecksum2 += 'A' - ':'; if ((expectedChecksum1 == *(workingNMEAPtr + charsChecked)) && (expectedChecksum2 == *(workingNMEAPtr + charsChecked + 1))) { uint8_t *completeLengthPtr = getNMEACompleteLengthPtr(); // Get a pointer to the complete copy length uint8_t *completeNMEAPtr = getNMEACompleteNMEAPtr(); // Get a pointer to the complete copy NMEA data memset(completeNMEAPtr, 0, nmeaMaxLength); // Clear the previous complete copy memcpy(completeNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the complete copy *completeLengthPtr = *workingLengthPtr; // Update the length nmeaAutomaticFlags *flagsPtr = getNMEAFlagsPtr(); // Get a pointer to the flags nmeaAutomaticFlags flagsCopy = *flagsPtr; flagsCopy.flags.bits.completeCopyValid = 1; // Set the complete copy valid flag flagsCopy.flags.bits.completeCopyRead = 0; // Clear the complete copy read flag *flagsPtr = flagsCopy; // Update the flags // Callback if (doesThisNMEAHaveCallback()) // Do we need to copy the data into the callback copy? { if (flagsCopy.flags.bits.callbackCopyValid == 0) // Has the callback copy valid flag been cleared (by checkCallbacks) { uint8_t *callbackLengthPtr = getNMEACallbackLengthPtr(); // Get a pointer to the callback copy length uint8_t *callbackNMEAPtr = getNMEACallbackNMEAPtr(); // Get a pointer to the callback copy NMEA data memset(callbackNMEAPtr, 0, nmeaMaxLength); // Clear the previous callback copy memcpy(callbackNMEAPtr, workingNMEAPtr, *workingLengthPtr); // Copy the working copy into the callback copy *callbackLengthPtr = *workingLengthPtr; // Update the length flagsCopy.flags.bits.callbackCopyValid = 1; // Set the callback copy valid flag *flagsPtr = flagsCopy; // Update the flags } } } else { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("process: NMEA checksum fail (2)! Expected ")); _debugSerial->write(expectedChecksum1); _debugSerial->write(expectedChecksum2); _debugSerial->print(F(" Got ")); _debugSerial->write(*(workingNMEAPtr + charsChecked)); _debugSerial->write(*(workingNMEAPtr + charsChecked + 1)); _debugSerial->println(); } } } else { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("process: NMEA checksum fail (1)!")); } } } #endif currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // All done! } } else if (currentSentence == SFE_UBLOX_SENTENCE_TYPE_RTCM) { currentSentence = processRTCMframe(incoming, &rtcmFrameCounter); // Deal with RTCM bytes } } // PRIVATE: Return true if we should add this NMEA message to the file buffer for logging bool 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') { 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); 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); } // PRIVATE: Return true if the NMEA header is valid bool SFE_UBLOX_GNSS::isNMEAHeaderValid() { if (nmeaAddressField[0] != '*') return (false); if (nmeaAddressField[1] != 'G') return (false); if ((nmeaAddressField[3] == 'D') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'M')) return (true); if (nmeaAddressField[3] == 'G') { if ((nmeaAddressField[4] == 'A') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'B') && (nmeaAddressField[5] == 'S')) return (true); if ((nmeaAddressField[4] == 'G') && (nmeaAddressField[5] == 'A')) return (true); if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'L')) return (true); if ((nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'N') && (nmeaAddressField[5] == 'S')) return (true); if ((nmeaAddressField[4] == 'P') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'Q') && (nmeaAddressField[5] == 'Q')) return (true); if ((nmeaAddressField[4] == 'R') && (nmeaAddressField[5] == 'S')) return (true); if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'A')) return (true); if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'T')) return (true); if ((nmeaAddressField[4] == 'S') && (nmeaAddressField[5] == 'V')) return (true); } if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'M')) return (true); if ((nmeaAddressField[3] == 'R') && (nmeaAddressField[4] == 'M') && (nmeaAddressField[5] == 'C')) return (true); if ((nmeaAddressField[3] == 'T') && (nmeaAddressField[4] == 'X') && (nmeaAddressField[5] == 'T')) return (true); if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'L') && (nmeaAddressField[5] == 'W')) return (true); if ((nmeaAddressField[3] == 'V') && (nmeaAddressField[4] == 'T') && (nmeaAddressField[5] == 'G')) return (true); if ((nmeaAddressField[3] == 'Z') && (nmeaAddressField[4] == 'D') && (nmeaAddressField[5] == 'A')) return (true); return (false); } // PRIVATE: Return true if we should pass this NMEA message to processNMEA bool 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. void SFE_UBLOX_GNSS::processNMEA_v(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 } void SFE_UBLOX_GNSS::processNMEA(char incoming) { processNMEA_v(incoming); } #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA // Check if the NMEA message (in nmeaAddressField) is "auto" (i.e. has RAM allocated for it) bool SFE_UBLOX_GNSS::isThisNMEAauto() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPGGA != NULL) return true; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNGGA != NULL) return true; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPVTG != NULL) return true; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNVTG != NULL) return true; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPRMC != NULL) return true; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNRMC != NULL) return true; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPZDA != NULL) return true; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNZDA != NULL) return true; } return false; } // Do we need to copy the data into the callback copy? bool SFE_UBLOX_GNSS::doesThisNMEAHaveCallback() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPGGA != NULL) if (storageNMEAGPGGA->callbackCopy != NULL) if ((storageNMEAGPGGA->callbackPointer != NULL) || (storageNMEAGPGGA->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNGGA != NULL) if (storageNMEAGNGGA->callbackCopy != NULL) if ((storageNMEAGNGGA->callbackPointer != NULL) || (storageNMEAGNGGA->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPVTG != NULL) if (storageNMEAGPVTG->callbackCopy != NULL) if ((storageNMEAGPVTG->callbackPointer != NULL) || (storageNMEAGPVTG->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNVTG != NULL) if (storageNMEAGNVTG->callbackCopy != NULL) if ((storageNMEAGNVTG->callbackPointer != NULL) || (storageNMEAGNVTG->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPRMC != NULL) if (storageNMEAGPRMC->callbackCopy != NULL) if ((storageNMEAGPRMC->callbackPointer != NULL) || (storageNMEAGPRMC->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNRMC != NULL) if (storageNMEAGNRMC->callbackCopy != NULL) if ((storageNMEAGNRMC->callbackPointer != NULL) || (storageNMEAGNRMC->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGPZDA != NULL) if (storageNMEAGPZDA->callbackCopy != NULL) if ((storageNMEAGPZDA->callbackPointer != NULL) || (storageNMEAGPZDA->callbackPointerPtr != NULL)) return true; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { if (storageNMEAGNZDA != NULL) if (storageNMEAGNZDA->callbackCopy != NULL) if ((storageNMEAGNZDA->callbackPointer != NULL) || (storageNMEAGNZDA->callbackPointerPtr != NULL)) return true; } return false; } // Get a pointer to the working copy length uint8_t *SFE_UBLOX_GNSS::getNMEAWorkingLengthPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->workingCopy.length; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->workingCopy.length; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->workingCopy.length; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->workingCopy.length; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->workingCopy.length; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->workingCopy.length; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->workingCopy.length; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->workingCopy.length; } return NULL; } // Get a pointer to the working copy NMEA data uint8_t *SFE_UBLOX_GNSS::getNMEAWorkingNMEAPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->workingCopy.nmea[0]; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->workingCopy.nmea[0]; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->workingCopy.nmea[0]; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->workingCopy.nmea[0]; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->workingCopy.nmea[0]; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->workingCopy.nmea[0]; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->workingCopy.nmea[0]; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->workingCopy.nmea[0]; } return NULL; } // Get a pointer to the complete copy length uint8_t *SFE_UBLOX_GNSS::getNMEACompleteLengthPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->completeCopy.length; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->completeCopy.length; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->completeCopy.length; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->completeCopy.length; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->completeCopy.length; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->completeCopy.length; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->completeCopy.length; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->completeCopy.length; } return NULL; } // Get a pointer to the complete copy NMEA data uint8_t *SFE_UBLOX_GNSS::getNMEACompleteNMEAPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->completeCopy.nmea[0]; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->completeCopy.nmea[0]; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->completeCopy.nmea[0]; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->completeCopy.nmea[0]; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->completeCopy.nmea[0]; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->completeCopy.nmea[0]; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->completeCopy.nmea[0]; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->completeCopy.nmea[0]; } return NULL; } // Get a pointer to the callback copy length uint8_t *SFE_UBLOX_GNSS::getNMEACallbackLengthPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->callbackCopy->length; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->callbackCopy->length; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->callbackCopy->length; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->callbackCopy->length; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->callbackCopy->length; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->callbackCopy->length; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->callbackCopy->length; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->callbackCopy->length; } return NULL; } // Get a pointer to the callback copy NMEA data uint8_t *SFE_UBLOX_GNSS::getNMEACallbackNMEAPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->callbackCopy->nmea[0]; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->callbackCopy->nmea[0]; } return NULL; } // Get the maximum length of this NMEA message uint8_t SFE_UBLOX_GNSS::getNMEAMaxLength() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_GGA_MAX_LENGTH; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_GGA_MAX_LENGTH; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_VTG_MAX_LENGTH; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_VTG_MAX_LENGTH; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_RMC_MAX_LENGTH; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_RMC_MAX_LENGTH; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_ZDA_MAX_LENGTH; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return NMEA_ZDA_MAX_LENGTH; } return 0; } // Get a pointer to the automatic NMEA flags nmeaAutomaticFlags *SFE_UBLOX_GNSS::getNMEAFlagsPtr() { char thisNMEA[] = "GPGGA"; if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPGGA->automaticFlags; } strcpy(thisNMEA, "GNGGA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNGGA->automaticFlags; } strcpy(thisNMEA, "GPVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPVTG->automaticFlags; } strcpy(thisNMEA, "GNVTG"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNVTG->automaticFlags; } strcpy(thisNMEA, "GPRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPRMC->automaticFlags; } strcpy(thisNMEA, "GNRMC"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNRMC->automaticFlags; } strcpy(thisNMEA, "GPZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGPZDA->automaticFlags; } strcpy(thisNMEA, "GNZDA"); if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) { return &storageNMEAGNZDA->automaticFlags; } return NULL; } #endif // We need to be able to identify an RTCM packet and then the length // so that we know when the RTCM message is completely received and we then start // listening for other sentences (like NMEA or UBX) // RTCM packet structure is very odd. I never found RTCM STANDARD 10403.2 but // http://d1.amobbs.com/bbs_upload782111/files_39/ourdev_635123CK0HJT.pdf is good // https://dspace.cvut.cz/bitstream/handle/10467/65205/F3-BP-2016-Shkalikava-Anastasiya-Prenos%20polohove%20informace%20prostrednictvim%20datove%20site.pdf?sequence=-1 // Lead me to: https://forum.u-blox.com/index.php/4348/how-to-read-rtcm-messages-from-neo-m8p // RTCM 3.2 bytes look like this: // Byte 0: Always 0xD3 // Byte 1: 6-bits of zero // Byte 2: 10-bits of length of this packet including the first two-ish header bytes, + 6. // byte 3 + 4 bits: Msg type 12 bits // Example: D3 00 7C 43 F0 ... / 0x7C = 124+6 = 130 bytes in this packet, 0x43F = Msg type 1087 SFE_UBLOX_GNSS::sfe_ublox_sentence_types_e SFE_UBLOX_GNSS::processRTCMframe_v(uint8_t incoming, uint16_t *rtcmFrameCounter) { static uint16_t rtcmLen = 0; if (*rtcmFrameCounter == 1) { rtcmLen = (incoming & 0x03) << 8; // Get the last two bits of this byte. Bits 8&9 of 10-bit length } else if (*rtcmFrameCounter == 2) { rtcmLen |= incoming; // Bits 0-7 of packet length rtcmLen += 6; // There are 6 additional bytes of what we presume is header, msgType, CRC, and stuff } /*else if (rtcmFrameCounter == 3) { rtcmMsgType = incoming << 4; //Message Type, MS 4 bits } else if (rtcmFrameCounter == 4) { rtcmMsgType |= (incoming >> 4); //Message Type, bits 0-7 }*/ *rtcmFrameCounter = *rtcmFrameCounter + 1; processRTCM(incoming); // Here is where we expose this byte to the user // Reset and start looking for next sentence type when done return (*rtcmFrameCounter == rtcmLen) ? SFE_UBLOX_SENTENCE_TYPE_NONE : SFE_UBLOX_SENTENCE_TYPE_RTCM; } SFE_UBLOX_GNSS::sfe_ublox_sentence_types_e SFE_UBLOX_GNSS::processRTCMframe(uint8_t incoming, uint16_t *rtcmFrameCounter) { return processRTCMframe_v(incoming, rtcmFrameCounter); } // This function is called for each byte of an RTCM frame // Ths user can overwrite this function and process the RTCM frame as they please // Bytes can be piped to Serial or other interface. The consumer could be a radio or the internet (Ntrip broadcaster) void SFE_UBLOX_GNSS::processRTCM_v(uint8_t incoming) { // Radio.sendReliable((String)incoming); //An example of passing this byte to a radio //_debugSerial->write(incoming); //An example of passing this byte out the serial port // Debug printing // _debugSerial->print(F(" ")); // if(incoming < 0x10) _debugSerial->print(F("0")); // _debugSerial->print(incoming, HEX); // if(rtcmFrameCounter % 16 == 0) _debugSerial->println(); (void)incoming; // Do something with incoming just to get rid of the pesky compiler warning! } void SFE_UBLOX_GNSS::processRTCM(uint8_t incoming) { processRTCM_v(incoming); } // Given a character, file it away into the uxb packet structure // Set valid to VALID or NOT_VALID once sentence is completely received and passes or fails CRC // The payload portion of the packet can be 100s of bytes but the max array size is packetCfgPayloadSize bytes. // startingSpot can be set so we only record a subset of bytes within a larger packet. void SFE_UBLOX_GNSS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t requestedClass, uint8_t requestedID) { // If incomingUBX is a user-defined custom packet, then the payload size could be different to packetCfgPayloadSize. // TO DO: update this to prevent an overrun when receiving an automatic message // and the incomingUBX payload size is smaller than packetCfgPayloadSize. uint16_t maximum_payload_size; if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETCFG) maximum_payload_size = packetCfgPayloadSize; else if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETAUTO) { // Calculate maximum payload size once Class and ID have been received // (This check is probably redundant as activePacketBuffer can only be SFE_UBLOX_PACKET_PACKETAUTO // when ubxFrameCounter >= 3) // if (incomingUBX->counter >= 2) //{ maximum_payload_size = getMaxPayloadSize(incomingUBX->cls, incomingUBX->id); if (maximum_payload_size == 0) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("processUBX: getMaxPayloadSize returned ZERO!! Class: 0x")); _debugSerial->print(incomingUBX->cls); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(incomingUBX->id); } #endif } //} // else // maximum_payload_size = 2; } else maximum_payload_size = 2; bool overrun = false; // Add all incoming bytes to the rolling checksum // Stop at len+4 as this is the checksum bytes to that should not be added to the rolling checksum if (incomingUBX->counter < incomingUBX->len + 4) addToChecksum(incoming); if (incomingUBX->counter == 0) { incomingUBX->cls = incoming; } else if (incomingUBX->counter == 1) { incomingUBX->id = incoming; } else if (incomingUBX->counter == 2) // Len LSB { incomingUBX->len = incoming; } else if (incomingUBX->counter == 3) // Len MSB { incomingUBX->len |= incoming << 8; } else if (incomingUBX->counter == incomingUBX->len + 4) // ChecksumA { incomingUBX->checksumA = incoming; } else if (incomingUBX->counter == incomingUBX->len + 5) // ChecksumB { incomingUBX->checksumB = incoming; currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // We're done! Reset the sentence to being looking for a new start char // Validate this sentence if ((incomingUBX->checksumA == rollingChecksumA) && (incomingUBX->checksumB == rollingChecksumB)) { incomingUBX->valid = SFE_UBLOX_PACKET_VALIDITY_VALID; // Flag the packet as valid _signsOfLife = true; // The checksum is valid, so set the _signsOfLife flag // Let's check if the class and ID match the requestedClass and requestedID // Remember - this could be a data packet or an ACK packet if ((incomingUBX->cls == requestedClass) && (incomingUBX->id == requestedID)) { incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_VALID; // If we have a match, set the classAndIDmatch flag to valid } // If this is an ACK then let's check if the class and ID match the requestedClass and requestedID else if ((incomingUBX->cls == UBX_CLASS_ACK) && (incomingUBX->id == UBX_ACK_ACK) && (incomingUBX->payload[0] == requestedClass) && (incomingUBX->payload[1] == requestedID)) { incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_VALID; // If we have a match, set the classAndIDmatch flag to valid } // If this is a NACK then let's check if the class and ID match the requestedClass and requestedID else if ((incomingUBX->cls == UBX_CLASS_ACK) && (incomingUBX->id == UBX_ACK_NACK) && (incomingUBX->payload[0] == requestedClass) && (incomingUBX->payload[1] == requestedID)) { incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_NOTACKNOWLEDGED; // If we have a match, set the classAndIDmatch flag to NOTACKNOWLEDGED #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("processUBX: NACK received: Requested Class: 0x")); _debugSerial->print(incomingUBX->payload[0], HEX); _debugSerial->print(F(" Requested ID: 0x")); _debugSerial->println(incomingUBX->payload[1], HEX); } #endif } // This is not an ACK and we do not have a complete class and ID match // So let's check for an "automatic" message arriving else if (checkAutomatic(incomingUBX->cls, incomingUBX->id)) { // This isn't the message we are looking for... // Let's say so and leave incomingUBX->classAndIDmatch _unchanged_ #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("processUBX: incoming \"automatic\" message: Class: 0x")); _debugSerial->print(incomingUBX->cls, HEX); _debugSerial->print(F(" ID: 0x")); _debugSerial->println(incomingUBX->id, HEX); } #endif } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("Incoming: Size: ")); _debugSerial->print(incomingUBX->len); _debugSerial->print(F(" Received: ")); printPacket(incomingUBX); if (incomingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID) { _debugSerial->println(F("packetCfg now valid")); } if (packetAck.valid == SFE_UBLOX_PACKET_VALIDITY_VALID) { _debugSerial->println(F("packetAck now valid")); } if (incomingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) { _debugSerial->println(F("packetCfg classAndIDmatch")); } if (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) { _debugSerial->println(F("packetAck classAndIDmatch")); } } #endif // We've got a valid packet, now do something with it but only if ignoreThisPayload is false if (ignoreThisPayload == false) { processUBXpacket(incomingUBX); } } else // Checksum failure { incomingUBX->valid = SFE_UBLOX_PACKET_VALIDITY_NOT_VALID; // Let's check if the class and ID match the requestedClass and requestedID. // This is potentially risky as we are saying that we saw the requested Class and ID // but that the packet checksum failed. Potentially it could be the class or ID bytes // that caused the checksum error! if ((incomingUBX->cls == requestedClass) && (incomingUBX->id == requestedID)) { incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_VALID; // If we have a match, set the classAndIDmatch flag to not valid } // If this is an ACK then let's check if the class and ID match the requestedClass and requestedID else if ((incomingUBX->cls == UBX_CLASS_ACK) && (incomingUBX->payload[0] == requestedClass) && (incomingUBX->payload[1] == requestedID)) { incomingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_VALID; // If we have a match, set the classAndIDmatch flag to not valid } if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { // Drive an external pin to allow for easier logic analyzation if (debugPin >= 0) { digitalWrite((uint8_t)debugPin, LOW); delay(10); digitalWrite((uint8_t)debugPin, HIGH); } #ifndef SFE_UBLOX_REDUCED_PROG_MEM _debugSerial->print(F("Checksum failed:")); _debugSerial->print(F(" checksumA: ")); _debugSerial->print(incomingUBX->checksumA); _debugSerial->print(F(" checksumB: ")); _debugSerial->print(incomingUBX->checksumB); _debugSerial->print(F(" rollingChecksumA: ")); _debugSerial->print(rollingChecksumA); _debugSerial->print(F(" rollingChecksumB: ")); _debugSerial->print(rollingChecksumB); _debugSerial->println(); #endif } } // Now that the packet is complete and has been processed, we need to delete the memory // allocated for packetAuto if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETAUTO) { delete[] payloadAuto; // Created with new[] payloadAuto = NULL; // Redundant? packetAuto.payload = payloadAuto; } } else // Load this byte into the payload array { // If an automatic packet comes in asynchronously, we need to fudge the startingSpot uint16_t startingSpot = incomingUBX->startingSpot; if (checkAutomatic(incomingUBX->cls, incomingUBX->id)) startingSpot = 0; // Check if this is payload data which should be ignored if (ignoreThisPayload == false) { // Begin recording if counter goes past startingSpot if ((incomingUBX->counter - 4) >= startingSpot) { // Check to see if we have room for this byte if (((incomingUBX->counter - 4) - startingSpot) < maximum_payload_size) // If counter = 208, starting spot = 200, we're good to record. { incomingUBX->payload[(incomingUBX->counter - 4) - startingSpot] = incoming; // Store this byte into payload array } else { overrun = true; } } } } // incomingUBX->counter should never reach maximum_payload_size + class + id + len[2] + checksum[2] if (overrun || ((incomingUBX->counter == maximum_payload_size + 6) && (ignoreThisPayload == false))) { // Something has gone very wrong currentSentence = SFE_UBLOX_SENTENCE_TYPE_NONE; // Reset the sentence to being looking for a new start char #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { if (overrun) _debugSerial->print(F("processUBX: buffer overrun detected!")); else _debugSerial->print(F("processUBX: counter hit maximum_payload_size + 6!")); _debugSerial->print(F(" activePacketBuffer: ")); _debugSerial->print(activePacketBuffer); _debugSerial->print(F(" maximum_payload_size: ")); _debugSerial->println(maximum_payload_size); } #endif } // Increment the counter incomingUBX->counter++; } // Once a packet has been received and validated, identify this packet's class/id and update internal flags void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) { switch (msg->cls) { case UBX_CLASS_NAV: if (msg->id == UBX_NAV_POSECEF && msg->len == UBX_NAV_POSECEF_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVPOSECEF != NULL) { packetUBXNAVPOSECEF->data.iTOW = extractLong(msg, 0); packetUBXNAVPOSECEF->data.ecefX = extractSignedLong(msg, 4); packetUBXNAVPOSECEF->data.ecefY = extractSignedLong(msg, 8); packetUBXNAVPOSECEF->data.ecefZ = extractSignedLong(msg, 12); packetUBXNAVPOSECEF->data.pAcc = extractLong(msg, 16); // Mark all datums as fresh (not read before) packetUBXNAVPOSECEF->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVPOSECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPOSECEF->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVPOSECEF->callbackData->iTOW, &packetUBXNAVPOSECEF->data.iTOW, sizeof(UBX_NAV_POSECEF_data_t)); packetUBXNAVPOSECEF->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVPOSECEF->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_STATUS && msg->len == UBX_NAV_STATUS_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVSTATUS != NULL) { packetUBXNAVSTATUS->data.iTOW = extractLong(msg, 0); packetUBXNAVSTATUS->data.gpsFix = extractByte(msg, 4); packetUBXNAVSTATUS->data.flags.all = extractByte(msg, 5); packetUBXNAVSTATUS->data.fixStat.all = extractByte(msg, 6); packetUBXNAVSTATUS->data.flags2.all = extractByte(msg, 7); packetUBXNAVSTATUS->data.ttff = extractLong(msg, 8); packetUBXNAVSTATUS->data.msss = extractLong(msg, 12); // Mark all datums as fresh (not read before) packetUBXNAVSTATUS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSTATUS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVSTATUS->callbackData->iTOW, &packetUBXNAVSTATUS->data.iTOW, sizeof(UBX_NAV_STATUS_data_t)); packetUBXNAVSTATUS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVSTATUS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_DOP && msg->len == UBX_NAV_DOP_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVDOP != NULL) { packetUBXNAVDOP->data.iTOW = extractLong(msg, 0); packetUBXNAVDOP->data.gDOP = extractInt(msg, 4); packetUBXNAVDOP->data.pDOP = extractInt(msg, 6); packetUBXNAVDOP->data.tDOP = extractInt(msg, 8); packetUBXNAVDOP->data.vDOP = extractInt(msg, 10); packetUBXNAVDOP->data.hDOP = extractInt(msg, 12); packetUBXNAVDOP->data.nDOP = extractInt(msg, 14); packetUBXNAVDOP->data.eDOP = extractInt(msg, 16); // Mark all datums as fresh (not read before) packetUBXNAVDOP->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVDOP->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVDOP->callbackData->iTOW, &packetUBXNAVDOP->data.iTOW, sizeof(UBX_NAV_DOP_data_t)); packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVDOP->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_ATT && msg->len == UBX_NAV_ATT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVATT != NULL) { packetUBXNAVATT->data.iTOW = extractLong(msg, 0); packetUBXNAVATT->data.version = extractByte(msg, 4); packetUBXNAVATT->data.roll = extractSignedLong(msg, 8); packetUBXNAVATT->data.pitch = extractSignedLong(msg, 12); packetUBXNAVATT->data.heading = extractSignedLong(msg, 16); packetUBXNAVATT->data.accRoll = extractLong(msg, 20); packetUBXNAVATT->data.accPitch = extractLong(msg, 24); packetUBXNAVATT->data.accHeading = extractLong(msg, 28); // Mark all datums as fresh (not read before) packetUBXNAVATT->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVATT->callbackData->iTOW, &packetUBXNAVATT->data.iTOW, sizeof(UBX_NAV_ATT_data_t)); packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVATT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_PVT && msg->len == UBX_NAV_PVT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVPVT != NULL) { packetUBXNAVPVT->data.iTOW = extractLong(msg, 0); packetUBXNAVPVT->data.year = extractInt(msg, 4); packetUBXNAVPVT->data.month = extractByte(msg, 6); packetUBXNAVPVT->data.day = extractByte(msg, 7); packetUBXNAVPVT->data.hour = extractByte(msg, 8); packetUBXNAVPVT->data.min = extractByte(msg, 9); packetUBXNAVPVT->data.sec = extractByte(msg, 10); packetUBXNAVPVT->data.valid.all = extractByte(msg, 11); packetUBXNAVPVT->data.tAcc = extractLong(msg, 12); packetUBXNAVPVT->data.nano = extractSignedLong(msg, 16); // Includes milliseconds packetUBXNAVPVT->data.fixType = extractByte(msg, 20); packetUBXNAVPVT->data.flags.all = extractByte(msg, 21); packetUBXNAVPVT->data.flags2.all = extractByte(msg, 22); packetUBXNAVPVT->data.numSV = extractByte(msg, 23); packetUBXNAVPVT->data.lon = extractSignedLong(msg, 24); packetUBXNAVPVT->data.lat = extractSignedLong(msg, 28); packetUBXNAVPVT->data.height = extractSignedLong(msg, 32); packetUBXNAVPVT->data.hMSL = extractSignedLong(msg, 36); packetUBXNAVPVT->data.hAcc = extractLong(msg, 40); packetUBXNAVPVT->data.vAcc = extractLong(msg, 44); packetUBXNAVPVT->data.velN = extractSignedLong(msg, 48); packetUBXNAVPVT->data.velE = extractSignedLong(msg, 52); packetUBXNAVPVT->data.velD = extractSignedLong(msg, 56); packetUBXNAVPVT->data.gSpeed = extractSignedLong(msg, 60); packetUBXNAVPVT->data.headMot = extractSignedLong(msg, 64); packetUBXNAVPVT->data.sAcc = extractLong(msg, 68); packetUBXNAVPVT->data.headAcc = extractLong(msg, 72); packetUBXNAVPVT->data.pDOP = extractInt(msg, 76); packetUBXNAVPVT->data.flags3.all = extractByte(msg, 78); packetUBXNAVPVT->data.headVeh = extractSignedLong(msg, 84); packetUBXNAVPVT->data.magDec = extractSignedInt(msg, 88); packetUBXNAVPVT->data.magAcc = extractInt(msg, 90); // Mark all datums as fresh (not read before) packetUBXNAVPVT->moduleQueried.moduleQueried1.all = 0xFFFFFFFF; packetUBXNAVPVT->moduleQueried.moduleQueried2.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVPVT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVPVT->callbackData->iTOW, &packetUBXNAVPVT->data.iTOW, sizeof(UBX_NAV_PVT_data_t)); packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVPVT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_ODO && msg->len == UBX_NAV_ODO_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVODO != NULL) { packetUBXNAVODO->data.version = extractByte(msg, 0); packetUBXNAVODO->data.iTOW = extractLong(msg, 4); packetUBXNAVODO->data.distance = extractLong(msg, 8); packetUBXNAVODO->data.totalDistance = extractLong(msg, 12); packetUBXNAVODO->data.distanceStd = extractLong(msg, 16); // Mark all datums as fresh (not read before) packetUBXNAVODO->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVODO->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVODO->callbackData->version, &packetUBXNAVODO->data.version, sizeof(UBX_NAV_ODO_data_t)); packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVODO->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_VELECEF && msg->len == UBX_NAV_VELECEF_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVVELECEF != NULL) { packetUBXNAVVELECEF->data.iTOW = extractLong(msg, 0); packetUBXNAVVELECEF->data.ecefVX = extractSignedLong(msg, 4); packetUBXNAVVELECEF->data.ecefVY = extractSignedLong(msg, 8); packetUBXNAVVELECEF->data.ecefVZ = extractSignedLong(msg, 12); packetUBXNAVVELECEF->data.sAcc = extractLong(msg, 16); // Mark all datums as fresh (not read before) packetUBXNAVVELECEF->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVVELECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVVELECEF->callbackData->iTOW, &packetUBXNAVVELECEF->data.iTOW, sizeof(UBX_NAV_VELECEF_data_t)); packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVVELECEF->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_VELNED && msg->len == UBX_NAV_VELNED_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVVELNED != NULL) { packetUBXNAVVELNED->data.iTOW = extractLong(msg, 0); packetUBXNAVVELNED->data.velN = extractSignedLong(msg, 4); packetUBXNAVVELNED->data.velE = extractSignedLong(msg, 8); packetUBXNAVVELNED->data.velD = extractSignedLong(msg, 12); packetUBXNAVVELNED->data.speed = extractLong(msg, 16); packetUBXNAVVELNED->data.gSpeed = extractLong(msg, 20); packetUBXNAVVELNED->data.heading = extractSignedLong(msg, 24); packetUBXNAVVELNED->data.sAcc = extractLong(msg, 28); packetUBXNAVVELNED->data.cAcc = extractLong(msg, 32); // Mark all datums as fresh (not read before) packetUBXNAVVELNED->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVVELNED->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELNED->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVVELNED->callbackData->iTOW, &packetUBXNAVVELNED->data.iTOW, sizeof(UBX_NAV_VELNED_data_t)); packetUBXNAVVELNED->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVVELNED->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_HPPOSECEF && msg->len == UBX_NAV_HPPOSECEF_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVHPPOSECEF != NULL) { packetUBXNAVHPPOSECEF->data.version = extractByte(msg, 0); packetUBXNAVHPPOSECEF->data.iTOW = extractLong(msg, 4); packetUBXNAVHPPOSECEF->data.ecefX = extractSignedLong(msg, 8); packetUBXNAVHPPOSECEF->data.ecefY = extractSignedLong(msg, 12); packetUBXNAVHPPOSECEF->data.ecefZ = extractSignedLong(msg, 16); packetUBXNAVHPPOSECEF->data.ecefXHp = extractSignedChar(msg, 20); packetUBXNAVHPPOSECEF->data.ecefYHp = extractSignedChar(msg, 21); packetUBXNAVHPPOSECEF->data.ecefZHp = extractSignedChar(msg, 22); packetUBXNAVHPPOSECEF->data.flags.all = extractByte(msg, 23); packetUBXNAVHPPOSECEF->data.pAcc = extractLong(msg, 24); // Mark all datums as fresh (not read before) packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVHPPOSECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVHPPOSECEF->callbackData->version, &packetUBXNAVHPPOSECEF->data.version, sizeof(UBX_NAV_HPPOSECEF_data_t)); packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_HPPOSLLH && msg->len == UBX_NAV_HPPOSLLH_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVHPPOSLLH != NULL) { packetUBXNAVHPPOSLLH->data.version = extractByte(msg, 0); packetUBXNAVHPPOSLLH->data.flags.all = extractByte(msg, 3); packetUBXNAVHPPOSLLH->data.iTOW = extractLong(msg, 4); packetUBXNAVHPPOSLLH->data.lon = extractSignedLong(msg, 8); packetUBXNAVHPPOSLLH->data.lat = extractSignedLong(msg, 12); packetUBXNAVHPPOSLLH->data.height = extractSignedLong(msg, 16); packetUBXNAVHPPOSLLH->data.hMSL = extractSignedLong(msg, 20); packetUBXNAVHPPOSLLH->data.lonHp = extractSignedChar(msg, 24); packetUBXNAVHPPOSLLH->data.latHp = extractSignedChar(msg, 25); packetUBXNAVHPPOSLLH->data.heightHp = extractSignedChar(msg, 26); packetUBXNAVHPPOSLLH->data.hMSLHp = extractSignedChar(msg, 27); packetUBXNAVHPPOSLLH->data.hAcc = extractLong(msg, 28); packetUBXNAVHPPOSLLH->data.vAcc = extractLong(msg, 32); // Mark all datums as fresh (not read before) packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVHPPOSLLH->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVHPPOSLLH->callbackData->version, &packetUBXNAVHPPOSLLH->data.version, sizeof(UBX_NAV_HPPOSLLH_data_t)); packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_PVAT && msg->len == UBX_NAV_PVAT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVPVAT != NULL) { packetUBXNAVPVAT->data.iTOW = extractLong(msg, 0); packetUBXNAVPVAT->data.version = extractByte(msg, 4); packetUBXNAVPVAT->data.valid.all = extractByte(msg, 5); packetUBXNAVPVAT->data.year = extractInt(msg, 6); packetUBXNAVPVAT->data.month = extractByte(msg, 8); packetUBXNAVPVAT->data.day = extractByte(msg, 9); packetUBXNAVPVAT->data.hour = extractByte(msg, 10); packetUBXNAVPVAT->data.min = extractByte(msg, 11); packetUBXNAVPVAT->data.sec = extractByte(msg, 12); packetUBXNAVPVAT->data.tAcc = extractLong(msg, 16); packetUBXNAVPVAT->data.nano = extractSignedLong(msg, 20); // Includes milliseconds packetUBXNAVPVAT->data.fixType = extractByte(msg, 24); packetUBXNAVPVAT->data.flags.all = extractByte(msg, 25); packetUBXNAVPVAT->data.flags2.all = extractByte(msg, 26); packetUBXNAVPVAT->data.numSV = extractByte(msg, 27); packetUBXNAVPVAT->data.lon = extractSignedLong(msg, 28); packetUBXNAVPVAT->data.lat = extractSignedLong(msg, 32); packetUBXNAVPVAT->data.height = extractSignedLong(msg, 36); packetUBXNAVPVAT->data.hMSL = extractSignedLong(msg, 40); packetUBXNAVPVAT->data.hAcc = extractLong(msg, 44); packetUBXNAVPVAT->data.vAcc = extractLong(msg, 48); packetUBXNAVPVAT->data.velN = extractSignedLong(msg, 52); packetUBXNAVPVAT->data.velE = extractSignedLong(msg, 56); packetUBXNAVPVAT->data.velD = extractSignedLong(msg, 60); packetUBXNAVPVAT->data.gSpeed = extractSignedLong(msg, 64); packetUBXNAVPVAT->data.sAcc = extractLong(msg, 68); packetUBXNAVPVAT->data.vehRoll = extractSignedLong(msg, 72); packetUBXNAVPVAT->data.vehPitch = extractSignedLong(msg, 76); packetUBXNAVPVAT->data.vehHeading = extractSignedLong(msg, 80); packetUBXNAVPVAT->data.motHeading = extractSignedLong(msg, 84); packetUBXNAVPVAT->data.accRoll = extractInt(msg, 88); packetUBXNAVPVAT->data.accPitch = extractInt(msg, 90); packetUBXNAVPVAT->data.accHeading = extractInt(msg, 92); packetUBXNAVPVAT->data.magDec = extractSignedInt(msg, 94); packetUBXNAVPVAT->data.magAcc = extractInt(msg, 96); packetUBXNAVPVAT->data.errEllipseOrient = extractInt(msg, 98); packetUBXNAVPVAT->data.errEllipseMajor = extractLong(msg, 100); packetUBXNAVPVAT->data.errEllipseMinor = extractLong(msg, 104); // Mark all datums as fresh (not read before) packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0xFFFFFFFF; packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVPVAT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVPVAT->callbackData->iTOW, &packetUBXNAVPVAT->data.iTOW, sizeof(UBX_NAV_PVAT_data_t)); packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVPVAT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_TIMEUTC && msg->len == UBX_NAV_TIMEUTC_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVTIMEUTC != NULL) { packetUBXNAVTIMEUTC->data.iTOW = extractLong(msg, 0); packetUBXNAVTIMEUTC->data.tAcc = extractLong(msg, 4); packetUBXNAVTIMEUTC->data.nano = extractSignedLong(msg, 8); packetUBXNAVTIMEUTC->data.year = extractInt(msg, 12); packetUBXNAVTIMEUTC->data.month = extractByte(msg, 14); packetUBXNAVTIMEUTC->data.day = extractByte(msg, 15); packetUBXNAVTIMEUTC->data.hour = extractByte(msg, 16); packetUBXNAVTIMEUTC->data.min = extractByte(msg, 17); packetUBXNAVTIMEUTC->data.sec = extractByte(msg, 18); packetUBXNAVTIMEUTC->data.valid.all = extractByte(msg, 19); // Mark all datums as fresh (not read before) packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVTIMEUTC->callbackData->iTOW, &packetUBXNAVTIMEUTC->data.iTOW, sizeof(UBX_NAV_TIMEUTC_data_t)); packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_CLOCK && msg->len == UBX_NAV_CLOCK_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVCLOCK != NULL) { packetUBXNAVCLOCK->data.iTOW = extractLong(msg, 0); packetUBXNAVCLOCK->data.clkB = extractSignedLong(msg, 4); packetUBXNAVCLOCK->data.clkD = extractSignedLong(msg, 8); packetUBXNAVCLOCK->data.tAcc = extractLong(msg, 12); packetUBXNAVCLOCK->data.fAcc = extractLong(msg, 16); // Mark all datums as fresh (not read before) packetUBXNAVCLOCK->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVCLOCK->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVCLOCK->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVCLOCK->callbackData->iTOW, &packetUBXNAVCLOCK->data.iTOW, sizeof(UBX_NAV_CLOCK_data_t)); packetUBXNAVCLOCK->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVCLOCK->automaticFlags.flags.bits.addToFileBuffer) { storePacket(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.all = 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 if (packetUBXNAVSVIN != NULL) { packetUBXNAVSVIN->data.version = extractByte(msg, 0); packetUBXNAVSVIN->data.iTOW = extractLong(msg, 4); packetUBXNAVSVIN->data.dur = extractLong(msg, 8); packetUBXNAVSVIN->data.meanX = extractSignedLong(msg, 12); packetUBXNAVSVIN->data.meanY = extractSignedLong(msg, 16); packetUBXNAVSVIN->data.meanZ = extractSignedLong(msg, 20); packetUBXNAVSVIN->data.meanXHP = extractSignedChar(msg, 24); packetUBXNAVSVIN->data.meanYHP = extractSignedChar(msg, 25); packetUBXNAVSVIN->data.meanZHP = extractSignedChar(msg, 26); packetUBXNAVSVIN->data.meanAcc = extractLong(msg, 28); packetUBXNAVSVIN->data.obs = extractLong(msg, 32); packetUBXNAVSVIN->data.valid = extractSignedChar(msg, 36); packetUBXNAVSVIN->data.active = extractSignedChar(msg, 37); // Mark all datums as fresh (not read before) packetUBXNAVSVIN->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVSVIN->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSVIN->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVSVIN->callbackData->version, &packetUBXNAVSVIN->data.version, sizeof(UBX_NAV_SVIN_data_t)); packetUBXNAVSVIN->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVSVIN->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_SAT) // Note: length is variable { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVSAT != NULL) { packetUBXNAVSAT->data.header.iTOW = extractLong(msg, 0); packetUBXNAVSAT->data.header.version = extractByte(msg, 4); packetUBXNAVSAT->data.header.numSvs = extractByte(msg, 5); // The NAV SAT message could contain data for 255 SVs max. (numSvs is uint8_t. UBX_NAV_SAT_MAX_BLOCKS is 255) for (uint16_t i = 0; (i < UBX_NAV_SAT_MAX_BLOCKS) && (i < ((uint16_t)packetUBXNAVSAT->data.header.numSvs)) && ((i * 12) < (msg->len - 8)); i++) { uint16_t offset = (i * 12) + 8; packetUBXNAVSAT->data.blocks[i].gnssId = extractByte(msg, offset + 0); packetUBXNAVSAT->data.blocks[i].svId = extractByte(msg, offset + 1); packetUBXNAVSAT->data.blocks[i].cno = extractByte(msg, offset + 2); packetUBXNAVSAT->data.blocks[i].elev = extractSignedChar(msg, offset + 3); packetUBXNAVSAT->data.blocks[i].azim = extractSignedInt(msg, offset + 4); packetUBXNAVSAT->data.blocks[i].prRes = extractSignedInt(msg, offset + 6); packetUBXNAVSAT->data.blocks[i].flags.all = extractLong(msg, offset + 8); } // Mark all datums as fresh (not read before) packetUBXNAVSAT->moduleQueried = true; // Check if we need to copy the data for the callback if ((packetUBXNAVSAT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSAT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVSAT->callbackData->header.iTOW, &packetUBXNAVSAT->data.header.iTOW, sizeof(UBX_NAV_SAT_data_t)); packetUBXNAVSAT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVSAT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_RELPOSNED && ((msg->len == UBX_NAV_RELPOSNED_LEN) || (msg->len == UBX_NAV_RELPOSNED_LEN_F9))) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVRELPOSNED != NULL) { // Note: // RELPOSNED on the M8 is only 40 bytes long // RELPOSNED on the F9 is 64 bytes long and contains much more information packetUBXNAVRELPOSNED->data.version = extractByte(msg, 0); packetUBXNAVRELPOSNED->data.refStationId = extractInt(msg, 2); packetUBXNAVRELPOSNED->data.iTOW = extractLong(msg, 4); packetUBXNAVRELPOSNED->data.relPosN = extractSignedLong(msg, 8); packetUBXNAVRELPOSNED->data.relPosE = extractSignedLong(msg, 12); packetUBXNAVRELPOSNED->data.relPosD = extractSignedLong(msg, 16); if (msg->len == UBX_NAV_RELPOSNED_LEN) { // The M8 version does not contain relPosLength or relPosHeading packetUBXNAVRELPOSNED->data.relPosLength = 0; packetUBXNAVRELPOSNED->data.relPosHeading = 0; packetUBXNAVRELPOSNED->data.relPosHPN = extractSignedChar(msg, 20); packetUBXNAVRELPOSNED->data.relPosHPE = extractSignedChar(msg, 21); packetUBXNAVRELPOSNED->data.relPosHPD = extractSignedChar(msg, 22); packetUBXNAVRELPOSNED->data.relPosHPLength = 0; // The M8 version does not contain relPosHPLength packetUBXNAVRELPOSNED->data.accN = extractLong(msg, 24); packetUBXNAVRELPOSNED->data.accE = extractLong(msg, 28); packetUBXNAVRELPOSNED->data.accD = extractLong(msg, 32); // The M8 version does not contain accLength or accHeading packetUBXNAVRELPOSNED->data.accLength = 0; packetUBXNAVRELPOSNED->data.accHeading = 0; packetUBXNAVRELPOSNED->data.flags.all = extractLong(msg, 36); } else { packetUBXNAVRELPOSNED->data.relPosLength = extractSignedLong(msg, 20); packetUBXNAVRELPOSNED->data.relPosHeading = extractSignedLong(msg, 24); packetUBXNAVRELPOSNED->data.relPosHPN = extractSignedChar(msg, 32); packetUBXNAVRELPOSNED->data.relPosHPE = extractSignedChar(msg, 33); packetUBXNAVRELPOSNED->data.relPosHPD = extractSignedChar(msg, 34); packetUBXNAVRELPOSNED->data.relPosHPLength = extractSignedChar(msg, 35); packetUBXNAVRELPOSNED->data.accN = extractLong(msg, 36); packetUBXNAVRELPOSNED->data.accE = extractLong(msg, 40); packetUBXNAVRELPOSNED->data.accD = extractLong(msg, 44); packetUBXNAVRELPOSNED->data.accLength = extractLong(msg, 48); packetUBXNAVRELPOSNED->data.accHeading = extractLong(msg, 52); packetUBXNAVRELPOSNED->data.flags.all = extractLong(msg, 60); } // Mark all datums as fresh (not read before) packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVRELPOSNED->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVRELPOSNED->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVRELPOSNED->callbackData->version, &packetUBXNAVRELPOSNED->data.version, sizeof(UBX_NAV_RELPOSNED_data_t)); packetUBXNAVRELPOSNED->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVRELPOSNED->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_AOPSTATUS && msg->len == UBX_NAV_AOPSTATUS_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVAOPSTATUS != NULL) { packetUBXNAVAOPSTATUS->data.iTOW = extractLong(msg, 0); packetUBXNAVAOPSTATUS->data.aopCfg.all = extractByte(msg, 4); packetUBXNAVAOPSTATUS->data.status = extractByte(msg, 5); // Mark all datums as fresh (not read before) packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVAOPSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVAOPSTATUS->callbackData->iTOW, &packetUBXNAVAOPSTATUS->data.iTOW, sizeof(UBX_NAV_AOPSTATUS_data_t)); packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_NAV_EOE && msg->len == UBX_NAV_EOE_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXNAVEOE != NULL) { packetUBXNAVEOE->data.iTOW = extractLong(msg, 0); // Mark all datums as fresh (not read before) packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXNAVEOE->callbackData->iTOW, &packetUBXNAVEOE->data.iTOW, sizeof(UBX_NAV_EOE_data_t)); packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } break; case UBX_CLASS_RXM: if (msg->id == UBX_RXM_PMP) // Note: length is variable with version 0x01 // Note: the field positions depend on the version { // Parse various byte fields into storage - but only if we have memory allocated for it. // By default, new PMP data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). // To prevent this, uncomment the line two lines below if ((packetUBXRXMPMP != NULL) && (packetUBXRXMPMP->callbackData != NULL) //&& (packetUBXRXMPMP->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' ) { packetUBXRXMPMP->callbackData->version = extractByte(msg, 0); packetUBXRXMPMP->callbackData->numBytesUserData = extractInt(msg, 2); packetUBXRXMPMP->callbackData->timeTag = extractLong(msg, 4); packetUBXRXMPMP->callbackData->uniqueWord[0] = extractLong(msg, 8); packetUBXRXMPMP->callbackData->uniqueWord[1] = extractLong(msg, 12); packetUBXRXMPMP->callbackData->serviceIdentifier = extractInt(msg, 16); packetUBXRXMPMP->callbackData->spare = extractByte(msg, 18); packetUBXRXMPMP->callbackData->uniqueWordBitErrors = extractByte(msg, 19); if (packetUBXRXMPMP->callbackData->version == 0x00) { packetUBXRXMPMP->callbackData->fecBits = extractInt(msg, 524); packetUBXRXMPMP->callbackData->ebno = extractByte(msg, 526); } else // if (packetUBXRXMPMP->data.version == 0x01) { packetUBXRXMPMP->callbackData->fecBits = extractInt(msg, 20); packetUBXRXMPMP->callbackData->ebno = extractByte(msg, 22); } uint16_t userDataStart = (packetUBXRXMPMP->callbackData->version == 0x00) ? 20 : 24; uint16_t userDataLength = (packetUBXRXMPMP->callbackData->version == 0x00) ? 504 : (packetUBXRXMPMP->callbackData->numBytesUserData); for (uint16_t i = 0; (i < userDataLength) && (i < 504); i++) { packetUBXRXMPMP->callbackData->userData[i] = extractByte(msg, i + userDataStart); } packetUBXRXMPMP->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } // Full PMP message, including Class, ID and checksum // By default, new PMP data will always overwrite 'old' data (data which is valid but which has not yet been read by the callback). // To prevent this, uncomment the line two lines below if ((packetUBXRXMPMPmessage != NULL) && (packetUBXRXMPMPmessage->callbackData != NULL) //&& (packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' ) { packetUBXRXMPMPmessage->callbackData->sync1 = UBX_SYNCH_1; packetUBXRXMPMPmessage->callbackData->sync2 = UBX_SYNCH_2; packetUBXRXMPMPmessage->callbackData->cls = UBX_CLASS_RXM; packetUBXRXMPMPmessage->callbackData->ID = UBX_RXM_PMP; packetUBXRXMPMPmessage->callbackData->lengthLSB = msg->len & 0xFF; packetUBXRXMPMPmessage->callbackData->lengthMSB = msg->len >> 8; memcpy(packetUBXRXMPMPmessage->callbackData->payload, msg->payload, msg->len); packetUBXRXMPMPmessage->callbackData->checksumA = msg->checksumA; packetUBXRXMPMPmessage->callbackData->checksumB = msg->checksumB; packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } } else if (msg->id == UBX_RXM_QZSSL6) // Note: length is variable with version 0x01 // Note: the field positions depend on the version { // Full QZSSL6 message, including Class, ID and checksum for (int ch = 0; ch < UBX_RXM_QZSSL6_NUM_CHANNELS; ch++) { if (0 == (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1 << ch))) { packetUBXRXMQZSSL6message->callbackData[ch].sync1 = UBX_SYNCH_1; packetUBXRXMQZSSL6message->callbackData[ch].sync2 = UBX_SYNCH_2; packetUBXRXMQZSSL6message->callbackData[ch].cls = UBX_CLASS_RXM; packetUBXRXMQZSSL6message->callbackData[ch].ID = UBX_RXM_QZSSL6; packetUBXRXMQZSSL6message->callbackData[ch].lengthLSB = msg->len & 0xFF; packetUBXRXMQZSSL6message->callbackData[ch].lengthMSB = msg->len >> 8; memcpy(packetUBXRXMQZSSL6message->callbackData[ch].payload, msg->payload, msg->len); packetUBXRXMQZSSL6message->callbackData[ch].checksumA = msg->checksumA; packetUBXRXMQZSSL6message->callbackData[ch].checksumB = msg->checksumB; packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid |= (1 << ch); break; // abort when added } } } else if (msg->id == UBX_RXM_COR) { // Parse various byte fields into storage - but only if we have memory allocated for it if ((packetUBXRXMCOR != NULL) && (packetUBXRXMCOR->callbackData != NULL) //&& (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' ) { packetUBXRXMCOR->callbackData->version = extractByte(msg, 0); packetUBXRXMCOR->callbackData->ebno = extractByte(msg, 1); packetUBXRXMCOR->callbackData->statusInfo.all = extractLong(msg, 4); packetUBXRXMCOR->callbackData->msgType = extractInt(msg, 8); packetUBXRXMCOR->callbackData->msgSubType = extractInt(msg, 10); packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } } else if (msg->id == UBX_RXM_SFRBX) // Note: length is variable // Note: on protocol version 17: numWords is (0..16) // on protocol version 18+: numWords is (0..10) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXRXMSFRBX != NULL) { packetUBXRXMSFRBX->data.gnssId = extractByte(msg, 0); packetUBXRXMSFRBX->data.svId = extractByte(msg, 1); packetUBXRXMSFRBX->data.freqId = extractByte(msg, 3); packetUBXRXMSFRBX->data.numWords = extractByte(msg, 4); packetUBXRXMSFRBX->data.chn = extractByte(msg, 5); packetUBXRXMSFRBX->data.version = extractByte(msg, 6); for (uint8_t i = 0; (i < UBX_RXM_SFRBX_MAX_WORDS) && (i < packetUBXRXMSFRBX->data.numWords) && ((i * 4) < (msg->len - 8)); i++) { packetUBXRXMSFRBX->data.dwrd[i] = extractLong(msg, 8 + (i * 4)); } // Mark all datums as fresh (not read before) packetUBXRXMSFRBX->moduleQueried = true; // Check if we need to copy the data for the callback if ((packetUBXRXMSFRBX->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXRXMSFRBX->callbackData->gnssId, &packetUBXRXMSFRBX->data.gnssId, sizeof(UBX_RXM_SFRBX_data_t)); packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXRXMSFRBX->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_RXM_RAWX) // Note: length is variable { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXRXMRAWX != NULL) { for (uint8_t i = 0; i < 8; i++) { packetUBXRXMRAWX->data.header.rcvTow[i] = extractByte(msg, i); } packetUBXRXMRAWX->data.header.week = extractInt(msg, 8); packetUBXRXMRAWX->data.header.leapS = extractSignedChar(msg, 10); packetUBXRXMRAWX->data.header.numMeas = extractByte(msg, 11); packetUBXRXMRAWX->data.header.recStat.all = extractByte(msg, 12); packetUBXRXMRAWX->data.header.version = extractByte(msg, 13); for (uint8_t i = 0; (i < UBX_RXM_RAWX_MAX_BLOCKS) && (i < packetUBXRXMRAWX->data.header.numMeas) && ((((uint16_t)i) * 32) < (msg->len - 16)); i++) { uint16_t offset = (((uint16_t)i) * 32) + 16; for (uint8_t j = 0; j < 8; j++) { packetUBXRXMRAWX->data.blocks[i].prMes[j] = extractByte(msg, offset + j); packetUBXRXMRAWX->data.blocks[i].cpMes[j] = extractByte(msg, offset + 8 + j); if (j < 4) packetUBXRXMRAWX->data.blocks[i].doMes[j] = extractByte(msg, offset + 16 + j); } packetUBXRXMRAWX->data.blocks[i].gnssId = extractByte(msg, offset + 20); packetUBXRXMRAWX->data.blocks[i].svId = extractByte(msg, offset + 21); packetUBXRXMRAWX->data.blocks[i].sigId = extractByte(msg, offset + 22); packetUBXRXMRAWX->data.blocks[i].freqId = extractByte(msg, offset + 23); packetUBXRXMRAWX->data.blocks[i].lockTime = extractInt(msg, offset + 24); packetUBXRXMRAWX->data.blocks[i].cno = extractByte(msg, offset + 26); packetUBXRXMRAWX->data.blocks[i].prStdev = extractByte(msg, offset + 27); packetUBXRXMRAWX->data.blocks[i].cpStdev = extractByte(msg, offset + 28); packetUBXRXMRAWX->data.blocks[i].doStdev = extractByte(msg, offset + 29); packetUBXRXMRAWX->data.blocks[i].trkStat.all = extractByte(msg, offset + 30); } // Mark all datums as fresh (not read before) packetUBXRXMRAWX->moduleQueried = true; // Check if we need to copy the data for the callback if ((packetUBXRXMRAWX->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMRAWX->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXRXMRAWX->callbackData->header.rcvTow[0], &packetUBXRXMRAWX->data.header.rcvTow[0], sizeof(UBX_RXM_RAWX_data_t)); packetUBXRXMRAWX->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXRXMRAWX->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } break; case UBX_CLASS_CFG: if (msg->id == UBX_CFG_PRT && msg->len == UBX_CFG_PRT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXCFGPRT != NULL) { packetUBXCFGPRT->data.portID = extractByte(msg, 0); packetUBXCFGPRT->data.txReady.all = extractInt(msg, 2); packetUBXCFGPRT->data.mode = extractLong(msg, 4); packetUBXCFGPRT->data.baudRate = extractLong(msg, 8); packetUBXCFGPRT->data.inProtoMask.all = extractInt(msg, 12); packetUBXCFGPRT->data.outProtoMask.all = extractInt(msg, 14); packetUBXCFGPRT->data.flags = extractInt(msg, 16); // Mark data as valid packetUBXCFGPRT->dataValid = true; } } else if (msg->id == UBX_CFG_RATE && msg->len == UBX_CFG_RATE_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXCFGRATE != NULL) { packetUBXCFGRATE->data.measRate = extractInt(msg, 0); packetUBXCFGRATE->data.navRate = extractInt(msg, 2); packetUBXCFGRATE->data.timeRef = extractInt(msg, 4); // Mark all datums as fresh (not read before) packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0xFFFFFFFF; } } break; case UBX_CLASS_TIM: if (msg->id == UBX_TIM_TM2 && msg->len == UBX_TIM_TM2_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXTIMTM2 != NULL) { packetUBXTIMTM2->data.ch = extractByte(msg, 0); packetUBXTIMTM2->data.flags.all = extractByte(msg, 1); packetUBXTIMTM2->data.count = extractInt(msg, 2); packetUBXTIMTM2->data.wnR = extractInt(msg, 4); packetUBXTIMTM2->data.wnF = extractInt(msg, 6); packetUBXTIMTM2->data.towMsR = extractLong(msg, 8); packetUBXTIMTM2->data.towSubMsR = extractLong(msg, 12); packetUBXTIMTM2->data.towMsF = extractLong(msg, 16); packetUBXTIMTM2->data.towSubMsF = extractLong(msg, 20); packetUBXTIMTM2->data.accEst = extractLong(msg, 24); // Mark all datums as fresh (not read before) packetUBXTIMTM2->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXTIMTM2->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXTIMTM2->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXTIMTM2->callbackData->ch, &packetUBXTIMTM2->data.ch, sizeof(UBX_TIM_TM2_data_t)); packetUBXTIMTM2->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXTIMTM2->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } break; case UBX_CLASS_ESF: if (msg->id == UBX_ESF_ALG && msg->len == UBX_ESF_ALG_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXESFALG != NULL) { packetUBXESFALG->data.iTOW = extractLong(msg, 0); packetUBXESFALG->data.version = extractByte(msg, 4); packetUBXESFALG->data.flags.all = extractByte(msg, 5); packetUBXESFALG->data.error.all = extractByte(msg, 6); packetUBXESFALG->data.yaw = extractLong(msg, 8); packetUBXESFALG->data.pitch = extractSignedInt(msg, 12); packetUBXESFALG->data.roll = extractSignedInt(msg, 14); // Mark all datums as fresh (not read before) packetUBXESFALG->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXESFALG->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFALG->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXESFALG->callbackData->iTOW, &packetUBXESFALG->data.iTOW, sizeof(UBX_ESF_ALG_data_t)); packetUBXESFALG->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXESFALG->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_ESF_INS && msg->len == UBX_ESF_INS_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXESFINS != NULL) { packetUBXESFINS->data.bitfield0.all = extractLong(msg, 0); packetUBXESFINS->data.iTOW = extractLong(msg, 8); packetUBXESFINS->data.xAngRate = extractSignedLong(msg, 12); packetUBXESFINS->data.yAngRate = extractSignedLong(msg, 16); packetUBXESFINS->data.zAngRate = extractSignedLong(msg, 20); packetUBXESFINS->data.xAccel = extractSignedLong(msg, 24); packetUBXESFINS->data.yAccel = extractSignedLong(msg, 28); packetUBXESFINS->data.zAccel = extractSignedLong(msg, 32); // Mark all datums as fresh (not read before) packetUBXESFINS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXESFINS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFINS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXESFINS->callbackData->bitfield0.all, &packetUBXESFINS->data.bitfield0.all, sizeof(UBX_ESF_INS_data_t)); packetUBXESFINS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXESFINS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_ESF_MEAS) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXESFMEAS != NULL) { packetUBXESFMEAS->data.timeTag = extractLong(msg, 0); packetUBXESFMEAS->data.flags.all = extractInt(msg, 4); packetUBXESFMEAS->data.id = extractInt(msg, 6); for (uint16_t i = 0; (i < DEF_MAX_NUM_ESF_MEAS) && (i < packetUBXESFMEAS->data.flags.bits.numMeas) && ((i * 4) < (msg->len - 8)); i++) { packetUBXESFMEAS->data.data[i].data.all = extractLong(msg, 8 + (i * 4)); } if ((uint16_t)msg->len > (uint16_t)(8 + (packetUBXESFMEAS->data.flags.bits.numMeas * 4))) packetUBXESFMEAS->data.calibTtag = extractLong(msg, 8 + (packetUBXESFMEAS->data.flags.bits.numMeas * 4)); // Check if we need to copy the data for the callback if ((packetUBXESFMEAS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXESFMEAS->callbackData->timeTag, &packetUBXESFMEAS->data.timeTag, sizeof(UBX_ESF_MEAS_data_t)); packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXESFMEAS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_ESF_RAW) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXESFRAW != NULL) { packetUBXESFRAW->data.numEsfRawBlocks = (msg->len - 4) / 8; // Record how many blocks were received. Could be 7 or 70 (ZED-F9R vs. NEO-M8U) for (uint16_t i = 0; (i < (DEF_NUM_SENS * DEF_MAX_NUM_ESF_RAW_REPEATS)) && ((i * 8) < (msg->len - 4)); i++) { packetUBXESFRAW->data.data[i].data.all = extractLong(msg, 4 + (i * 8)); packetUBXESFRAW->data.data[i].sTag = extractLong(msg, 8 + (i * 8)); } // Check if we need to copy the data for the callback if ((packetUBXESFRAW->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXESFRAW->callbackData->data[0].data.all, &packetUBXESFRAW->data.data[0].data.all, sizeof(UBX_ESF_RAW_data_t)); packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXESFRAW->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_ESF_STATUS) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXESFSTATUS != NULL) { packetUBXESFSTATUS->data.iTOW = extractLong(msg, 0); packetUBXESFSTATUS->data.version = extractByte(msg, 4); packetUBXESFSTATUS->data.fusionMode = extractByte(msg, 12); packetUBXESFSTATUS->data.numSens = extractByte(msg, 15); for (uint16_t i = 0; (i < DEF_NUM_SENS) && (i < packetUBXESFSTATUS->data.numSens) && ((i * 4) < (msg->len - 16)); i++) { packetUBXESFSTATUS->data.status[i].sensStatus1.all = extractByte(msg, 16 + (i * 4) + 0); packetUBXESFSTATUS->data.status[i].sensStatus2.all = extractByte(msg, 16 + (i * 4) + 1); packetUBXESFSTATUS->data.status[i].freq = extractByte(msg, 16 + (i * 4) + 2); packetUBXESFSTATUS->data.status[i].faults.all = extractByte(msg, 16 + (i * 4) + 3); } // Mark all datums as fresh (not read before) packetUBXESFSTATUS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXESFSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFSTATUS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXESFSTATUS->callbackData->iTOW, &packetUBXESFSTATUS->data.iTOW, sizeof(UBX_ESF_STATUS_data_t)); packetUBXESFSTATUS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXESFSTATUS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } break; case UBX_CLASS_MGA: if (msg->id == UBX_MGA_ACK_DATA0 && msg->len == UBX_MGA_ACK_DATA0_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXMGAACK != NULL) { // Calculate how many ACKs are already stored in the ring buffer uint8_t ackBufferContains; if (packetUBXMGAACK->head >= packetUBXMGAACK->tail) // Check if wrap-around has occurred { // Wrap-around has not occurred so do a simple subtraction ackBufferContains = packetUBXMGAACK->head - packetUBXMGAACK->tail; } else { // Wrap-around has occurred so do a simple subtraction but add in the buffer length (UBX_MGA_ACK_RINGBUFFER_LEN) ackBufferContains = ((uint8_t)(((uint16_t)packetUBXMGAACK->head + (uint16_t)UBX_MGA_ACK_DATA0_RINGBUFFER_LEN) - (uint16_t)packetUBXMGAACK->tail)); } // Have we got space to store this ACK? if (ackBufferContains < (UBX_MGA_ACK_DATA0_RINGBUFFER_LEN - 1)) { // Yes, we have, so store it packetUBXMGAACK->data[packetUBXMGAACK->head].type = extractByte(msg, 0); packetUBXMGAACK->data[packetUBXMGAACK->head].version = extractByte(msg, 1); packetUBXMGAACK->data[packetUBXMGAACK->head].infoCode = extractByte(msg, 2); packetUBXMGAACK->data[packetUBXMGAACK->head].msgId = extractByte(msg, 3); packetUBXMGAACK->data[packetUBXMGAACK->head].msgPayloadStart[0] = extractByte(msg, 4); packetUBXMGAACK->data[packetUBXMGAACK->head].msgPayloadStart[1] = extractByte(msg, 5); packetUBXMGAACK->data[packetUBXMGAACK->head].msgPayloadStart[2] = extractByte(msg, 6); packetUBXMGAACK->data[packetUBXMGAACK->head].msgPayloadStart[3] = extractByte(msg, 7); // Increment the head packetUBXMGAACK->head++; if (packetUBXMGAACK->head == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN) packetUBXMGAACK->head = 0; } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("processUBXpacket: packetUBXMGAACK is full. ACK will be lost!")); } #endif } } } else if (msg->id == UBX_MGA_DBD && msg->len <= UBX_MGA_DBD_LEN) // Message length may be less than UBX_MGA_DBD_LEN. UBX_MGA_DBD_LEN is the maximum it will be. { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXMGADBD != NULL) { // Calculate how many DBDs are already stored in the ring buffer uint8_t dbdBufferContains; if (packetUBXMGADBD->head >= packetUBXMGADBD->tail) // Check if wrap-around has occurred { // Wrap-around has not occurred so do a simple subtraction dbdBufferContains = packetUBXMGADBD->head - packetUBXMGADBD->tail; } else { // Wrap-around has occurred so do a simple subtraction but add in the buffer length (UBX_MGA_DBD_RINGBUFFER_LEN) dbdBufferContains = ((uint8_t)(((uint16_t)packetUBXMGADBD->head + (uint16_t)UBX_MGA_DBD_RINGBUFFER_LEN) - (uint16_t)packetUBXMGADBD->tail)); } // Have we got space to store this DBD? if (dbdBufferContains < (UBX_MGA_DBD_RINGBUFFER_LEN - 1)) { // Yes, we have, so store it // We need to save the entire message - header, payload and checksum packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryHeader1 = UBX_SYNCH_1; packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryHeader2 = UBX_SYNCH_2; packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryClass = UBX_CLASS_MGA; packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryID = UBX_MGA_DBD; packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryLenLSB = (uint8_t)(msg->len & 0xFF); // We need to store the length of the DBD entry. The entry itself does not contain a length... packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryLenMSB = (uint8_t)((msg->len >> 8) & 0xFF); for (uint16_t i = 0; i < msg->len; i++) { packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntry[i] = extractByte(msg, i); } packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryChecksumA = msg->checksumA; packetUBXMGADBD->data[packetUBXMGADBD->head].dbdEntryChecksumB = msg->checksumB; // Increment the head packetUBXMGADBD->head++; if (packetUBXMGADBD->head == UBX_MGA_DBD_RINGBUFFER_LEN) packetUBXMGADBD->head = 0; } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("processUBXpacket: packetUBXMGADBD is full. DBD data will be lost!")); } #endif } } } break; case UBX_CLASS_HNR: if (msg->id == UBX_HNR_PVT && msg->len == UBX_HNR_PVT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXHNRPVT != NULL) { packetUBXHNRPVT->data.iTOW = extractLong(msg, 0); packetUBXHNRPVT->data.year = extractInt(msg, 4); packetUBXHNRPVT->data.month = extractByte(msg, 6); packetUBXHNRPVT->data.day = extractByte(msg, 7); packetUBXHNRPVT->data.hour = extractByte(msg, 8); packetUBXHNRPVT->data.min = extractByte(msg, 9); packetUBXHNRPVT->data.sec = extractByte(msg, 10); packetUBXHNRPVT->data.valid.all = extractByte(msg, 11); packetUBXHNRPVT->data.nano = extractSignedLong(msg, 12); packetUBXHNRPVT->data.gpsFix = extractByte(msg, 16); packetUBXHNRPVT->data.flags.all = extractByte(msg, 17); packetUBXHNRPVT->data.lon = extractSignedLong(msg, 20); packetUBXHNRPVT->data.lat = extractSignedLong(msg, 24); packetUBXHNRPVT->data.height = extractSignedLong(msg, 28); packetUBXHNRPVT->data.hMSL = extractSignedLong(msg, 32); packetUBXHNRPVT->data.gSpeed = extractSignedLong(msg, 36); packetUBXHNRPVT->data.speed = extractSignedLong(msg, 40); packetUBXHNRPVT->data.headMot = extractSignedLong(msg, 44); packetUBXHNRPVT->data.headVeh = extractSignedLong(msg, 48); packetUBXHNRPVT->data.hAcc = extractLong(msg, 52); packetUBXHNRPVT->data.vAcc = extractLong(msg, 56); packetUBXHNRPVT->data.sAcc = extractLong(msg, 60); packetUBXHNRPVT->data.headAcc = extractLong(msg, 64); // Mark all datums as fresh (not read before) packetUBXHNRPVT->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXHNRPVT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXHNRPVT->callbackData->iTOW, &packetUBXHNRPVT->data.iTOW, sizeof(UBX_HNR_PVT_data_t)); packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXHNRPVT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_HNR_ATT && msg->len == UBX_HNR_ATT_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXHNRATT != NULL) { packetUBXHNRATT->data.iTOW = extractLong(msg, 0); packetUBXHNRATT->data.version = extractByte(msg, 4); packetUBXHNRATT->data.roll = extractSignedLong(msg, 8); packetUBXHNRATT->data.pitch = extractSignedLong(msg, 12); packetUBXHNRATT->data.heading = extractSignedLong(msg, 16); packetUBXHNRATT->data.accRoll = extractLong(msg, 20); packetUBXHNRATT->data.accPitch = extractLong(msg, 24); packetUBXHNRATT->data.accHeading = extractLong(msg, 28); // Mark all datums as fresh (not read before) packetUBXHNRATT->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXHNRATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRATT->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXHNRATT->callbackData->iTOW, &packetUBXHNRATT->data.iTOW, sizeof(UBX_HNR_ATT_data_t)); packetUBXHNRATT->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXHNRATT->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } else if (msg->id == UBX_HNR_INS && msg->len == UBX_HNR_INS_LEN) { // Parse various byte fields into storage - but only if we have memory allocated for it if (packetUBXHNRINS != NULL) { packetUBXHNRINS->data.bitfield0.all = extractLong(msg, 0); packetUBXHNRINS->data.iTOW = extractLong(msg, 8); packetUBXHNRINS->data.xAngRate = extractSignedLong(msg, 12); packetUBXHNRINS->data.yAngRate = extractSignedLong(msg, 16); packetUBXHNRINS->data.zAngRate = extractSignedLong(msg, 20); packetUBXHNRINS->data.xAccel = extractSignedLong(msg, 24); packetUBXHNRINS->data.yAccel = extractSignedLong(msg, 28); packetUBXHNRINS->data.zAccel = extractSignedLong(msg, 32); // Mark all datums as fresh (not read before) packetUBXHNRINS->moduleQueried.moduleQueried.all = 0xFFFFFFFF; // Check if we need to copy the data for the callback if ((packetUBXHNRINS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRINS->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale { memcpy(&packetUBXHNRINS->callbackData->bitfield0.all, &packetUBXHNRINS->data.bitfield0.all, sizeof(UBX_HNR_INS_data_t)); packetUBXHNRINS->automaticFlags.flags.bits.callbackCopyValid = true; } // Check if we need to copy the data into the file buffer if (packetUBXHNRINS->automaticFlags.flags.bits.addToFileBuffer) { storePacket(msg); } } } break; } } // Given a message, calc and store the two byte "8-Bit Fletcher" checksum over the entirety of the message // This is called before we send a command message void SFE_UBLOX_GNSS::calcChecksum(ubxPacket *msg) { msg->checksumA = 0; msg->checksumB = 0; msg->checksumA += msg->cls; msg->checksumB += msg->checksumA; msg->checksumA += msg->id; msg->checksumB += msg->checksumA; msg->checksumA += (msg->len & 0xFF); msg->checksumB += msg->checksumA; msg->checksumA += (msg->len >> 8); msg->checksumB += msg->checksumA; for (uint16_t i = 0; i < msg->len; i++) { msg->checksumA += msg->payload[i]; msg->checksumB += msg->checksumA; } } // Given a message and a byte, add to rolling "8-Bit Fletcher" checksum // This is used when receiving messages from module void SFE_UBLOX_GNSS::addToChecksum(uint8_t incoming) { rollingChecksumA += incoming; rollingChecksumB += rollingChecksumA; } // Given a packet and payload, send everything including CRC bytes via I2C port sfe_ublox_status_e SFE_UBLOX_GNSS::sendCommand(ubxPacket *outgoingUBX, uint16_t maxWait, bool expectACKonly) { sfe_ublox_status_e retVal = SFE_UBLOX_STATUS_SUCCESS; calcChecksum(outgoingUBX); // Sets checksum A and B bytes of the packet #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("\nSending: ")); printPacket(outgoingUBX, true); // Always print payload } #endif if (commType == COMM_TYPE_I2C) { retVal = sendI2cCommand(outgoingUBX, maxWait); if (retVal != SFE_UBLOX_STATUS_SUCCESS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("Send I2C Command failed")); } #endif return retVal; } } else if (commType == COMM_TYPE_SERIAL) { sendSerialCommand(outgoingUBX); } else if (commType == COMM_TYPE_SPI) { sendSpiCommand(outgoingUBX); } if (maxWait > 0) { // Depending on what we just sent, either we need to look for an ACK or not if ((outgoingUBX->cls == UBX_CLASS_CFG) || (expectACKonly == true)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("sendCommand: Waiting for ACK response")); } #endif retVal = waitForACKResponse(outgoingUBX, outgoingUBX->cls, outgoingUBX->id, maxWait); // Wait for Ack response } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("sendCommand: Waiting for No ACK response")); } #endif retVal = waitForNoACKResponse(outgoingUBX, outgoingUBX->cls, outgoingUBX->id, maxWait); // Wait for Ack response } } return retVal; } // Returns false if sensor fails to respond to I2C traffic sfe_ublox_status_e SFE_UBLOX_GNSS::sendI2cCommand(ubxPacket *outgoingUBX, uint16_t maxWait) { // From the integration guide: // "The receiver does not provide any write access except for writing UBX and NMEA messages to the // receiver, such as configuration or aiding data. Therefore, the register set mentioned in section Read // Access is not writeable. Following the start condition from the master, the 7-bit device address and // the RW bit (which is a logic low for write access) are clocked onto the bus by the master transmitter. // The receiver answers with an acknowledge (logic low) to indicate that it is responsible for the given // address. Now, the master can write 2 to N bytes to the receiver, generating a stop condition after the // last byte being written. The number of data bytes must be at least 2 to properly distinguish from // the write access to set the address counter in random read accesses." // I take two things from this: // 1) We do not need to write 0xFF to point at register 0xFF. We're already pointing at it. // 2) We must always write at least 2 bytes, otherwise it looks like we are starting to do a read. // Point 2 is important. It means: // * In this function: // if we do multiple writes (because we're trying to write more than i2cTransactionSize), // we may need to write one byte less in the penultimate write to ensure we always have two bytes left for the final write. // * In pushRawData: // if there is one byte to write, or one byte left to write, we need to do the same thing and may need to store a single // byte until pushRawData is called again. // The next four lines can be commented. We do not need to point at the 0xFF data register //_i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); //There is no register to write to, we just begin writing data bytes //_i2cPort->write(0xFF); // if (_i2cPort->endTransmission(false) != 0) //Don't release bus // return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); //Sensor did not ACK // The total number of bytes to be written is: payload len + 8 // UBX_SYNCH_1 // UBX_SYNCH_2 // cls // id // len (MSB) // len (LSB) // < payload > // checksumA // checksumB // i2cTransactionSize will be at least 8. We don't need to check for smaller values than that. uint16_t bytesToSend = outgoingUBX->len + 8; // How many bytes need to be sent uint16_t bytesSent = 0; // How many bytes have been sent uint16_t bytesLeftToSend = bytesToSend; // How many bytes remain to be sent uint16_t startSpot = 0; // Payload pointer while (bytesLeftToSend > 0) { uint16_t len = bytesLeftToSend; // How many bytes should we actually write? if (len > i2cTransactionSize) // Limit len to i2cTransactionSize len = i2cTransactionSize; bytesLeftToSend -= len; // Calculate how many bytes will be left after we do this write // If bytesLeftToSend is zero, that's OK. // If bytesLeftToSend is >= 2, that's OK. // But if bytesLeftToSend is 1, we need to adjust len to make sure we write at least 2 bytes in the final write if (bytesLeftToSend == 1) { len -= 1; // Decrement len by 1 bytesLeftToSend += 1; // Increment bytesLeftToSend by 1 } _i2cPort->beginTransmission((uint8_t)_gpsI2Caddress); // Start the transmission if (bytesSent == 0) // Is this the first write? If it is, write the header bytes { _i2cPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. _i2cPort->write(UBX_SYNCH_2); // b _i2cPort->write(outgoingUBX->cls); _i2cPort->write(outgoingUBX->id); _i2cPort->write(outgoingUBX->len & 0xFF); // LSB _i2cPort->write(outgoingUBX->len >> 8); // MSB bytesSent += 6; uint16_t x = 0; // Write a portion of the payload to the bus. // Keep going until we reach the end of the payload (x == outgoingUBX->len) // or we've sent as many bytes as we can in this transmission (bytesSent == len). for (; (x < outgoingUBX->len) && (bytesSent < len); x++) { _i2cPort->write(outgoingUBX->payload[startSpot + x]); bytesSent++; } startSpot += x; // Can we write both checksum bytes? // We can send both bytes now if we have exactly 2 bytes left // to be sent in this transmission (bytesSent == (len - 2)). if (bytesSent == (len - 2)) { // Write checksum _i2cPort->write(outgoingUBX->checksumA); _i2cPort->write(outgoingUBX->checksumB); bytesSent += 2; } } else // Keep writing payload bytes. Write the checksum at the right time. { uint16_t x = 0; // Write a portion of the payload to the bus. // Keep going until we've sent as many bytes as we can in this transmission (x == len) // or until we reach the end of the payload ((startSpot + x) == (outgoingUBX->len)) for (; (x < len) && ((startSpot + x) < (outgoingUBX->len)); x++) { _i2cPort->write(outgoingUBX->payload[startSpot + x]); bytesSent++; } startSpot += x; // Can we write both checksum bytes? // We can send both bytes if we have exactly 2 bytes left to be sent (bytesSent == (bytesToSend - 2)) // and if there is room for 2 bytes in this transmission if ((bytesSent == (bytesToSend - 2)) && (x == (len - 2))) { // Write checksum _i2cPort->write(outgoingUBX->checksumA); _i2cPort->write(outgoingUBX->checksumB); bytesSent += 2; } } if (bytesSent < bytesToSend) // Do we need to go round the loop again? { if (_i2cPort->endTransmission(_i2cStopRestart) != 0) // Don't release bus unless we have to return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); // Sensor did not ACK } } // All done transmitting bytes. Release bus. if (_i2cPort->endTransmission() != 0) return (SFE_UBLOX_STATUS_I2C_COMM_FAILURE); // Sensor did not ACK (void)maxWait; // Do something with maxWait just to avoid the pesky compiler warnings! return (SFE_UBLOX_STATUS_SUCCESS); } // Given a packet and payload, send everything including CRC bytesA via Serial port void SFE_UBLOX_GNSS::sendSerialCommand(ubxPacket *outgoingUBX) { // Write header bytes _serialPort->write(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. _serialPort->write(UBX_SYNCH_2); // b _serialPort->write(outgoingUBX->cls); _serialPort->write(outgoingUBX->id); _serialPort->write(outgoingUBX->len & 0xFF); // LSB _serialPort->write(outgoingUBX->len >> 8); // MSB // Write payload. for (uint16_t i = 0; i < outgoingUBX->len; i++) { _serialPort->write(outgoingUBX->payload[i]); } // Write checksum _serialPort->write(outgoingUBX->checksumA); _serialPort->write(outgoingUBX->checksumB); } // Transfer a byte to SPI. Also capture any bytes received from the UBLOX device during sending and capture them in a small buffer so that // they can be processed later with process void SFE_UBLOX_GNSS::spiTransfer(uint8_t byteToTransfer) { uint8_t returnedByte = _spiPort->transfer(byteToTransfer); if ((spiBufferIndex < getSpiTransactionSize()) && (returnedByte != 0xFF || currentSentence != SFE_UBLOX_SENTENCE_TYPE_NONE)) { spiBuffer[spiBufferIndex] = returnedByte; spiBufferIndex++; } } // Send a command via SPI void SFE_UBLOX_GNSS::sendSpiCommand(ubxPacket *outgoingUBX) { if (spiBuffer == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("sendSpiCommand: no memory allocation for SPI Buffer!")); } #endif return; } // Start at the beginning of the SPI buffer spiBufferIndex = 0; _spiPort->beginTransaction(SPISettings(_spiSpeed, MSBFIRST, SPI_MODE0)); digitalWrite(_csPin, LOW); // Write header bytes spiTransfer(UBX_SYNCH_1); //μ - oh ublox, you're funny. I will call you micro-blox from now on. spiTransfer(UBX_SYNCH_2); // b spiTransfer(outgoingUBX->cls); spiTransfer(outgoingUBX->id); spiTransfer(outgoingUBX->len & 0xFF); // LSB spiTransfer(outgoingUBX->len >> 8); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug) { _debugSerial->print(F("sendSpiCommand: ")); _debugSerial->print(UBX_SYNCH_1, HEX); _debugSerial->print(F(" ")); _debugSerial->print(UBX_SYNCH_2, HEX); _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->cls, HEX); _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->id, HEX); _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->len & 0xFF, HEX); _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->len >> 8, HEX); } #endif // Write payload. for (uint16_t i = 0; i < outgoingUBX->len; i++) { spiTransfer(outgoingUBX->payload[i]); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug) { _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->payload[i], HEX); } #endif } // Write checksum spiTransfer(outgoingUBX->checksumA); spiTransfer(outgoingUBX->checksumB); digitalWrite(_csPin, HIGH); _spiPort->endTransaction(); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug) { _debugSerial->print(F(" ")); _debugSerial->print(outgoingUBX->checksumA, HEX); _debugSerial->print(F(" ")); _debugSerial->println(outgoingUBX->checksumB, HEX); } #endif } // Pretty prints the current ubxPacket void SFE_UBLOX_GNSS::printPacket(ubxPacket *packet, bool alwaysPrintPayload) { // Only print the payload is ignoreThisPayload is false otherwise // we could be printing gibberish from beyond the end of packetBuf // (These two lines get rid of a pesky compiler warning) bool printPayload = (ignoreThisPayload == false); printPayload |= (alwaysPrintPayload == true); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("CLS:")); if (packet->cls == UBX_CLASS_NAV) // 1 _debugSerial->print(F("NAV")); else if (packet->cls == UBX_CLASS_ACK) // 5 _debugSerial->print(F("ACK")); else if (packet->cls == UBX_CLASS_CFG) // 6 _debugSerial->print(F("CFG")); else if (packet->cls == UBX_CLASS_MON) // 0x0A _debugSerial->print(F("MON")); else { _debugSerial->print(F("0x")); _debugSerial->print(packet->cls, HEX); } _debugSerial->print(F(" ID:")); if (packet->cls == UBX_CLASS_NAV && packet->id == UBX_NAV_PVT) _debugSerial->print(F("PVT")); else if (packet->cls == UBX_CLASS_CFG && packet->id == UBX_CFG_RATE) _debugSerial->print(F("RATE")); else if (packet->cls == UBX_CLASS_CFG && packet->id == UBX_CFG_CFG) _debugSerial->print(F("SAVE")); else { _debugSerial->print(F("0x")); _debugSerial->print(packet->id, HEX); } _debugSerial->print(F(" Len: 0x")); _debugSerial->print(packet->len, HEX); if (printPayload) { _debugSerial->print(F(" Payload:")); for (uint16_t x = 0; x < packet->len; x++) { _debugSerial->print(F(" ")); _debugSerial->print(packet->payload[x], HEX); } } else { _debugSerial->print(F(" Payload: IGNORED")); } _debugSerial->println(); } #else if (_printDebug == true) { _debugSerial->print(F("Len: 0x")); _debugSerial->print(packet->len, HEX); } #endif } // When messages from the class CFG are sent to the receiver, the receiver will send an "acknowledge"(UBX - ACK - ACK) or a //"not acknowledge"(UBX-ACK-NAK) message back to the sender, depending on whether or not the message was processed correctly. // Some messages from other classes also use the same acknowledgement mechanism. // When we poll or get a setting, we will receive _both_ a config packet and an ACK // If the poll or get request is not valid, we will receive _only_ a NACK // If we are trying to get or poll a setting, then packetCfg.len will be 0 or 1 when the packetCfg is _sent_. // If we poll the setting for a particular port using UBX-CFG-PRT then .len will be 1 initially // For all other gets or polls, .len will be 0 initially //(It would be possible for .len to be 2 _if_ we were using UBX-CFG-MSG to poll the settings for a particular message - but we don't use that (currently)) // If the get or poll _fails_, i.e. is NACK'd, then packetCfg.len could still be 0 or 1 after the NACK is received // But if the get or poll is ACK'd, then packetCfg.len will have been updated by the incoming data and will always be at least 2 // If we are going to set the value for a setting, then packetCfg.len will be at least 3 when the packetCfg is _sent_. //(UBX-CFG-MSG appears to have the shortest set length of 3 bytes) // We need to think carefully about how interleaved PVT packets affect things. // It is entirely possible that our packetCfg and packetAck were received successfully // but while we are still in the "if (checkUblox() == true)" loop a PVT packet is processed // or _starts_ to arrive (remember that Serial data can arrive very slowly). // Returns SFE_UBLOX_STATUS_DATA_RECEIVED if we got an ACK and a valid packetCfg (module is responding with register content) // Returns SFE_UBLOX_STATUS_DATA_SENT if we got an ACK and no packetCfg (no valid packetCfg needed, module absorbs new register data) // Returns SFE_UBLOX_STATUS_FAIL if something very bad happens (e.g. a double checksum failure) // Returns SFE_UBLOX_STATUS_COMMAND_NACK if the packet was not-acknowledged (NACK) // Returns SFE_UBLOX_STATUS_CRC_FAIL if we had a checksum failure // Returns SFE_UBLOX_STATUS_TIMEOUT if we timed out // Returns SFE_UBLOX_STATUS_DATA_OVERWRITTEN if we got an ACK and a valid packetCfg but that the packetCfg has been // or is currently being overwritten (remember that Serial data can arrive very slowly) sfe_ublox_status_e SFE_UBLOX_GNSS::waitForACKResponse(ubxPacket *outgoingUBX, uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) { outgoingUBX->valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // This will go VALID (or NOT_VALID) when we receive a response to the packet we sent packetAck.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetBuf.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetAuto.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; outgoingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // This will go VALID (or NOT_VALID) when we receive a packet that matches the requested class and ID packetAck.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetBuf.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetAuto.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; unsigned long startTime = millis(); while (millis() < (startTime + (unsigned long)maxTime)) { if (checkUbloxInternal(outgoingUBX, requestedClass, requestedID) == true) // See if new data is available. Process bytes as they come in. { // If both the outgoingUBX->classAndIDmatch and packetAck.classAndIDmatch are VALID // and outgoingUBX->valid is _still_ VALID and the class and ID _still_ match // then we can be confident that the data in outgoingUBX is valid if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->cls == requestedClass) && (outgoingUBX->id == requestedID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: valid data and valid ACK received after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_RECEIVED); // We received valid data and a correct ACK! } // We can be confident that the data packet (if we are going to get one) will always arrive // before the matching ACK. So if we sent a config packet which only produces an ACK // then outgoingUBX->classAndIDmatch will be NOT_DEFINED and the packetAck.classAndIDmatch will VALID. // We should not check outgoingUBX->valid, outgoingUBX->cls or outgoingUBX->id // as these may have been changed by an automatic packet. else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: no data and valid ACK after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_SENT); // We got an ACK but no data... } // If both the outgoingUBX->classAndIDmatch and packetAck.classAndIDmatch are VALID // but the outgoingUBX->cls or ID no longer match then we can be confident that we had // valid data but it has been or is currently being overwritten by an automatic packet (e.g. PVT). // If (e.g.) a PVT packet is _being_ received: outgoingUBX->valid will be NOT_DEFINED // If (e.g.) a PVT packet _has been_ received: outgoingUBX->valid will be VALID (or just possibly NOT_VALID) // So we cannot use outgoingUBX->valid as part of this check. // Note: the addition of packetBuf should make this check redundant! else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && ((outgoingUBX->cls != requestedClass) || (outgoingUBX->id != requestedID))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: data being OVERWRITTEN after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_OVERWRITTEN); // Data was valid but has been or is being overwritten } // If packetAck.classAndIDmatch is VALID but both outgoingUBX->valid and outgoingUBX->classAndIDmatch // are NOT_VALID then we can be confident we have had a checksum failure on the data packet else if ((packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: CRC failed after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_CRC_FAIL); // Checksum fail } // If our packet was not-acknowledged (NACK) we do not receive a data packet - we only get the NACK. // So you would expect outgoingUBX->valid and outgoingUBX->classAndIDmatch to still be NOT_DEFINED // But if a full PVT packet arrives afterwards outgoingUBX->valid could be VALID (or just possibly NOT_VALID) // but outgoingUBX->cls and outgoingUBX->id would not match... // So I think this is telling us we need a special state for packetAck.classAndIDmatch to tell us // the packet was definitely NACK'd otherwise we are possibly just guessing... // Note: the addition of packetBuf changes the logic of this, but we'll leave the code as is for now. else if (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_NOTACKNOWLEDGED) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: data was NOTACKNOWLEDGED (NACK) after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_COMMAND_NACK); // We received a NACK! } // If the outgoingUBX->classAndIDmatch is VALID but the packetAck.classAndIDmatch is NOT_VALID // then the ack probably had a checksum error. We will take a gamble and return DATA_RECEIVED. // If we were playing safe, we should return FAIL instead else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->cls == requestedClass) && (outgoingUBX->id == requestedID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: VALID data and INVALID ACK received after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_RECEIVED); // We received valid data and an invalid ACK! } // If the outgoingUBX->classAndIDmatch is NOT_VALID and the packetAck.classAndIDmatch is NOT_VALID // then we return a FAIL. This must be a double checksum failure? else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: INVALID data and INVALID ACK received after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_FAIL); // We received invalid data and an invalid ACK! } // If the outgoingUBX->classAndIDmatch is VALID and the packetAck.classAndIDmatch is NOT_DEFINED // then the ACK has not yet been received and we should keep waiting for it else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED)) { // if (_printDebug == true) // { // _debugSerial->print(F("waitForACKResponse: valid data after ")); // _debugSerial->print(millis() - startTime); // _debugSerial->println(F(" msec. Waiting for ACK.")); // } } } // checkUbloxInternal == true delay(1); // Allow an RTOS to get an elbow in (#11) } // while (millis() < (startTime + (unsigned long)maxTime)) // We have timed out... // If the outgoingUBX->classAndIDmatch is VALID then we can take a gamble and return DATA_RECEIVED // even though we did not get an ACK if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (packetAck.classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->cls == requestedClass) && (outgoingUBX->id == requestedID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: TIMEOUT with valid data after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec. ")); } #endif return (SFE_UBLOX_STATUS_DATA_RECEIVED); // We received valid data... But no ACK! } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForACKResponse: TIMEOUT after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec.")); } #endif return (SFE_UBLOX_STATUS_TIMEOUT); } // For non-CFG queries no ACK is sent so we use this function // Returns SFE_UBLOX_STATUS_DATA_RECEIVED if we got a config packet full of response data that has CLS/ID match to our query packet // Returns SFE_UBLOX_STATUS_CRC_FAIL if we got a corrupt config packet that has CLS/ID match to our query packet // Returns SFE_UBLOX_STATUS_TIMEOUT if we timed out // Returns SFE_UBLOX_STATUS_DATA_OVERWRITTEN if we got an a valid packetCfg but that the packetCfg has been // or is currently being overwritten (remember that Serial data can arrive very slowly) sfe_ublox_status_e SFE_UBLOX_GNSS::waitForNoACKResponse(ubxPacket *outgoingUBX, uint8_t requestedClass, uint8_t requestedID, uint16_t maxTime) { outgoingUBX->valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // This will go VALID (or NOT_VALID) when we receive a response to the packet we sent packetAck.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetBuf.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetAuto.valid = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; outgoingUBX->classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; // This will go VALID (or NOT_VALID) when we receive a packet that matches the requested class and ID packetAck.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetBuf.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; packetAuto.classAndIDmatch = SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED; unsigned long startTime = millis(); while (millis() - startTime < maxTime) { if (checkUbloxInternal(outgoingUBX, requestedClass, requestedID) == true) // See if new data is available. Process bytes as they come in. { // If outgoingUBX->classAndIDmatch is VALID // and outgoingUBX->valid is _still_ VALID and the class and ID _still_ match // then we can be confident that the data in outgoingUBX is valid if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID) && (outgoingUBX->cls == requestedClass) && (outgoingUBX->id == requestedID)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForNoACKResponse: valid data with CLS/ID match after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_RECEIVED); // We received valid data! } // If the outgoingUBX->classAndIDmatch is VALID // but the outgoingUBX->cls or ID no longer match then we can be confident that we had // valid data but it has been or is currently being overwritten by another packet (e.g. PVT). // If (e.g.) a PVT packet is _being_ received: outgoingUBX->valid will be NOT_DEFINED // If (e.g.) a PVT packet _has been_ received: outgoingUBX->valid will be VALID (or just possibly NOT_VALID) // So we cannot use outgoingUBX->valid as part of this check. // Note: the addition of packetBuf should make this check redundant! else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_VALID) && ((outgoingUBX->cls != requestedClass) || (outgoingUBX->id != requestedID))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForNoACKResponse: data being OVERWRITTEN after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_DATA_OVERWRITTEN); // Data was valid but has been or is being overwritten } // If outgoingUBX->classAndIDmatch is NOT_DEFINED // and outgoingUBX->valid is VALID then this must be (e.g.) a PVT packet else if ((outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_DEFINED) && (outgoingUBX->valid == SFE_UBLOX_PACKET_VALIDITY_VALID)) { // if (_printDebug == true) // { // _debugSerial->print(F("waitForNoACKResponse: valid but UNWANTED data after ")); // _debugSerial->print(millis() - startTime); // _debugSerial->print(F(" msec. Class: ")); // _debugSerial->print(outgoingUBX->cls); // _debugSerial->print(F(" ID: ")); // _debugSerial->print(outgoingUBX->id); // } } // If the outgoingUBX->classAndIDmatch is NOT_VALID then we return CRC failure else if (outgoingUBX->classAndIDmatch == SFE_UBLOX_PACKET_VALIDITY_NOT_VALID) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForNoACKResponse: CLS/ID match but failed CRC after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec")); } #endif return (SFE_UBLOX_STATUS_CRC_FAIL); // We received invalid data } } delay(1); // Allow an RTOS to get an elbow in (#11) } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("waitForNoACKResponse: TIMEOUT after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" msec. No packet received.")); } #endif return (SFE_UBLOX_STATUS_TIMEOUT); } // Check if any callbacks are waiting to be processed void SFE_UBLOX_GNSS::checkCallbacks(void) { if (checkCallbacksReentrant == true) // Check for reentry (i.e. checkCallbacks has been called from inside a callback) return; checkCallbacksReentrant = true; if ((packetUBXNAVPOSECEF != NULL) // If RAM has been allocated for message storage && (packetUBXNAVPOSECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPOSECEF->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVPOSECEF->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV POSECEF")); packetUBXNAVPOSECEF->callbackPointer(*packetUBXNAVPOSECEF->callbackData); // Call the callback } if (packetUBXNAVPOSECEF->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV POSECEF")); packetUBXNAVPOSECEF->callbackPointerPtr(packetUBXNAVPOSECEF->callbackData); // Call the callback } packetUBXNAVPOSECEF->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVSTATUS != NULL) // If RAM has been allocated for message storage && (packetUBXNAVSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSTATUS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVSTATUS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV STATUS")); packetUBXNAVSTATUS->callbackPointer(*packetUBXNAVSTATUS->callbackData); // Call the callback } if (packetUBXNAVSTATUS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV STATUS")); packetUBXNAVSTATUS->callbackPointerPtr(packetUBXNAVSTATUS->callbackData); // Call the callback } packetUBXNAVSTATUS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVDOP != NULL) // If RAM has been allocated for message storage && (packetUBXNAVDOP->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVDOP->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV DOP")); packetUBXNAVDOP->callbackPointer(*packetUBXNAVDOP->callbackData); // Call the callback } if (packetUBXNAVDOP->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV DOP")); packetUBXNAVDOP->callbackPointerPtr(packetUBXNAVDOP->callbackData); // Call the callback } packetUBXNAVDOP->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVATT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVATT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV ATT")); packetUBXNAVATT->callbackPointer(*packetUBXNAVATT->callbackData); // Call the callback } if (packetUBXNAVATT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV ATT")); packetUBXNAVATT->callbackPointerPtr(packetUBXNAVATT->callbackData); // Call the callback } packetUBXNAVATT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVPVT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVPVT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVPVT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV PVT")); packetUBXNAVPVT->callbackPointer(*packetUBXNAVPVT->callbackData); // Call the callback } if (packetUBXNAVPVT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV PVT")); packetUBXNAVPVT->callbackPointerPtr(packetUBXNAVPVT->callbackData); // Call the callback } packetUBXNAVPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVODO != NULL) // If RAM has been allocated for message storage && (packetUBXNAVODO->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVODO->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV ODO")); packetUBXNAVODO->callbackPointer(*packetUBXNAVODO->callbackData); // Call the callback } if (packetUBXNAVODO->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV ODO")); packetUBXNAVODO->callbackPointerPtr(packetUBXNAVODO->callbackData); // Call the callback } packetUBXNAVODO->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVVELECEF != NULL) // If RAM has been allocated for message storage && (packetUBXNAVVELECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVVELECEF->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV VELECEF")); packetUBXNAVVELECEF->callbackPointer(*packetUBXNAVVELECEF->callbackData); // Call the callback } if (packetUBXNAVVELECEF->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV VELECEF")); packetUBXNAVVELECEF->callbackPointerPtr(packetUBXNAVVELECEF->callbackData); // Call the callback } packetUBXNAVVELECEF->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVVELNED != NULL) // If RAM has been allocated for message storage && (packetUBXNAVVELNED->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVVELNED->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVVELNED->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV VELNED")); packetUBXNAVVELNED->callbackPointer(*packetUBXNAVVELNED->callbackData); // Call the callback } if (packetUBXNAVVELNED->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV VELNED")); packetUBXNAVVELNED->callbackPointerPtr(packetUBXNAVVELNED->callbackData); // Call the callback } packetUBXNAVVELNED->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVHPPOSECEF != NULL) // If RAM has been allocated for message storage && (packetUBXNAVHPPOSECEF->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVHPPOSECEF->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV HPPOSECEF")); packetUBXNAVHPPOSECEF->callbackPointer(*packetUBXNAVHPPOSECEF->callbackData); // Call the callback } if (packetUBXNAVHPPOSECEF->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV HPPOSECEF")); packetUBXNAVHPPOSECEF->callbackPointerPtr(packetUBXNAVHPPOSECEF->callbackData); // Call the callback } packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVHPPOSLLH != NULL) // If RAM has been allocated for message storage && (packetUBXNAVHPPOSLLH->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVHPPOSLLH->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV HPPOSLLH")); packetUBXNAVHPPOSLLH->callbackPointer(*packetUBXNAVHPPOSLLH->callbackData); // Call the callback } if (packetUBXNAVHPPOSLLH->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV HPPOSLLH")); packetUBXNAVHPPOSLLH->callbackPointerPtr(packetUBXNAVHPPOSLLH->callbackData); // Call the callback } packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVPVAT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVPVAT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVPVAT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV PVAT")); packetUBXNAVPVAT->callbackPointer(*packetUBXNAVPVAT->callbackData); // Call the callback } if (packetUBXNAVPVAT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV PVAT")); packetUBXNAVPVAT->callbackPointerPtr(packetUBXNAVPVAT->callbackData); // Call the callback } packetUBXNAVPVAT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVTIMEUTC != NULL) // If RAM has been allocated for message storage && (packetUBXNAVTIMEUTC->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVTIMEUTC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV TIMEUTC")); packetUBXNAVTIMEUTC->callbackPointerPtr(packetUBXNAVTIMEUTC->callbackData); // Call the callback } packetUBXNAVTIMEUTC->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVCLOCK != NULL) // If RAM has been allocated for message storage && (packetUBXNAVCLOCK->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVCLOCK->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVCLOCK->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV CLOCK")); packetUBXNAVCLOCK->callbackPointer(*packetUBXNAVCLOCK->callbackData); // Call the callback } if (packetUBXNAVCLOCK->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV CLOCK")); packetUBXNAVCLOCK->callbackPointerPtr(packetUBXNAVCLOCK->callbackData); // Call the callback } packetUBXNAVCLOCK->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVSVIN != NULL) // If RAM has been allocated for message storage && (packetUBXNAVSVIN->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSVIN->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVSVIN->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV SVIN")); packetUBXNAVSVIN->callbackPointerPtr(packetUBXNAVSVIN->callbackData); // Call the callback } packetUBXNAVSVIN->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVSAT != NULL) // If RAM has been allocated for message storage && (packetUBXNAVSAT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVSAT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVSAT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV SAT")); packetUBXNAVSAT->callbackPointer(*packetUBXNAVSAT->callbackData); // Call the callback } if (packetUBXNAVSAT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV SAT")); packetUBXNAVSAT->callbackPointerPtr(packetUBXNAVSAT->callbackData); // Call the callback } packetUBXNAVSAT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVRELPOSNED != NULL) // If RAM has been allocated for message storage && (packetUBXNAVRELPOSNED->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVRELPOSNED->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVRELPOSNED->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV RELPOSNED")); packetUBXNAVRELPOSNED->callbackPointer(*packetUBXNAVRELPOSNED->callbackData); // Call the callback } if (packetUBXNAVRELPOSNED->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV RELPOSNED")); packetUBXNAVRELPOSNED->callbackPointerPtr(packetUBXNAVRELPOSNED->callbackData); // Call the callback } packetUBXNAVRELPOSNED->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVAOPSTATUS != NULL) // If RAM has been allocated for message storage && (packetUBXNAVAOPSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVAOPSTATUS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for NAV AOPSTATUS")); packetUBXNAVAOPSTATUS->callbackPointer(*packetUBXNAVAOPSTATUS->callbackData); // Call the callback } if (packetUBXNAVAOPSTATUS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV AOPSTATUS")); packetUBXNAVAOPSTATUS->callbackPointerPtr(packetUBXNAVAOPSTATUS->callbackData); // Call the callback } packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXNAVEOE != NULL) // If RAM has been allocated for message storage && (packetUBXNAVEOE->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXNAVEOE->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for NAV EOE")); packetUBXNAVEOE->callbackPointerPtr(packetUBXNAVEOE->callbackData); // Call the callback } packetUBXNAVEOE->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXRXMPMP != NULL) // If RAM has been allocated for message storage && (packetUBXRXMPMP->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMPMP->callbackPointerPtr != NULL) // If the pointer to the callback has been defined && (packetUBXRXMPMP->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM PMP")); packetUBXRXMPMP->callbackPointerPtr(packetUBXRXMPMP->callbackData); // Call the callback packetUBXRXMPMP->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXRXMPMPmessage != NULL) // If RAM has been allocated for message storage && (packetUBXRXMPMPmessage->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMPMPmessage->callbackPointerPtr != NULL) // If the pointer to the callback has been defined && (packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM PMP message")); packetUBXRXMPMPmessage->callbackPointerPtr(packetUBXRXMPMPmessage->callbackData); // Call the callback packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXRXMQZSSL6message != NULL) && // If RAM has been allocated for message storage (packetUBXRXMQZSSL6message->callbackData != NULL) && // If RAM has been allocated for the copy of the data (packetUBXRXMQZSSL6message->callbackPointerPtr != NULL)) // If the pointer to the callback has been defined { for (int ch = 0; ch < UBX_RXM_QZSSL6_NUM_CHANNELS; ch++) { if (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid & (1 << ch)) // If the copy of the data is valid { packetUBXRXMQZSSL6message->callbackPointerPtr(&packetUBXRXMQZSSL6message->callbackData[ch]); // Call the callback packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid &= ~(1 << ch); // clear it } } } if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage && (packetUBXRXMCOR->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMCOR->callbackPointerPtr != NULL) // If the pointer to the callback has been defined && (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM COR")); packetUBXRXMCOR->callbackPointerPtr(packetUBXRXMCOR->callbackData); // Call the callback packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXRXMSFRBX != NULL) // If RAM has been allocated for message storage && (packetUBXRXMSFRBX->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXRXMSFRBX->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for RXM SFRBX")); packetUBXRXMSFRBX->callbackPointer(*packetUBXRXMSFRBX->callbackData); // Call the callback } if (packetUBXRXMSFRBX->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM SFRBX")); packetUBXRXMSFRBX->callbackPointerPtr(packetUBXRXMSFRBX->callbackData); // Call the callback } packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXRXMRAWX != NULL) // If RAM has been allocated for message storage && (packetUBXRXMRAWX->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXRXMRAWX->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXRXMRAWX->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for RXM RAWX")); packetUBXRXMRAWX->callbackPointer(*packetUBXRXMRAWX->callbackData); // Call the callback } if (packetUBXRXMRAWX->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM RAWX")); packetUBXRXMRAWX->callbackPointerPtr(packetUBXRXMRAWX->callbackData); // Call the callback } packetUBXRXMRAWX->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXTIMTM2 != NULL) // If RAM has been allocated for message storage && (packetUBXTIMTM2->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXTIMTM2->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXTIMTM2->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for TIM TM2")); packetUBXTIMTM2->callbackPointer(*packetUBXTIMTM2->callbackData); // Call the callback } if (packetUBXTIMTM2->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for TIM TM2")); packetUBXTIMTM2->callbackPointerPtr(packetUBXTIMTM2->callbackData); // Call the callback } packetUBXTIMTM2->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXESFALG != NULL) // If RAM has been allocated for message storage && (packetUBXESFALG->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFALG->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXESFALG->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for ESF ALG")); packetUBXESFALG->callbackPointer(*packetUBXESFALG->callbackData); // Call the callback } if (packetUBXESFALG->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for ESF ALG")); packetUBXESFALG->callbackPointerPtr(packetUBXESFALG->callbackData); // Call the callback } packetUBXESFALG->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXESFINS != NULL) // If RAM has been allocated for message storage && (packetUBXESFINS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFINS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXESFINS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for ESF INS")); packetUBXESFINS->callbackPointer(*packetUBXESFINS->callbackData); // Call the callback } if (packetUBXESFINS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for ESF INS")); packetUBXESFINS->callbackPointerPtr(packetUBXESFINS->callbackData); // Call the callback } packetUBXESFINS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXESFMEAS != NULL) // If RAM has been allocated for message storage && (packetUBXESFMEAS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXESFMEAS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for ESF MEAS")); packetUBXESFMEAS->callbackPointer(*packetUBXESFMEAS->callbackData); // Call the callback } if (packetUBXESFMEAS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for ESF MEAS")); packetUBXESFMEAS->callbackPointerPtr(packetUBXESFMEAS->callbackData); // Call the callback } packetUBXESFMEAS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXESFRAW != NULL) // If RAM has been allocated for message storage && (packetUBXESFRAW->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXESFRAW->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for ESF RAW")); packetUBXESFRAW->callbackPointer(*packetUBXESFRAW->callbackData); // Call the callback } if (packetUBXESFRAW->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for ESF RAW")); packetUBXESFRAW->callbackPointerPtr(packetUBXESFRAW->callbackData); // Call the callback } packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXESFSTATUS != NULL) // If RAM has been allocated for message storage && (packetUBXESFSTATUS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXESFSTATUS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXESFSTATUS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for ESF STATUS")); packetUBXESFSTATUS->callbackPointer(*packetUBXESFSTATUS->callbackData); // Call the callback } if (packetUBXESFSTATUS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for ESF STATUS")); packetUBXESFSTATUS->callbackPointerPtr(packetUBXESFSTATUS->callbackData); // Call the callback } packetUBXESFSTATUS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXHNRATT != NULL) // If RAM has been allocated for message storage && (packetUBXHNRATT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRATT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXHNRATT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for HNR ATT")); packetUBXHNRATT->callbackPointer(*packetUBXHNRATT->callbackData); // Call the callback } if (packetUBXHNRATT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for HNR ATT")); packetUBXHNRATT->callbackPointerPtr(packetUBXHNRATT->callbackData); // Call the callback } packetUBXHNRATT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXHNRINS != NULL) // If RAM has been allocated for message storage && (packetUBXHNRINS->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRINS->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXHNRINS->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for HNR INS")); packetUBXHNRINS->callbackPointer(*packetUBXHNRINS->callbackData); // Call the callback } if (packetUBXHNRINS->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for HNR INS")); packetUBXHNRINS->callbackPointerPtr(packetUBXHNRINS->callbackData); // Call the callback } packetUBXHNRINS->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } if ((packetUBXHNRPVT != NULL) // If RAM has been allocated for message storage && (packetUBXHNRPVT->callbackData != NULL) // If RAM has been allocated for the copy of the data && (packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid { if (packetUBXHNRPVT->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for HNR PVT")); packetUBXHNRPVT->callbackPointer(*packetUBXHNRPVT->callbackData); // Call the callback } if (packetUBXHNRPVT->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for HNR PVT")); packetUBXHNRPVT->callbackPointerPtr(packetUBXHNRPVT->callbackData); // Call the callback } packetUBXHNRPVT->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if ((storageNMEAGPGGA != NULL) // If RAM has been allocated for message storage && (storageNMEAGPGGA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGPGGA->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GPGGA")); storageNMEAGPGGA->callbackPointer(*storageNMEAGPGGA->callbackCopy); // Call the callback } if (storageNMEAGPGGA->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GPGGA")); storageNMEAGPGGA->callbackPointerPtr(storageNMEAGPGGA->callbackCopy); // Call the callback } storageNMEAGPGGA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGNGGA != NULL) // If RAM has been allocated for message storage && (storageNMEAGNGGA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGNGGA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGNGGA->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GNGGA")); storageNMEAGNGGA->callbackPointer(*storageNMEAGNGGA->callbackCopy); // Call the callback } if (storageNMEAGNGGA->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GNGGA")); storageNMEAGNGGA->callbackPointerPtr(storageNMEAGNGGA->callbackCopy); // Call the callback } storageNMEAGNGGA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGPVTG != NULL) // If RAM has been allocated for message storage && (storageNMEAGPVTG->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGPVTG->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGPVTG->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GPVTG")); storageNMEAGPVTG->callbackPointer(*storageNMEAGPVTG->callbackCopy); // Call the callback } if (storageNMEAGPVTG->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GPVTG")); storageNMEAGPVTG->callbackPointerPtr(storageNMEAGPVTG->callbackCopy); // Call the callback } storageNMEAGPVTG->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGNVTG != NULL) // If RAM has been allocated for message storage && (storageNMEAGNVTG->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGNVTG->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGNVTG->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GNVTG")); storageNMEAGNVTG->callbackPointer(*storageNMEAGNVTG->callbackCopy); // Call the callback } if (storageNMEAGNVTG->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GNVTG")); storageNMEAGNVTG->callbackPointerPtr(storageNMEAGNVTG->callbackCopy); // Call the callback } storageNMEAGNVTG->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGPRMC != NULL) // If RAM has been allocated for message storage && (storageNMEAGPRMC->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGPRMC->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGPRMC->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GPRMC")); storageNMEAGPRMC->callbackPointer(*storageNMEAGPRMC->callbackCopy); // Call the callback } if (storageNMEAGPRMC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GPRMC")); storageNMEAGPRMC->callbackPointerPtr(storageNMEAGPRMC->callbackCopy); // Call the callback } storageNMEAGPRMC->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGNRMC != NULL) // If RAM has been allocated for message storage && (storageNMEAGNRMC->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGNRMC->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGNRMC->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GNRMC")); storageNMEAGNRMC->callbackPointer(*storageNMEAGNRMC->callbackCopy); // Call the callback } if (storageNMEAGNRMC->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GNRMC")); storageNMEAGNRMC->callbackPointerPtr(storageNMEAGNRMC->callbackCopy); // Call the callback } storageNMEAGNRMC->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGPZDA != NULL) // If RAM has been allocated for message storage && (storageNMEAGPZDA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGPZDA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGPZDA->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GPZDA")); storageNMEAGPZDA->callbackPointer(*storageNMEAGPZDA->callbackCopy); // Call the callback } if (storageNMEAGPZDA->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GPZDA")); storageNMEAGPZDA->callbackPointerPtr(storageNMEAGPZDA->callbackCopy); // Call the callback } storageNMEAGPZDA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } if ((storageNMEAGNZDA != NULL) // If RAM has been allocated for message storage && (storageNMEAGNZDA->callbackCopy != NULL) // If RAM has been allocated for the copy of the data && (storageNMEAGNZDA->automaticFlags.flags.bits.callbackCopyValid == 1)) // If the copy of the data is valid { if (storageNMEAGNZDA->callbackPointer != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callback for GNZDA")); storageNMEAGNZDA->callbackPointer(*storageNMEAGNZDA->callbackCopy); // Call the callback } if (storageNMEAGNZDA->callbackPointerPtr != NULL) // If the pointer to the callback has been defined { // if (_printDebug == true) // _debugSerial->println(F("checkCallbacks: calling callbackPtr for GNZDA")); storageNMEAGNZDA->callbackPointerPtr(storageNMEAGNZDA->callbackCopy); // Call the callback } storageNMEAGNZDA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } #endif checkCallbacksReentrant = false; } // Push (e.g.) RTCM data directly to the module // Returns true if all numDataBytes were pushed successfully // Warning: this function does not check that the data is valid. It is the user's responsibility to ensure the data is valid before pushing. // Default to using a restart between transmissions. But processors like ESP32 seem to need a stop (#30). Set stop to true to use a stop instead. // On processors like the ESP32, you can use setI2CTransactionSize to increase the size of each transmission - to e.g. 128 bytes bool SFE_UBLOX_GNSS::pushRawData(uint8_t *dataBytes, size_t numDataBytes, bool stop) { // Return now if numDataBytes is zero if (numDataBytes == 0) return (false); // Indicate to the user that there was no data to push if (commType == COMM_TYPE_SERIAL) { // Serial: write all the bytes in one go size_t bytesWritten = _serialPort->write(dataBytes, numDataBytes); return (bytesWritten == numDataBytes); } else if (commType == COMM_TYPE_I2C) { // We can not write a single data byte to I2C as it would look like the address of a random read. // If numDataBytes is 1, we should probably just reject the data and return false. // But we'll be nice and store the byte until the next time pushRawData is called. if ((numDataBytes == 1) && (_pushSingleByte == false)) { _pushThisSingleByte = *dataBytes; _pushSingleByte = true; return (false); // Indicate to the user that their data has not been pushed yet } // If stop is true then always use a stop // Else if _i2cStopRestart is true then always use a stop // Else use a restart where needed if (stop == true) stop = true; // Redundant - but makes it clear what is happening else if (_i2cStopRestart == true) stop = true; else stop = false; // Use a restart // I2C: split the data up into packets of i2cTransactionSize size_t bytesLeftToWrite = numDataBytes; size_t bytesWrittenTotal = 0; if (_pushSingleByte == true) // Increment bytesLeftToWrite if we have a single byte waiting to be pushed bytesLeftToWrite++; while (bytesLeftToWrite > 0) { size_t bytesToWrite; // Limit bytesToWrite to i2cTransactionSize if (bytesLeftToWrite > i2cTransactionSize) bytesToWrite = i2cTransactionSize; else bytesToWrite = bytesLeftToWrite; // If there would be one byte left to be written next time, send one byte less now if ((bytesLeftToWrite - bytesToWrite) == 1) bytesToWrite--; _i2cPort->beginTransmission(_gpsI2Caddress); size_t bytesWritten = 0; // If _pushSingleByte is true, push it now if (_pushSingleByte == true) { bytesWritten += _i2cPort->write(_pushThisSingleByte); // Write the single byte bytesWritten += _i2cPort->write(dataBytes, bytesToWrite - 1); // Write the bytes - but send one byte less dataBytes += bytesToWrite - 1; // Point to fresh data _pushSingleByte = false; // Clear the flag } else { bytesWritten += _i2cPort->write(dataBytes, bytesToWrite); // Write the bytes dataBytes += bytesToWrite; // Point to fresh data } bytesWrittenTotal += bytesWritten; // Update the totals bytesLeftToWrite -= bytesToWrite; if (bytesLeftToWrite > 0) { if (_i2cPort->endTransmission(stop) != 0) // Send a restart or stop command return (false); // Sensor did not ACK } else { if (_i2cPort->endTransmission() != 0) // We're done. Release bus. Always use a stop here return (false); // Sensor did not ACK } } return (bytesWrittenTotal == numDataBytes); // Return true if the correct number of bytes were written } else // SPI { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("pushRawData: SPI not currently supported")); } #endif return (false); } } // Push MGA AssistNow data to the module. // Check for UBX-MGA-ACK responses if required (if mgaAck is YES or ENQUIRE). // Wait for maxWait millis after sending each packet (if mgaAck is NO). // Return how many bytes were pushed successfully. // If skipTime is true, any UBX-MGA-INI-TIME_UTC or UBX-MGA-INI-TIME_GNSS packets found in the data will be skipped, // allowing the user to override with their own time data with setUTCTimeAssistance. size_t SFE_UBLOX_GNSS::pushAssistNowData(const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(0, false, (const uint8_t *)dataBytes.c_str(), numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowData(const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(0, false, dataBytes, numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowData(bool skipTime, const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(0, skipTime, (const uint8_t *)dataBytes.c_str(), numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowData(bool skipTime, const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(0, skipTime, dataBytes, numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowData(size_t offset, bool skipTime, const String &dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(offset, skipTime, (const uint8_t *)dataBytes.c_str(), numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowData(size_t offset, bool skipTime, const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { return (pushAssistNowDataInternal(offset, skipTime, dataBytes, numDataBytes, mgaAck, maxWait)); } size_t SFE_UBLOX_GNSS::pushAssistNowDataInternal(size_t offset, bool skipTime, const uint8_t *dataBytes, size_t numDataBytes, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { size_t dataPtr = offset; // Pointer into dataBytes size_t packetsProcessed = 0; // Keep count of how many packets have been processed size_t bytesPushed = 0; // Keep count bool checkForAcks = (mgaAck == SFE_UBLOX_MGA_ASSIST_ACK_YES); // If mgaAck is YES, always check for Acks // If mgaAck is ENQUIRE, we need to check UBX-CFG-NAVX5 ackAiding to determine if UBX-MGA-ACK's are expected if (mgaAck == SFE_UBLOX_MGA_ASSIST_ACK_ENQUIRE) { uint8_t ackAiding = getAckAiding(maxWait); // Enquire if we should expect Acks if (ackAiding == 1) checkForAcks = true; #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: mgaAck is ENQUIRE. getAckAiding returned ")); _debugSerial->println(ackAiding); } #endif } // If checkForAcks is true, then we need to set up storage for the UBX-MGA-ACK-DATA0 messages if (checkForAcks) { if (packetUBXMGAACK == NULL) initPacketUBXMGAACK(); // Check that RAM has been allocated for the MGA_ACK data if (packetUBXMGAACK == NULL) // Bail if the RAM allocation failed return (0); } while (dataPtr < (offset + numDataBytes)) // Keep going until we have processed all the bytes { // Start by checking the validity of the packet being pointed to bool dataIsOK = true; dataIsOK &= (*(dataBytes + dataPtr + 0) == UBX_SYNCH_1); // Check for 0xB5 dataIsOK &= (*(dataBytes + dataPtr + 1) == UBX_SYNCH_2); // Check for 0x62 dataIsOK &= (*(dataBytes + dataPtr + 2) == UBX_CLASS_MGA); // Check for class UBX-MGA size_t packetLength = ((size_t) * (dataBytes + dataPtr + 4)) | (((size_t) * (dataBytes + dataPtr + 5)) << 8); // Extract the length uint8_t checksumA = 0; uint8_t checksumB = 0; // Calculate the checksum bytes // Keep going until the end of the packet is reached (payloadPtr == (dataPtr + packetLength)) // or we reach the end of the AssistNow data (payloadPtr == offset + numDataBytes) for (size_t payloadPtr = dataPtr + ((size_t)2); (payloadPtr < (dataPtr + packetLength + ((size_t)6))) && (payloadPtr < (offset + numDataBytes)); payloadPtr++) { checksumA += *(dataBytes + payloadPtr); checksumB += checksumA; } // Check the checksum bytes dataIsOK &= (checksumA == *(dataBytes + dataPtr + packetLength + ((size_t)6))); dataIsOK &= (checksumB == *(dataBytes + dataPtr + packetLength + ((size_t)7))); dataIsOK &= ((dataPtr + packetLength + ((size_t)8)) <= (offset + numDataBytes)); // Check we haven't overrun // If the data is valid, push it if (dataIsOK) { // Check if this is time assistance data which should be skipped if ((skipTime) && ((*(dataBytes + dataPtr + 3) == UBX_MGA_INI_TIME_UTC) || (*(dataBytes + dataPtr + 3) == UBX_MGA_INI_TIME_GNSS))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: skipped INI_TIME ID 0x")); if (*(dataBytes + dataPtr + 3) < 0x10) _debugSerial->print(F("0")); _debugSerial->println(*(dataBytes + dataPtr + 3), HEX); } #endif } else { bool pushResult = pushRawData((uint8_t *)(dataBytes + dataPtr), packetLength + ((size_t)8)); // Push the data if (pushResult) bytesPushed += packetLength + ((size_t)8); // Increment bytesPushed if the push was successful if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: packet ID 0x")); if (*(dataBytes + dataPtr + 3) < 0x10) _debugSerial->print(F("0")); _debugSerial->print(*(dataBytes + dataPtr + 3), HEX); _debugSerial->print(F(" length ")); _debugSerial->println(packetLength); } if (checkForAcks) { unsigned long startTime = millis(); bool keepGoing = true; while (keepGoing && (millis() < (startTime + maxWait))) // Keep checking for the ACK until we time out { checkUblox(); if (packetUBXMGAACK->head != packetUBXMGAACK->tail) // Does the MGA ACK ringbuffer contain any ACK's? { bool dataAckd = true; // Check if we've received the correct ACK dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgId == *(dataBytes + dataPtr + 3)); // Check if the message ID matches dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[0] == *(dataBytes + dataPtr + 6)); // Check if the first four data bytes match dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[1] == *(dataBytes + dataPtr + 7)); dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[2] == *(dataBytes + dataPtr + 8)); dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[3] == *(dataBytes + dataPtr + 9)); if (dataAckd) // Is this the ACK we are looking for? { if ((packetUBXMGAACK->data[packetUBXMGAACK->tail].type == (uint8_t)1) && (packetUBXMGAACK->data[packetUBXMGAACK->tail].infoCode == (uint8_t)SFE_UBLOX_MGA_ACK_INFOCODE_ACCEPTED)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: packet was accepted after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" ms")); } #endif packetsProcessed++; } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: packet was _not_ accepted. infoCode is ")); _debugSerial->println(packetUBXMGAACK->data[packetUBXMGAACK->tail].infoCode); } #endif } keepGoing = false; } // Increment the tail packetUBXMGAACK->tail++; if (packetUBXMGAACK->tail == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN) packetUBXMGAACK->tail = 0; } } if (keepGoing) // If keepGoing is still true, we must have timed out { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("pushAssistNowData: packet ack timed out!")); } #endif } } else { // We are not checking for Acks, so let's assume the send was successful? packetsProcessed++; // We are not checking for Acks, so delay for maxWait millis unless we've reached the end of the data if ((dataPtr + packetLength + ((size_t)8)) < (offset + numDataBytes)) { delay(maxWait); } } } dataPtr += packetLength + ((size_t)8); // Point to the next message } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM // The data was invalid. Send a debug message and then try to find the next 0xB5 if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: bad data - ignored! dataPtr is ")); _debugSerial->println(dataPtr); } #endif while ((dataPtr < (offset + numDataBytes)) && (*(dataBytes + ++dataPtr) != UBX_SYNCH_1)) { ; // Increment dataPtr until we are pointing at the next 0xB5 - or we reach the end of the data } } } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("pushAssistNowData: packetsProcessed: ")); _debugSerial->println(packetsProcessed); } #endif return (bytesPushed); // Return the number of valid bytes successfully pushed } // PRIVATE: Allocate RAM for packetUBXMGAACK and initialize it bool SFE_UBLOX_GNSS::initPacketUBXMGAACK() { packetUBXMGAACK = new UBX_MGA_ACK_DATA0_t; // Allocate RAM for the main struct if (packetUBXMGAACK == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXMGAACK: RAM alloc failed!")); #endif return (false); } packetUBXMGAACK->head = 0; // Initialize the ring buffer pointers packetUBXMGAACK->tail = 0; return (true); } // Provide initial time assistance bool SFE_UBLOX_GNSS::setUTCTimeAssistance(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second, uint32_t nanos, uint16_t tAccS, uint32_t tAccNs, uint8_t source, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { uint8_t iniTimeUTC[32]; // Create the UBX-MGA-INI-TIME_UTC message by hand memset(iniTimeUTC, 0x00, 32); // Set all unused / reserved bytes and the checksum to zero iniTimeUTC[0] = UBX_SYNCH_1; // Sync char 1 iniTimeUTC[1] = UBX_SYNCH_2; // Sync char 2 iniTimeUTC[2] = UBX_CLASS_MGA; // Class iniTimeUTC[3] = UBX_MGA_INI_TIME_UTC; // ID iniTimeUTC[4] = 24; // Length LSB iniTimeUTC[5] = 0x00; // Length MSB iniTimeUTC[6] = 0x10; // type iniTimeUTC[7] = 0x00; // version iniTimeUTC[8] = source; // ref (source) iniTimeUTC[9] = 0x80; // leapSecs. Set to 0x80 = unknown iniTimeUTC[10] = (uint8_t)(year & 0xFF); // year LSB iniTimeUTC[11] = (uint8_t)(year >> 8); // year MSB iniTimeUTC[12] = month; // month starting at 1 iniTimeUTC[13] = day; // day starting at 1 iniTimeUTC[14] = hour; // hour 0:23 iniTimeUTC[15] = minute; // minute 0:59 iniTimeUTC[16] = second; // seconds 0:59 iniTimeUTC[18] = (uint8_t)(nanos & 0xFF); // nanoseconds LSB iniTimeUTC[19] = (uint8_t)((nanos >> 8) & 0xFF); iniTimeUTC[20] = (uint8_t)((nanos >> 16) & 0xFF); iniTimeUTC[21] = (uint8_t)(nanos >> 24); // nanoseconds MSB iniTimeUTC[22] = (uint8_t)(tAccS & 0xFF); // seconds part of the accuracy LSB iniTimeUTC[23] = (uint8_t)(tAccS >> 8); // seconds part of the accuracy MSB iniTimeUTC[26] = (uint8_t)(tAccNs & 0xFF); // nanoseconds part of the accuracy LSB iniTimeUTC[27] = (uint8_t)((tAccNs >> 8) & 0xFF); iniTimeUTC[28] = (uint8_t)((tAccNs >> 16) & 0xFF); iniTimeUTC[29] = (uint8_t)(tAccNs >> 24); // nanoseconds part of the accuracy MSB for (uint8_t i = 2; i < 30; i++) // Calculate the checksum { iniTimeUTC[30] += iniTimeUTC[i]; iniTimeUTC[31] += iniTimeUTC[30]; } // Return true if the one packet was pushed successfully return (pushAssistNowDataInternal(0, false, iniTimeUTC, 32, mgaAck, maxWait) == 32); } // Provide initial position assistance // The units for ecefX/Y/Z and posAcc (stddev) are cm. bool SFE_UBLOX_GNSS::setPositionAssistanceXYZ(int32_t ecefX, int32_t ecefY, int32_t ecefZ, uint32_t posAcc, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { uint8_t iniPosXYZ[28]; // Create the UBX-MGA-INI-POS_XYZ message by hand memset(iniPosXYZ, 0x00, 28); // Set all unused / reserved bytes and the checksum to zero iniPosXYZ[0] = UBX_SYNCH_1; // Sync char 1 iniPosXYZ[1] = UBX_SYNCH_2; // Sync char 2 iniPosXYZ[2] = UBX_CLASS_MGA; // Class iniPosXYZ[3] = UBX_MGA_INI_POS_XYZ; // ID iniPosXYZ[4] = 20; // Length LSB iniPosXYZ[5] = 0x00; // Length MSB iniPosXYZ[6] = 0x00; // type iniPosXYZ[7] = 0x00; // version union // Use a union to convert from int32_t to uint32_t { int32_t signedLong; uint32_t unsignedLong; } signedUnsigned; signedUnsigned.signedLong = ecefX; iniPosXYZ[10] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosXYZ[11] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosXYZ[12] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosXYZ[13] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB signedUnsigned.signedLong = ecefY; iniPosXYZ[14] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosXYZ[15] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosXYZ[16] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosXYZ[17] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB signedUnsigned.signedLong = ecefZ; iniPosXYZ[18] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosXYZ[19] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosXYZ[20] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosXYZ[21] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB iniPosXYZ[22] = (uint8_t)(posAcc & 0xFF); // LSB iniPosXYZ[23] = (uint8_t)((posAcc >> 8) & 0xFF); iniPosXYZ[24] = (uint8_t)((posAcc >> 16) & 0xFF); iniPosXYZ[25] = (uint8_t)(posAcc >> 24); // MSB for (uint8_t i = 2; i < 26; i++) // Calculate the checksum { iniPosXYZ[26] += iniPosXYZ[i]; iniPosXYZ[27] += iniPosXYZ[26]; } // Return true if the one packet was pushed successfully return (pushAssistNowDataInternal(0, false, iniPosXYZ, 28, mgaAck, maxWait) == 28); } // The units for lat and lon are degrees * 1e-7 (WGS84) // The units for alt (WGS84) and posAcc (stddev) are cm. bool SFE_UBLOX_GNSS::setPositionAssistanceLLH(int32_t lat, int32_t lon, int32_t alt, uint32_t posAcc, sfe_ublox_mga_assist_ack_e mgaAck, uint16_t maxWait) { uint8_t iniPosLLH[28]; // Create the UBX-MGA-INI-POS_LLH message by hand memset(iniPosLLH, 0x00, 28); // Set all unused / reserved bytes and the checksum to zero iniPosLLH[0] = UBX_SYNCH_1; // Sync char 1 iniPosLLH[1] = UBX_SYNCH_2; // Sync char 2 iniPosLLH[2] = UBX_CLASS_MGA; // Class iniPosLLH[3] = UBX_MGA_INI_POS_LLH; // ID iniPosLLH[4] = 20; // Length LSB iniPosLLH[5] = 0x00; // Length MSB iniPosLLH[6] = 0x01; // type iniPosLLH[7] = 0x00; // version union // Use a union to convert from int32_t to uint32_t { int32_t signedLong; uint32_t unsignedLong; } signedUnsigned; signedUnsigned.signedLong = lat; iniPosLLH[10] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosLLH[11] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosLLH[12] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosLLH[13] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB signedUnsigned.signedLong = lon; iniPosLLH[14] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosLLH[15] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosLLH[16] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosLLH[17] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB signedUnsigned.signedLong = alt; iniPosLLH[18] = (uint8_t)(signedUnsigned.unsignedLong & 0xFF); // LSB iniPosLLH[19] = (uint8_t)((signedUnsigned.unsignedLong >> 8) & 0xFF); iniPosLLH[20] = (uint8_t)((signedUnsigned.unsignedLong >> 16) & 0xFF); iniPosLLH[21] = (uint8_t)(signedUnsigned.unsignedLong >> 24); // MSB iniPosLLH[22] = (uint8_t)(posAcc & 0xFF); // LSB iniPosLLH[23] = (uint8_t)((posAcc >> 8) & 0xFF); iniPosLLH[24] = (uint8_t)((posAcc >> 16) & 0xFF); iniPosLLH[25] = (uint8_t)(posAcc >> 24); // MSB for (uint8_t i = 2; i < 26; i++) // Calculate the checksum { iniPosLLH[26] += iniPosLLH[i]; iniPosLLH[27] += iniPosLLH[26]; } // Return true if the one packet was pushed successfully return (pushAssistNowDataInternal(0, false, iniPosLLH, 28, mgaAck, maxWait) == 28); } // Find the start of the AssistNow Offline (UBX_MGA_ANO) data for the chosen day // The daysIntoFture parameter makes it easy to get the data for (e.g.) tomorrow based on today's date // Returns numDataBytes if unsuccessful // TO DO: enhance this so it will find the nearest data for the chosen day - instead of an exact match size_t SFE_UBLOX_GNSS::findMGAANOForDate(const String &dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture) { return (findMGAANOForDateInternal((const uint8_t *)dataBytes.c_str(), numDataBytes, year, month, day, daysIntoFuture)); } size_t SFE_UBLOX_GNSS::findMGAANOForDate(const uint8_t *dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture) { return (findMGAANOForDateInternal(dataBytes, numDataBytes, year, month, day, daysIntoFuture)); } size_t SFE_UBLOX_GNSS::findMGAANOForDateInternal(const uint8_t *dataBytes, size_t numDataBytes, uint16_t year, uint8_t month, uint8_t day, uint8_t daysIntoFuture) { size_t dataPtr = 0; // Pointer into dataBytes bool dateFound = false; // Flag to indicate when the date has been found // Calculate matchDay, matchMonth and matchYear uint8_t matchDay = day; uint8_t matchMonth = month; uint8_t matchYear = (uint8_t)(year - 2000); // Add on daysIntoFuture uint8_t daysIntoFutureCopy = daysIntoFuture; while (daysIntoFutureCopy > 0) { matchDay++; daysIntoFutureCopy--; switch (matchMonth) { case 1: case 3: case 5: case 7: case 8: case 10: case 12: if (matchDay == 32) { matchDay = 1; matchMonth++; if (matchMonth == 13) { matchMonth = 1; matchYear++; } } break; case 4: case 6: case 9: case 11: if (matchDay == 31) { matchDay = 1; matchMonth++; } break; default: // February if (((matchYear % 4) == 0) && (matchDay == 30)) { matchDay = 1; matchMonth++; } else if (((matchYear % 4) > 0) && (matchDay == 29)) { matchDay = 1; matchMonth++; } break; } } while ((!dateFound) && (dataPtr < numDataBytes)) // Keep going until we have found the date or processed all the bytes { // Start by checking the validity of the packet being pointed to bool dataIsOK = true; dataIsOK &= (*(dataBytes + dataPtr + 0) == UBX_SYNCH_1); // Check for 0xB5 dataIsOK &= (*(dataBytes + dataPtr + 1) == UBX_SYNCH_2); // Check for 0x62 dataIsOK &= (*(dataBytes + dataPtr + 2) == UBX_CLASS_MGA); // Check for class UBX-MGA size_t packetLength = ((size_t) * (dataBytes + dataPtr + 4)) | (((size_t) * (dataBytes + dataPtr + 5)) << 8); // Extract the length uint8_t checksumA = 0; uint8_t checksumB = 0; // Calculate the checksum bytes // Keep going until the end of the packet is reached (payloadPtr == (dataPtr + packetLength)) // or we reach the end of the AssistNow data (payloadPtr == numDataBytes) for (size_t payloadPtr = dataPtr + ((size_t)2); (payloadPtr < (dataPtr + packetLength + ((size_t)6))) && (payloadPtr < numDataBytes); payloadPtr++) { checksumA += *(dataBytes + payloadPtr); checksumB += checksumA; } // Check the checksum bytes dataIsOK &= (checksumA == *(dataBytes + dataPtr + packetLength + ((size_t)6))); dataIsOK &= (checksumB == *(dataBytes + dataPtr + packetLength + ((size_t)7))); dataIsOK &= ((dataPtr + packetLength + ((size_t)8)) <= numDataBytes); // Check we haven't overrun // If the data is valid, check for a date match if (dataIsOK) { if ((*(dataBytes + dataPtr + 3) == UBX_MGA_ANO) && (*(dataBytes + dataPtr + 10) == matchYear) && (*(dataBytes + dataPtr + 11) == matchMonth) && (*(dataBytes + dataPtr + 12) == matchDay)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("findMGAANOForDate: found date match at location ")); _debugSerial->println(dataPtr); } #endif dateFound = true; } else { // The data is valid, but these are not the droids we are looking for... dataPtr += packetLength + ((size_t)8); // Point to the next message } } else { #ifndef SFE_UBLOX_REDUCED_PROG_MEM // The data was invalid. Send a debug message and then try to find the next 0xB5 if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("findMGAANOForDate: bad data - ignored! dataPtr is ")); _debugSerial->println(dataPtr); } #endif while ((dataPtr < numDataBytes) && (*(dataBytes + ++dataPtr) != UBX_SYNCH_1)) { ; // Increment dataPtr until we are pointing at the next 0xB5 - or we reach the end of the data } } } return (dataPtr); } // Read the whole navigation data base. The receiver will send all available data from its internal database. // Data is written to dataBytes. Set maxNumDataBytes to the (maximum) size of dataBytes. // If the database exceeds maxNumDataBytes, the excess bytes will be lost. // The function returns the number of database bytes written to dataBytes. // The return value will be equal to maxNumDataBytes if excess data was received. // The function will timeout after maxWait milliseconds - in case the final UBX-MGA-ACK was missed. size_t SFE_UBLOX_GNSS::readNavigationDatabase(uint8_t *dataBytes, size_t maxNumDataBytes, uint16_t maxWait) { // Allocate RAM to store the MGA ACK message if (packetUBXMGAACK == NULL) initPacketUBXMGAACK(); // Check that RAM has been allocated for the MGA_ACK data if (packetUBXMGAACK == NULL) // Bail if the RAM allocation failed { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("readNavigationDatabase: packetUBXMGAACK RAM allocation failed!")); } #endif return ((size_t)0); } if (packetUBXMGAACK->head != packetUBXMGAACK->tail) // Does the MGA ACK ringbuffer contain any data? { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("readNavigationDatabase: packetUBXMGAACK contains unprocessed data. Clearing it.")); } #endif packetUBXMGAACK->tail = packetUBXMGAACK->head; // Clear the buffer by setting the tail equal to the head } // Allocate RAM to store the MGA DBD messages if (packetUBXMGADBD == NULL) initPacketUBXMGADBD(); // Check that RAM has been allocated for the MGA_DBD data if (packetUBXMGADBD == NULL) // Bail if the RAM allocation failed { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("readNavigationDatabase: packetUBXMGADBD RAM allocation failed!")); } #endif return ((size_t)0); } if (packetUBXMGADBD->head != packetUBXMGADBD->tail) // Does the MGA DBD ringbuffer contain any data? { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("readNavigationDatabase: packetUBXMGADBD contains unprocessed data. Clearing it.")); } #endif packetUBXMGADBD->tail = packetUBXMGADBD->head; // Clear the buffer by setting the tail equal to the head } // Record what ackAiding is currently set to so we can restore it uint8_t currentAckAiding = getAckAiding(); if (currentAckAiding == 255) currentAckAiding = 0; // If the get failed, disable the ACKs when returning // Enable ackAiding setAckAiding(1); // Record what i2cPollingWait is currently set to so we can restore it uint8_t currentI2cPollingWait = i2cPollingWait; // Set the I2C polling wait to 1ms i2cPollingWait = 1; // Construct the poll message: uint8_t pollNaviDatabase[8]; // Create the UBX-MGA-DBD message by hand memset(pollNaviDatabase, 0x00, 8); // Set all unused / reserved bytes and the checksum to zero pollNaviDatabase[0] = UBX_SYNCH_1; // Sync char 1 pollNaviDatabase[1] = UBX_SYNCH_2; // Sync char 2 pollNaviDatabase[2] = UBX_CLASS_MGA; // Class pollNaviDatabase[3] = UBX_MGA_DBD; // ID pollNaviDatabase[4] = 0x00; // Length LSB pollNaviDatabase[5] = 0x00; // Length MSB for (uint8_t i = 2; i < 6; i++) // Calculate the checksum { pollNaviDatabase[6] += pollNaviDatabase[i]; pollNaviDatabase[7] += pollNaviDatabase[6]; } // Push the poll message to the module. // Do not Wait for an ACK - the DBD data will start arriving immediately. size_t pushResult = pushAssistNowDataInternal(0, false, pollNaviDatabase, (size_t)8, SFE_UBLOX_MGA_ASSIST_ACK_NO, 0); // Check pushResult == 8 if (pushResult != 8) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("readNavigationDatabase: pushAssistNowDataInternal failed!")); } #endif i2cPollingWait = currentI2cPollingWait; // Restore i2cPollingWait setAckAiding(currentAckAiding); // Restore Ack Aiding return ((size_t)0); } // Now keep checking for the arrival of UBX-MGA-DBD packets and write them to dataBytes bool keepGoing = true; unsigned long startTime = millis(); uint32_t databaseEntriesRX = 0; // Keep track of how many database entries are received size_t numBytesReceived = 0; // Keep track of how many bytes are received while (keepGoing && (millis() < (startTime + maxWait))) { checkUblox(); while (packetUBXMGADBD->head != packetUBXMGADBD->tail) // Does the MGA DBD ringbuffer contain any data? { // The data will be valid - process will have already checked it. So we can simply copy the data into dataBuffer. // We do not need to check if there is room to store the entire database entry. pushAssistNowData will check the data before pushing it. if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryHeader1; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryHeader2; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryClass; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryID; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenLSB; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenMSB; size_t msgLen = (((size_t)packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenMSB) * 256) + ((size_t)packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryLenLSB); for (size_t i = 0; i < msgLen; i++) { if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntry[i]; } if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryChecksumA; if (numBytesReceived < maxNumDataBytes) *(dataBytes + (numBytesReceived++)) = packetUBXMGADBD->data[packetUBXMGADBD->tail].dbdEntryChecksumB; // Increment the tail packetUBXMGADBD->tail++; if (packetUBXMGADBD->tail == UBX_MGA_DBD_RINGBUFFER_LEN) packetUBXMGADBD->tail = 0; databaseEntriesRX++; // Increment the number of entries received } // The final MGA-ACK is sent at the end of the DBD packets. So, we need to check the ACK buffer _after_ the DBD buffer. while (packetUBXMGAACK->head != packetUBXMGAACK->tail) // Does the MGA ACK ringbuffer contain any data? { // Check if we've received the correct ACK bool idMatch = (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgId == UBX_MGA_DBD); // Check if the message ID matches bool dataAckd = true; dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[0] == (uint8_t)(databaseEntriesRX & 0xFF)); // Check if the ACK contents match databaseEntriesRX dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[1] == (uint8_t)((databaseEntriesRX >> 8) & 0xFF)); dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[2] == (uint8_t)((databaseEntriesRX >> 16) & 0xFF)); dataAckd &= (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[3] == (uint8_t)((databaseEntriesRX >> 24) & 0xFF)); if (idMatch && dataAckd) // Is the ACK valid? { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("readNavigationDatabase: ACK received. databaseEntriesRX is ")); _debugSerial->print(databaseEntriesRX); _debugSerial->print(F(". numBytesReceived is ")); _debugSerial->print(numBytesReceived); _debugSerial->print(F(". DBD read complete after ")); _debugSerial->print(millis() - startTime); _debugSerial->println(F(" ms")); } #endif keepGoing = false; } else if (idMatch) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->print(F("readNavigationDatabase: unexpected ACK received. databaseEntriesRX is 0x")); _debugSerial->print(databaseEntriesRX, HEX); _debugSerial->print(F(". msgPayloadStart is 0x")); for (uint8_t i = 4; i > 0; i--) { if (packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[i - 1] < 0x10) _debugSerial->print(F("0")); _debugSerial->print(packetUBXMGAACK->data[packetUBXMGAACK->tail].msgPayloadStart[i - 1], HEX); } _debugSerial->println(); } #endif } // Increment the tail packetUBXMGAACK->tail++; if (packetUBXMGAACK->tail == UBX_MGA_ACK_DATA0_RINGBUFFER_LEN) packetUBXMGAACK->tail = 0; } } if (keepGoing) // If keepGoing is still true, we must have timed out { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("readNavigationDatabase: DBD RX timed out!")); } #endif } i2cPollingWait = currentI2cPollingWait; // Restore i2cPollingWait setAckAiding(currentAckAiding); // Restore Ack Aiding return (numBytesReceived); } // PRIVATE: Allocate RAM for packetUBXMGADBD and initialize it bool SFE_UBLOX_GNSS::initPacketUBXMGADBD() { packetUBXMGADBD = new UBX_MGA_DBD_t; // Allocate RAM for the main struct if (packetUBXMGADBD == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXMGADBD: RAM alloc failed!")); #endif return (false); } packetUBXMGADBD->head = 0; // Initialize the ring buffer pointers packetUBXMGADBD->tail = 0; return (true); } // Support for data logging // Set the file buffer size. This must be called _before_ .begin void SFE_UBLOX_GNSS::setFileBufferSize(uint16_t bufferSize) { fileBufferSize = bufferSize; } // Return the file buffer size uint16_t SFE_UBLOX_GNSS::getFileBufferSize(void) { return (fileBufferSize); } // Extract numBytes of data from the file buffer. Copy it to destination. // It is the user's responsibility to ensure destination is large enough. // Returns the number of bytes extracted - which may be less than numBytes. uint16_t SFE_UBLOX_GNSS::extractFileBufferData(uint8_t *destination, uint16_t numBytes) { // Check how many bytes are available in the buffer uint16_t bytesAvailable = fileBufferSpaceUsed(); if (numBytes > bytesAvailable) // Limit numBytes if required numBytes = bytesAvailable; // Start copying at fileBufferTail. Wrap-around if required. uint16_t bytesBeforeWrapAround = fileBufferSize - fileBufferTail; // How much space is available 'above' Tail? if (bytesBeforeWrapAround > numBytes) // Will we need to wrap-around? { bytesBeforeWrapAround = numBytes; // We need to wrap-around } memcpy(destination, &ubxFileBuffer[fileBufferTail], bytesBeforeWrapAround); // Copy the data out of the buffer // Is there any data leftover which we need to copy from the 'bottom' of the buffer? uint16_t bytesLeftToCopy = numBytes - bytesBeforeWrapAround; // Calculate if there are any bytes left to copy if (bytesLeftToCopy > 0) // If there are bytes left to copy { memcpy(&destination[bytesBeforeWrapAround], &ubxFileBuffer[0], bytesLeftToCopy); // Copy the remaining data out of the buffer fileBufferTail = bytesLeftToCopy; // Update Tail. The next byte to be read will be read from here. } else { fileBufferTail += numBytes; // Only update Tail. The next byte to be read will be read from here. } return (numBytes); // Return the number of bytes extracted } // Returns the number of bytes available in file buffer which are waiting to be read uint16_t SFE_UBLOX_GNSS::fileBufferAvailable(void) { return (fileBufferSpaceUsed()); } // Returns the maximum number of bytes which the file buffer contained. // Handy for checking the buffer is large enough to handle all the incoming data. uint16_t SFE_UBLOX_GNSS::getMaxFileBufferAvail(void) { return (fileBufferMaxAvail); } // Clear the file buffer - discard all contents void SFE_UBLOX_GNSS::clearFileBuffer(void) { if (fileBufferSize == 0) // Bail if the user has not called setFileBufferSize (probably redundant) return; fileBufferTail = fileBufferHead; } // Reset fileBufferMaxAvail void SFE_UBLOX_GNSS::clearMaxFileBufferAvail(void) { fileBufferMaxAvail = 0; } // PRIVATE: Create the file buffer. Called by .begin bool SFE_UBLOX_GNSS::createFileBuffer(void) { if (fileBufferSize == 0) // Bail if the user has not called setFileBufferSize { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("createFileBuffer: Warning. fileBufferSize is zero. Data logging is not possible.")); } #endif return (false); } if (ubxFileBuffer != NULL) // Bail if RAM has already been allocated for the file buffer { // This will happen if you call .begin more than once - without calling .end first #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("createFileBuffer: Warning. File buffer already exists. Skipping...")); } #endif return (false); } ubxFileBuffer = new uint8_t[fileBufferSize]; // Allocate RAM for the buffer if (ubxFileBuffer == NULL) // Check if the new (alloc) was successful { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("createFileBuffer: RAM alloc failed!")); } return (false); } #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("createFileBuffer: fileBufferSize is: ")); _debugSerial->println(fileBufferSize); } #endif fileBufferHead = 0; // Initialize head and tail fileBufferTail = 0; return (true); } // PRIVATE: Check how much space is available in the buffer uint16_t SFE_UBLOX_GNSS::fileBufferSpaceAvailable(void) { return (fileBufferSize - fileBufferSpaceUsed()); } // PRIVATE: Check how much space is used in the buffer uint16_t SFE_UBLOX_GNSS::fileBufferSpaceUsed(void) { if (fileBufferHead >= fileBufferTail) // Check if wrap-around has occurred { // Wrap-around has not occurred so do a simple subtraction return (fileBufferHead - fileBufferTail); } else { // Wrap-around has occurred so do a simple subtraction but add in the fileBufferSize return ((uint16_t)(((uint32_t)fileBufferHead + (uint32_t)fileBufferSize) - (uint32_t)fileBufferTail)); } } // PRIVATE: Add a UBX packet to the file buffer bool SFE_UBLOX_GNSS::storePacket(ubxPacket *msg) { // First, check that the file buffer has been created if ((ubxFileBuffer == NULL) || (fileBufferSize == 0)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("storePacket: file buffer not available!")); } #endif return (false); } // Now, check if there is enough space in the buffer for all of the data uint16_t totalLength = msg->len + 8; // Total length. Include sync chars, class, id, length and checksum bytes if (totalLength > fileBufferSpaceAvailable()) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("storePacket: insufficient space available! Data will be lost!")); } #endif return (false); } // Store the two sync chars uint8_t sync_chars[] = {UBX_SYNCH_1, UBX_SYNCH_2}; writeToFileBuffer(sync_chars, 2); // Store the Class & ID writeToFileBuffer(&msg->cls, 1); writeToFileBuffer(&msg->id, 1); // Store the length. Ensure length is little-endian uint8_t msg_length[2]; msg_length[0] = msg->len & 0xFF; msg_length[1] = msg->len >> 8; writeToFileBuffer(msg_length, 2); // Store the payload writeToFileBuffer(msg->payload, msg->len); // Store the checksum writeToFileBuffer(&msg->checksumA, 1); writeToFileBuffer(&msg->checksumB, 1); return (true); } // PRIVATE: Add theBytes to the file buffer bool SFE_UBLOX_GNSS::storeFileBytes(uint8_t *theBytes, uint16_t numBytes) { // First, check that the file buffer has been created if ((ubxFileBuffer == NULL) || (fileBufferSize == 0)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("storeFileBytes: file buffer not available!")); } #endif return (false); } // Now, check if there is enough space in the buffer for all of the data if (numBytes > fileBufferSpaceAvailable()) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("storeFileBytes: insufficient space available! Data will be lost!")); } #endif return (false); } // There is room for all the data in the buffer so copy the data into the buffer writeToFileBuffer(theBytes, numBytes); return (true); } // PRIVATE: Write theBytes to the file buffer void SFE_UBLOX_GNSS::writeToFileBuffer(uint8_t *theBytes, uint16_t numBytes) { // Start writing at fileBufferHead. Wrap-around if required. uint16_t bytesBeforeWrapAround = fileBufferSize - fileBufferHead; // How much space is available 'above' Head? if (bytesBeforeWrapAround > numBytes) // Is there enough room for all the data? { bytesBeforeWrapAround = numBytes; // There is enough room for all the data } memcpy(&ubxFileBuffer[fileBufferHead], theBytes, bytesBeforeWrapAround); // Copy the data into the buffer // Is there any data leftover which we need to copy to the 'bottom' of the buffer? uint16_t bytesLeftToCopy = numBytes - bytesBeforeWrapAround; // Calculate if there are any bytes left to copy if (bytesLeftToCopy > 0) // If there are bytes left to copy { memcpy(&ubxFileBuffer[0], &theBytes[bytesBeforeWrapAround], bytesLeftToCopy); // Copy the remaining data into the buffer fileBufferHead = bytesLeftToCopy; // Update Head. The next byte written will be written here. } else { fileBufferHead += numBytes; // Only update Head. The next byte written will be written here. } // Update fileBufferMaxAvail if required uint16_t bytesInBuffer = fileBufferSpaceUsed(); if (bytesInBuffer > fileBufferMaxAvail) fileBufferMaxAvail = bytesInBuffer; } //=-=-=-=-=-=-=-= Specific commands =-=-=-=-=-=-=-==-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // Loads the payloadCfg array with the current protocol bits located the UBX-CFG-PRT register for a given port bool SFE_UBLOX_GNSS::getPortSettings(uint8_t portID, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 1; packetCfg.startingSpot = 0; payloadCfg[0] = portID; return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_RECEIVED); // We are expecting data and an ACK } // Configure a given port to output UBX, NMEA, RTCM3 or a combination thereof // Port 0=I2c, 1=UART1, 2=UART2, 3=USB, 4=SPI // Bit:0 = UBX, :1=NMEA, :5=RTCM3 bool SFE_UBLOX_GNSS::setPortOutput(uint8_t portID, uint8_t outStreamSettings, uint16_t maxWait) { // Get the current config values for this port ID if (getPortSettings(portID, maxWait) == false) return (false); // Something went wrong. Bail. packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; packetCfg.startingSpot = 0; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[14] = outStreamSettings; // OutProtocolMask LSB - Set outStream bits return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Configure a given port to input UBX, NMEA, RTCM3 or a combination thereof // Port 0=I2c, 1=UART1, 2=UART2, 3=USB, 4=SPI // Bit:0 = UBX, :1=NMEA, :5=RTCM3 bool SFE_UBLOX_GNSS::setPortInput(uint8_t portID, uint8_t inStreamSettings, uint16_t maxWait) { // Get the current config values for this port ID // This will load the payloadCfg array with current port settings if (getPortSettings(portID, maxWait) == false) return (false); // Something went wrong. Bail. packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; packetCfg.startingSpot = 0; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[12] = inStreamSettings; // InProtocolMask LSB - Set inStream bits return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Changes the I2C address that the u-blox module responds to // 0x42 is the default but can be changed with this command bool SFE_UBLOX_GNSS::setI2CAddress(uint8_t deviceAddress, uint16_t maxWait) { // Get the current config values for the I2C port // This will load the payloadCfg array with current port settings if (getPortSettings(COM_PORT_I2C, maxWait) == false) return (false); // Something went wrong. Bail. packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; packetCfg.startingSpot = 0; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[4] = deviceAddress << 1; // DDC mode LSB if (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT) // We are only expecting an ACK { // Success! Now change our internal global. _gpsI2Caddress = deviceAddress; // Store the I2C address from user return (true); } return (false); } // Changes the serial baud rate of the u-blox module, can't return success/fail 'cause ACK from modem // is lost due to baud rate change void SFE_UBLOX_GNSS::setSerialRate(uint32_t baudrate, uint8_t uartPort, uint16_t maxWait) { // Get the current config values for the UART port // This will load the payloadCfg array with current port settings if (getPortSettings(uartPort, maxWait) == false) return; // Something went wrong. Bail. #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("Current baud rate: ")); _debugSerial->println(((uint32_t)payloadCfg[10] << 16) | ((uint32_t)payloadCfg[9] << 8) | payloadCfg[8]); } #endif packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 20; packetCfg.startingSpot = 0; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[8] = baudrate; payloadCfg[9] = baudrate >> 8; payloadCfg[10] = baudrate >> 16; payloadCfg[11] = baudrate >> 24; #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("New baud rate:")); _debugSerial->println(((uint32_t)payloadCfg[10] << 16) | ((uint32_t)payloadCfg[9] << 8) | payloadCfg[8]); } #endif sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("setSerialRate: sendCommand returned: ")); _debugSerial->println(statusString(retVal)); } #else (void)retVal; // Get rid of a pesky compiler warning! #endif } // Configure a port to output UBX, NMEA, RTCM3 or a combination thereof bool SFE_UBLOX_GNSS::setI2COutput(uint8_t comSettings, uint16_t maxWait) { return (setPortOutput(COM_PORT_I2C, comSettings, maxWait)); } bool SFE_UBLOX_GNSS::setUART1Output(uint8_t comSettings, uint16_t maxWait) { return (setPortOutput(COM_PORT_UART1, comSettings, maxWait)); } bool SFE_UBLOX_GNSS::setUART2Output(uint8_t comSettings, uint16_t maxWait) { return (setPortOutput(COM_PORT_UART2, comSettings, maxWait)); } bool SFE_UBLOX_GNSS::setUSBOutput(uint8_t comSettings, uint16_t maxWait) { return (setPortOutput(COM_PORT_USB, comSettings, maxWait)); } bool SFE_UBLOX_GNSS::setSPIOutput(uint8_t comSettings, uint16_t maxWait) { return (setPortOutput(COM_PORT_SPI, comSettings, maxWait)); } // Want to see the NMEA messages on the Serial port? Here's how void SFE_UBLOX_GNSS::setNMEAOutputPort(Stream &nmeaOutputPort) { _nmeaOutputPort = &nmeaOutputPort; // Store the port from user } void SFE_UBLOX_GNSS::setOutputPort(Stream &outputPort) { _outputPort = &outputPort; // Store the port from user } // Reset to defaults void SFE_UBLOX_GNSS::factoryReset() { // Copy default settings to permanent // Note: this does not load the permanent configuration into the current configuration. Calling factoryDefault() will do that. packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_CFG; packetCfg.len = 13; packetCfg.startingSpot = 0; for (uint8_t i = 0; i < 4; i++) { payloadCfg[0 + i] = 0xff; // clear mask: copy default config to permanent config payloadCfg[4 + i] = 0x00; // save mask: don't save current to permanent payloadCfg[8 + i] = 0x00; // load mask: don't copy permanent config to current } payloadCfg[12] = 0xff; // all forms of permanent memory sendCommand(&packetCfg, 0); // don't expect ACK hardReset(); // cause factory default config to actually be loaded and used cleanly } void SFE_UBLOX_GNSS::hardReset() { // Issue hard reset packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RST; packetCfg.len = 4; packetCfg.startingSpot = 0; payloadCfg[0] = 0xff; // cold start payloadCfg[1] = 0xff; // cold start payloadCfg[2] = 0; // 0=HW reset payloadCfg[3] = 0; // reserved sendCommand(&packetCfg, 0); // don't expect ACK } void SFE_UBLOX_GNSS::softwareResetGNSSOnly() { // Issue controlled software reset (GNSS only) packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RST; packetCfg.len = 4; packetCfg.startingSpot = 0; payloadCfg[0] = 0; // hot start payloadCfg[1] = 0; // hot start payloadCfg[2] = 0x02; // 0x02 = Controlled software reset (GNSS only) payloadCfg[3] = 0; // reserved sendCommand(&packetCfg, 0); // don't expect ACK } // Reset module to factory defaults // This still works but it is the old way of configuring ublox modules. See getVal and setVal for the new methods bool SFE_UBLOX_GNSS::factoryDefault(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_CFG; packetCfg.len = 12; packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); packetCfg.payload[0] = 0xFF; // Set any bit in the clearMask field to clear saved config packetCfg.payload[1] = 0xFF; packetCfg.payload[8] = 0xFF; // Set any bit in the loadMask field to discard current config and rebuild from lower non-volatile memory layers packetCfg.payload[9] = 0xFF; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Save configuration to BBR / Flash // Save current configuration to flash and BBR (battery backed RAM) // This still works but it is the old way of configuring ublox modules. See getVal and setVal for the new methods bool SFE_UBLOX_GNSS::saveConfiguration(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_CFG; packetCfg.len = 12; packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); packetCfg.payload[4] = 0xFF; // Set any bit in the saveMask field to save current config to Flash and BBR packetCfg.payload[5] = 0xFF; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Save the selected configuration sub-sections to flash and BBR (battery backed RAM) // This still works but it is the old way of configuring ublox modules. See getVal and setVal for the new methods bool SFE_UBLOX_GNSS::saveConfigSelective(uint32_t configMask, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_CFG; packetCfg.len = 12; packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); packetCfg.payload[4] = configMask & 0xFF; // Set the appropriate bits in the saveMask field to save current config to Flash and BBR packetCfg.payload[5] = (configMask >> 8) & 0xFF; packetCfg.payload[6] = (configMask >> 16) & 0xFF; packetCfg.payload[7] = (configMask >> 24) & 0xFF; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Configure a given message type for a given port (UART1, I2C, SPI, etc) bool SFE_UBLOX_GNSS::configureMessage(uint8_t msgClass, uint8_t msgID, uint8_t portID, uint8_t sendRate, uint16_t maxWait) { // Poll for the current settings for a given message packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 2; packetCfg.startingSpot = 0; payloadCfg[0] = msgClass; payloadCfg[1] = msgID; // This will load the payloadCfg array with current settings of the given register if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // If command send fails then bail // Now send it back with new mods packetCfg.len = 8; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[2 + portID] = sendRate; // Send rate is relative to the event a message is registered on. For example, if the rate of a navigation message is set to 2, the message is sent every 2nd navigation solution. return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Enable a given message type, default of 1 per update rate (usually 1 per second) bool SFE_UBLOX_GNSS::enableMessage(uint8_t msgClass, uint8_t msgID, uint8_t portID, uint8_t rate, uint16_t maxWait) { return (configureMessage(msgClass, msgID, portID, rate, maxWait)); } // Disable a given message type on a given port bool SFE_UBLOX_GNSS::disableMessage(uint8_t msgClass, uint8_t msgID, uint8_t portID, uint16_t maxWait) { return (configureMessage(msgClass, msgID, portID, 0, maxWait)); } bool SFE_UBLOX_GNSS::enableNMEAMessage(uint8_t msgID, uint8_t portID, uint8_t rate, uint16_t maxWait) { return (configureMessage(UBX_CLASS_NMEA, msgID, portID, rate, maxWait)); } bool SFE_UBLOX_GNSS::disableNMEAMessage(uint8_t msgID, uint8_t portID, uint16_t maxWait) { return (enableNMEAMessage(msgID, portID, 0, maxWait)); } // Given a message number turns on a message ID for output over a given portID (UART, I2C, SPI, USB, etc) // To disable a message, set secondsBetween messages to 0 // Note: This function will return false if the message is already enabled // For base station RTK output we need to enable various sentences // NEO-M8P has four: // 1005 = 0xF5 0x05 - Stationary RTK reference ARP // 1077 = 0xF5 0x4D - GPS MSM7 // 1087 = 0xF5 0x57 - GLONASS MSM7 // 1230 = 0xF5 0xE6 - GLONASS code-phase biases, set to once every 10 seconds // ZED-F9P has six: // 1005, 1074, 1084, 1094, 1124, 1230 // Much of this configuration is not documented and instead discerned from u-center binary console bool SFE_UBLOX_GNSS::enableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint8_t sendRate, uint16_t maxWait) { return (configureMessage(UBX_RTCM_MSB, messageNumber, portID, sendRate, maxWait)); } // Disable a given message on a given port by setting secondsBetweenMessages to zero bool SFE_UBLOX_GNSS::disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait) { return (enableRTCMmessage(messageNumber, portID, 0, maxWait)); } // Functions used for RTK and base station setup // Get the current TimeMode3 settings - these contain survey in statuses bool SFE_UBLOX_GNSS::getSurveyMode(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TMODE3; packetCfg.len = 0; packetCfg.startingSpot = 0; return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_RECEIVED); // We are expecting data and an ACK } // Get the current TimeMode3 settings - these contain survey in statuses bool SFE_UBLOX_GNSS::getSurveyMode(UBX_CFG_TMODE3_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TMODE3; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->version = extractByte(&packetCfg, 0); data->flags.all = extractInt(&packetCfg, 2); data->ecefXOrLat = extractSignedLong(&packetCfg, 4); data->ecefYOrLon = extractSignedLong(&packetCfg, 8); data->ecefZOrAlt = extractSignedLong(&packetCfg, 12); data->ecefXOrLatHP = extractSignedChar(&packetCfg, 16); data->ecefYOrLonHP = extractSignedChar(&packetCfg, 17); data->ecefZOrAltHP = extractSignedChar(&packetCfg, 18); data->fixedPosAcc = extractLong(&packetCfg, 20); data->svinMinDur = extractLong(&packetCfg, 24); data->svinAccLimit = extractLong(&packetCfg, 28); return (true); } // Control Survey-In for NEO-M8P bool SFE_UBLOX_GNSS::setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait) { return (setSurveyModeFull(mode, (uint32_t)observationTime, requiredAccuracy, maxWait)); } bool SFE_UBLOX_GNSS::setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait) { if (getSurveyMode(maxWait) == false) // Ask module for the current TimeMode3 settings. Loads into payloadCfg. return (false); packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TMODE3; packetCfg.len = 40; packetCfg.startingSpot = 0; // payloadCfg should be loaded with poll response. Now modify only the bits we care about payloadCfg[2] = mode; // Set mode. Survey-In and Disabled are most common. Use ECEF (not LAT/LON/ALT). // svinMinDur is U4 (uint32_t) in seconds payloadCfg[24] = observationTime & 0xFF; // svinMinDur in seconds payloadCfg[25] = (observationTime >> 8) & 0xFF; payloadCfg[26] = (observationTime >> 16) & 0xFF; payloadCfg[27] = (observationTime >> 24) & 0xFF; // svinAccLimit is U4 (uint32_t) in 0.1mm. uint32_t svinAccLimit = (uint32_t)(requiredAccuracy * 10000.0); // Convert m to 0.1mm payloadCfg[28] = svinAccLimit & 0xFF; // svinAccLimit in 0.1mm increments payloadCfg[29] = (svinAccLimit >> 8) & 0xFF; payloadCfg[30] = (svinAccLimit >> 16) & 0xFF; payloadCfg[31] = (svinAccLimit >> 24) & 0xFF; return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Begin Survey-In for NEO-M8P bool SFE_UBLOX_GNSS::enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait) { return (setSurveyModeFull(SVIN_MODE_ENABLE, (uint32_t)observationTime, requiredAccuracy, maxWait)); } bool SFE_UBLOX_GNSS::enableSurveyModeFull(uint32_t observationTime, float requiredAccuracy, uint16_t maxWait) { return (setSurveyModeFull(SVIN_MODE_ENABLE, observationTime, requiredAccuracy, maxWait)); } // Stop Survey-In for NEO-M8P bool SFE_UBLOX_GNSS::disableSurveyMode(uint16_t maxWait) { return (setSurveyMode(SVIN_MODE_DISABLE, 0, 0, maxWait)); } // Set the ECEF or Lat/Long coordinates of a receiver // This imediately puts the receiver in TIME mode (fixed) and will begin outputting RTCM sentences if enabled // This is helpful once an antenna's position has been established. See this tutorial: https://learn.sparkfun.com/tutorials/how-to-build-a-diy-gnss-reference-station#gather-raw-gnss-data // For ECEF the units are: cm, 0.1mm, cm, 0.1mm, cm, 0.1mm // For Lat/Lon/Alt the units are: degrees^-7, degrees^-9, degrees^-7, degrees^-9, cm, 0.1mm bool SFE_UBLOX_GNSS::setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TMODE3; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current TimeMode3 settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (false); packetCfg.len = 40; // customCfg should be loaded with poll response. Now modify only the bits we care about payloadCfg[2] = 2; // Set mode to fixed. Use ECEF (not LAT/LON/ALT). if (latLong == true) payloadCfg[3] = (uint8_t)(1 << 0); // Set mode to fixed. Use LAT/LON/ALT. // Set ECEF X or Lat payloadCfg[4] = (ecefXOrLat >> 8 * 0) & 0xFF; // LSB payloadCfg[5] = (ecefXOrLat >> 8 * 1) & 0xFF; payloadCfg[6] = (ecefXOrLat >> 8 * 2) & 0xFF; payloadCfg[7] = (ecefXOrLat >> 8 * 3) & 0xFF; // MSB // Set ECEF Y or Long payloadCfg[8] = (ecefYOrLon >> 8 * 0) & 0xFF; // LSB payloadCfg[9] = (ecefYOrLon >> 8 * 1) & 0xFF; payloadCfg[10] = (ecefYOrLon >> 8 * 2) & 0xFF; payloadCfg[11] = (ecefYOrLon >> 8 * 3) & 0xFF; // MSB // Set ECEF Z or Altitude payloadCfg[12] = (ecefZOrAlt >> 8 * 0) & 0xFF; // LSB payloadCfg[13] = (ecefZOrAlt >> 8 * 1) & 0xFF; payloadCfg[14] = (ecefZOrAlt >> 8 * 2) & 0xFF; payloadCfg[15] = (ecefZOrAlt >> 8 * 3) & 0xFF; // MSB // Set high precision parts payloadCfg[16] = ecefXOrLatHP; payloadCfg[17] = ecefYOrLonHP; payloadCfg[18] = ecefZOrAltHP; return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } bool SFE_UBLOX_GNSS::setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latlong, uint16_t maxWait) { return (setStaticPosition(ecefXOrLat, 0, ecefYOrLon, 0, ecefZOrAlt, 0, latlong, maxWait)); } // Set the DGNSS differential mode bool SFE_UBLOX_GNSS::setDGNSSConfiguration(sfe_ublox_dgnss_mode_e dgnssMode, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_DGNSS; packetCfg.len = 4; packetCfg.startingSpot = 0; payloadCfg[0] = (uint8_t)dgnssMode; payloadCfg[1] = 0; // reserved0 payloadCfg[2] = 0; payloadCfg[3] = 0; return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Module Protocol Version // Get the current protocol version of the u-blox module we're communicating with // This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) uint8_t SFE_UBLOX_GNSS::getProtocolVersionHigh(uint16_t maxWait) { if (moduleSWVersion == NULL) initModuleSWVersion(); // Check that RAM has been allocated for the SW version if (moduleSWVersion == NULL) // Bail if the RAM allocation failed return (false); if (moduleSWVersion->moduleQueried == false) getProtocolVersion(maxWait); return (moduleSWVersion->versionHigh); } // Get the current protocol version of the u-blox module we're communicating with // This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) uint8_t SFE_UBLOX_GNSS::getProtocolVersionLow(uint16_t maxWait) { if (moduleSWVersion == NULL) initModuleSWVersion(); // Check that RAM has been allocated for the SW version if (moduleSWVersion == NULL) // Bail if the RAM allocation failed return (false); if (moduleSWVersion->moduleQueried == false) getProtocolVersion(maxWait); return (moduleSWVersion->versionLow); } // Get the current protocol version of the u-blox module we're communicating with // This is helpful when deciding if we should call the high-precision Lat/Long (HPPOSLLH) or the regular (POSLLH) bool SFE_UBLOX_GNSS::getProtocolVersion(uint16_t maxWait) { if (moduleSWVersion == NULL) initModuleSWVersion(); // Check that RAM has been allocated for the SW version if (moduleSWVersion == NULL) // Bail if the RAM allocation failed return (false); // Send packet with only CLS and ID, length of zero. This will cause the module to respond with the contents of that CLS/ID. packetCfg.cls = UBX_CLASS_MON; packetCfg.id = UBX_MON_VER; packetCfg.len = 0; packetCfg.startingSpot = 40; // Start at first "extended software information" string if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are only expecting data (no ACK) return (false); // If command send fails then bail // Payload should now contain ~220 characters (depends on module type) // if (_printDebug == true) // { // _debugSerial->print(F("MON VER Payload:")); // for (int location = 0; location < packetCfg.len; location++) // { // if (location % 30 == 0) // _debugSerial->println(); // _debugSerial->write(payloadCfg[location]); // } // _debugSerial->println(); // } // We will step through the payload looking at each extension field of 30 bytes char *ptr; for (uint8_t extensionNumber = 0; extensionNumber < ((packetCfg.len - 40) / 30); extensionNumber++) { ptr = strstr((const char *)&payloadCfg[(30 * extensionNumber)], "PROTVER="); // Check for PROTVER (should be in extension 2) if (ptr != nullptr) { ptr += strlen("PROTVER="); // Point to the protocol version int protHi = 0; int protLo = 0; int scanned = sscanf(ptr, "%d.%d", &protHi, &protLo); if (scanned == 2) // Check we extracted the firmware version successfully { moduleSWVersion->versionHigh = protHi; moduleSWVersion->versionLow = protLo; moduleSWVersion->moduleQueried = true; // Mark this data as new #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("Protocol version: ")); _debugSerial->print(moduleSWVersion->versionHigh); _debugSerial->print(F(".")); _debugSerial->println(moduleSWVersion->versionLow); } #endif return (true); // Success! } } } return (false); // We failed } // PRIVATE: Allocate RAM for moduleSWVersion and initialize it bool SFE_UBLOX_GNSS::initModuleSWVersion() { moduleSWVersion = new moduleSWVersion_t; // Allocate RAM for the main struct if (moduleSWVersion == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initModuleSWVersion: RAM alloc failed!")); #endif return (false); } moduleSWVersion->versionHigh = 0; moduleSWVersion->versionLow = 0; moduleSWVersion->moduleQueried = false; return (true); } // Geofences // Add a new geofence using UBX-CFG-GEOFENCE bool SFE_UBLOX_GNSS::addGeofence(int32_t latitude, int32_t longitude, uint32_t radius, byte confidence, byte pinPolarity, byte pin, uint16_t maxWait) { if (currentGeofenceParams == NULL) initGeofenceParams(); // Check if RAM has been allocated for currentGeofenceParams if (currentGeofenceParams == NULL) // Abort if the RAM allocation failed return (false); if (currentGeofenceParams->numFences >= 4) return (false); // Quit if we already have four geofences defined // Store the new geofence parameters currentGeofenceParams->lats[currentGeofenceParams->numFences] = latitude; currentGeofenceParams->longs[currentGeofenceParams->numFences] = longitude; currentGeofenceParams->rads[currentGeofenceParams->numFences] = radius; currentGeofenceParams->numFences = currentGeofenceParams->numFences + 1; // Increment the number of fences packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_GEOFENCE; packetCfg.len = (currentGeofenceParams->numFences * 12) + 8; packetCfg.startingSpot = 0; payloadCfg[0] = 0; // Message version = 0x00 payloadCfg[1] = currentGeofenceParams->numFences; // numFences payloadCfg[2] = confidence; // confLvl = Confidence level 0-4 (none, 68%, 95%, 99.7%, 99.99%) payloadCfg[3] = 0; // reserved1 if (pin > 0) { payloadCfg[4] = 1; // enable PIO combined fence state } else { payloadCfg[4] = 0; // disable PIO combined fence state } payloadCfg[5] = pinPolarity; // PIO pin polarity (0 = low means inside, 1 = low means outside (or unknown)) payloadCfg[6] = pin; // PIO pin payloadCfg[7] = 0; // reserved2 payloadCfg[8] = currentGeofenceParams->lats[0] & 0xFF; payloadCfg[9] = currentGeofenceParams->lats[0] >> 8; payloadCfg[10] = currentGeofenceParams->lats[0] >> 16; payloadCfg[11] = currentGeofenceParams->lats[0] >> 24; payloadCfg[12] = currentGeofenceParams->longs[0] & 0xFF; payloadCfg[13] = currentGeofenceParams->longs[0] >> 8; payloadCfg[14] = currentGeofenceParams->longs[0] >> 16; payloadCfg[15] = currentGeofenceParams->longs[0] >> 24; payloadCfg[16] = currentGeofenceParams->rads[0] & 0xFF; payloadCfg[17] = currentGeofenceParams->rads[0] >> 8; payloadCfg[18] = currentGeofenceParams->rads[0] >> 16; payloadCfg[19] = currentGeofenceParams->rads[0] >> 24; if (currentGeofenceParams->numFences >= 2) { payloadCfg[20] = currentGeofenceParams->lats[1] & 0xFF; payloadCfg[21] = currentGeofenceParams->lats[1] >> 8; payloadCfg[22] = currentGeofenceParams->lats[1] >> 16; payloadCfg[23] = currentGeofenceParams->lats[1] >> 24; payloadCfg[24] = currentGeofenceParams->longs[1] & 0xFF; payloadCfg[25] = currentGeofenceParams->longs[1] >> 8; payloadCfg[26] = currentGeofenceParams->longs[1] >> 16; payloadCfg[27] = currentGeofenceParams->longs[1] >> 24; payloadCfg[28] = currentGeofenceParams->rads[1] & 0xFF; payloadCfg[29] = currentGeofenceParams->rads[1] >> 8; payloadCfg[30] = currentGeofenceParams->rads[1] >> 16; payloadCfg[31] = currentGeofenceParams->rads[1] >> 24; } if (currentGeofenceParams->numFences >= 3) { payloadCfg[32] = currentGeofenceParams->lats[2] & 0xFF; payloadCfg[33] = currentGeofenceParams->lats[2] >> 8; payloadCfg[34] = currentGeofenceParams->lats[2] >> 16; payloadCfg[35] = currentGeofenceParams->lats[2] >> 24; payloadCfg[36] = currentGeofenceParams->longs[2] & 0xFF; payloadCfg[37] = currentGeofenceParams->longs[2] >> 8; payloadCfg[38] = currentGeofenceParams->longs[2] >> 16; payloadCfg[39] = currentGeofenceParams->longs[2] >> 24; payloadCfg[40] = currentGeofenceParams->rads[2] & 0xFF; payloadCfg[41] = currentGeofenceParams->rads[2] >> 8; payloadCfg[42] = currentGeofenceParams->rads[2] >> 16; payloadCfg[43] = currentGeofenceParams->rads[2] >> 24; } if (currentGeofenceParams->numFences >= 4) { payloadCfg[44] = currentGeofenceParams->lats[3] & 0xFF; payloadCfg[45] = currentGeofenceParams->lats[3] >> 8; payloadCfg[46] = currentGeofenceParams->lats[3] >> 16; payloadCfg[47] = currentGeofenceParams->lats[3] >> 24; payloadCfg[48] = currentGeofenceParams->longs[3] & 0xFF; payloadCfg[49] = currentGeofenceParams->longs[3] >> 8; payloadCfg[50] = currentGeofenceParams->longs[3] >> 16; payloadCfg[51] = currentGeofenceParams->longs[3] >> 24; payloadCfg[52] = currentGeofenceParams->rads[3] & 0xFF; payloadCfg[53] = currentGeofenceParams->rads[3] >> 8; payloadCfg[54] = currentGeofenceParams->rads[3] >> 16; payloadCfg[55] = currentGeofenceParams->rads[3] >> 24; } return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Clear all geofences using UBX-CFG-GEOFENCE bool SFE_UBLOX_GNSS::clearGeofences(uint16_t maxWait) { if (currentGeofenceParams == NULL) initGeofenceParams(); // Check if RAM has been allocated for currentGeofenceParams if (currentGeofenceParams == NULL) // Abort if the RAM allocation failed return (false); packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_GEOFENCE; packetCfg.len = 8; packetCfg.startingSpot = 0; payloadCfg[0] = 0; // Message version = 0x00 payloadCfg[1] = 0; // numFences payloadCfg[2] = 0; // confLvl payloadCfg[3] = 0; // reserved1 payloadCfg[4] = 0; // disable PIO combined fence state payloadCfg[5] = 0; // PIO pin polarity (0 = low means inside, 1 = low means outside (or unknown)) payloadCfg[6] = 0; // PIO pin payloadCfg[7] = 0; // reserved2 currentGeofenceParams->numFences = 0; // Zero the number of geofences currently in use return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Clear the antenna control settings using UBX-CFG-ANT // This function is hopefully redundant but may be needed to release // any PIO pins pre-allocated for antenna functions bool SFE_UBLOX_GNSS::clearAntPIO(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ANT; packetCfg.len = 4; packetCfg.startingSpot = 0; payloadCfg[0] = 0x10; // Antenna flag mask: set the recovery bit payloadCfg[1] = 0; payloadCfg[2] = 0xFF; // Antenna pin configuration: set pinSwitch and pinSCD to 31 payloadCfg[3] = 0xFF; // Antenna pin configuration: set pinOCD to 31, set reconfig bit return ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Returns the combined geofence state using UBX-NAV-GEOFENCE bool SFE_UBLOX_GNSS::getGeofenceState(geofenceState ¤tGeofenceState, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_GEOFENCE; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the geofence status. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); currentGeofenceState.status = payloadCfg[5]; // Extract the status currentGeofenceState.numFences = payloadCfg[6]; // Extract the number of geofences currentGeofenceState.combState = payloadCfg[7]; // Extract the combined state of all geofences if (currentGeofenceState.numFences > 0) currentGeofenceState.states[0] = payloadCfg[8]; // Extract geofence 1 state if (currentGeofenceState.numFences > 1) currentGeofenceState.states[1] = payloadCfg[10]; // Extract geofence 2 state if (currentGeofenceState.numFences > 2) currentGeofenceState.states[2] = payloadCfg[12]; // Extract geofence 3 state if (currentGeofenceState.numFences > 3) currentGeofenceState.states[3] = payloadCfg[14]; // Extract geofence 4 state return (true); } // PRIVATE: Allocate RAM for currentGeofenceParams and initialize it bool SFE_UBLOX_GNSS::initGeofenceParams() { currentGeofenceParams = new geofenceParams_t; // Allocate RAM for the main struct if (currentGeofenceParams == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initGeofenceParams: RAM alloc failed!")); #endif return (false); } currentGeofenceParams->numFences = 0; return (true); } // Power Save Mode // Enables/Disables Low Power Mode using UBX-CFG-RXM bool SFE_UBLOX_GNSS::powerSaveMode(bool power_save, uint16_t maxWait) { // Let's begin by checking the Protocol Version as UBX_CFG_RXM is not supported on the ZED (protocol >= 27) uint8_t protVer = getProtocolVersionHigh(maxWait); /* if (_printDebug == true) { _debugSerial->print(F("Protocol version is ")); _debugSerial->println(protVer); } */ if (protVer >= 27) { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("powerSaveMode (UBX-CFG-RXM) is not supported by this protocol version")); } return (false); } // Now let's change the power setting using UBX-CFG-RXM return setupPowerMode(power_save ? SFE_UBLOX_CFG_RXM_POWERSAVE : SFE_UBLOX_CFG_RXM_CONTINUOUS, maxWait); } // Get Power Save Mode // Returns the current Low Power Mode using UBX-CFG-RXM // Returns 255 if the sendCommand fails uint8_t SFE_UBLOX_GNSS::getPowerSaveMode(uint16_t maxWait) { // Let's begin by checking the Protocol Version as UBX_CFG_RXM is not supported on the ZED (protocol >= 27) uint8_t protVer = getProtocolVersionHigh(maxWait); /* if (_printDebug == true) { _debugSerial->print(F("Protocol version is ")); _debugSerial->println(protVer); } */ if (protVer >= 27) { if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging { _debugSerial->println(F("powerSaveMode (UBX-CFG-RXM) is not supported by this protocol version")); } return (255); } // Now let's read the power setting using UBX-CFG-RXM packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RXM; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current power management settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (255); return (payloadCfg[1]); // Return the low power mode } // Powers off the GPS device for a given duration to reduce power consumption. // NOTE: Querying the device before the duration is complete, for example by "getLatitude()" will wake it up! // Returns true if command has not been not acknowledged. // Returns false if command has not been acknowledged or maxWait = 0. bool SFE_UBLOX_GNSS::powerOff(uint32_t durationInMs, uint16_t maxWait) { // use durationInMs = 0 for infinite duration #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("Powering off for ")); _debugSerial->print(durationInMs); _debugSerial->println(" ms"); } #endif // Power off device using UBX-RXM-PMREQ packetCfg.cls = UBX_CLASS_RXM; // 0x02 packetCfg.id = UBX_RXM_PMREQ; // 0x41 packetCfg.len = 8; packetCfg.startingSpot = 0; // duration // big endian to little endian, switch byte order payloadCfg[0] = (durationInMs >> (8 * 0)) & 0xff; payloadCfg[1] = (durationInMs >> (8 * 1)) & 0xff; payloadCfg[2] = (durationInMs >> (8 * 2)) & 0xff; payloadCfg[3] = (durationInMs >> (8 * 3)) & 0xff; payloadCfg[4] = 0x02; // Flags : set the backup bit payloadCfg[5] = 0x00; // Flags payloadCfg[6] = 0x00; // Flags payloadCfg[7] = 0x00; // Flags if (maxWait != 0) { // check for "not acknowledged" command return (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_COMMAND_NACK); } else { sendCommand(&packetCfg, maxWait); return false; // can't tell if command not acknowledged if maxWait = 0 } } // Powers off the GPS device for a given duration to reduce power consumption. // While powered off it can be woken up by creating a falling or rising voltage edge on the specified pin. // NOTE: The GPS seems to be sensitve to signals on the pins while powered off. Works best when Microcontroller is in deepsleep. // NOTE: Querying the device before the duration is complete, for example by "getLatitude()" will wake it up! // Returns true if command has not been not acknowledged. // Returns false if command has not been acknowledged or maxWait = 0. bool SFE_UBLOX_GNSS::powerOffWithInterrupt(uint32_t durationInMs, uint32_t wakeupSources, bool forceWhileUsb, uint16_t maxWait) { // use durationInMs = 0 for infinite duration #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("Powering off for ")); _debugSerial->print(durationInMs); _debugSerial->println(" ms"); } #endif // Power off device using UBX-RXM-PMREQ packetCfg.cls = UBX_CLASS_RXM; // 0x02 packetCfg.id = UBX_RXM_PMREQ; // 0x41 packetCfg.len = 16; packetCfg.startingSpot = 0; payloadCfg[0] = 0x00; // message version // bytes 1-3 are reserved - and must be set to zero payloadCfg[1] = 0x00; payloadCfg[2] = 0x00; payloadCfg[3] = 0x00; // duration // big endian to little endian, switch byte order payloadCfg[4] = (durationInMs >> (8 * 0)) & 0xff; payloadCfg[5] = (durationInMs >> (8 * 1)) & 0xff; payloadCfg[6] = (durationInMs >> (8 * 2)) & 0xff; payloadCfg[7] = (durationInMs >> (8 * 3)) & 0xff; // flags // disables USB interface when powering off, defaults to true if (forceWhileUsb) { payloadCfg[8] = 0x06; // force | backup } else { payloadCfg[8] = 0x02; // backup only (leave the force bit clear - module will stay on if USB is connected) } payloadCfg[9] = 0x00; payloadCfg[10] = 0x00; payloadCfg[11] = 0x00; // wakeUpSources // wakeupPin mapping, defaults to VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 // Possible values are: // VAL_RXM_PMREQ_WAKEUPSOURCE_UARTRX // VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT0 // VAL_RXM_PMREQ_WAKEUPSOURCE_EXTINT1 // VAL_RXM_PMREQ_WAKEUPSOURCE_SPICS payloadCfg[12] = (wakeupSources >> (8 * 0)) & 0xff; payloadCfg[13] = (wakeupSources >> (8 * 1)) & 0xff; payloadCfg[14] = (wakeupSources >> (8 * 2)) & 0xff; payloadCfg[15] = (wakeupSources >> (8 * 3)) & 0xff; if (maxWait != 0) { // check for "not acknowledged" command return (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_COMMAND_NACK); } else { sendCommand(&packetCfg, maxWait); return false; // can't tell if command not acknowledged if maxWait = 0 } } bool SFE_UBLOX_GNSS::setPowerManagement(sfe_ublox_pms_mode_e mode, uint16_t period, uint16_t onTime, uint16_t maxWait) { // INVALID only valid in response if (mode == SFE_UBLOX_PMS_MODE_INVALID) return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PMS; packetCfg.len = 8; packetCfg.startingSpot = 0; packetCfg.payload[0] = 0x0; //message version packetCfg.payload[1] = mode; // only valid if mode==SFE_UBLOX_PMS_MODE_INTERVAL if (mode == SFE_UBLOX_PMS_MODE_INTERVAL) { packetCfg.payload[2] = period >> 8; packetCfg.payload[3] = period & 0xff; packetCfg.payload[4] = onTime >> 8; packetCfg.payload[5] = onTime & 0xff; } else { packetCfg.payload[2] = 0; packetCfg.payload[3] = 0; packetCfg.payload[4] = 0; packetCfg.payload[5] = 0; } packetCfg.payload[6] = 0x0; //reserved packetCfg.payload[7] = 0x0; //reserved return sendCommand(&packetCfg, maxWait); } bool SFE_UBLOX_GNSS::setupPowerMode(sfe_ublox_rxm_mode_e mode, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RXM; packetCfg.len = 2; packetCfg.startingSpot = 0; packetCfg.payload[0] = 0x0; //reserved packetCfg.payload[1] = mode; //low power mode return sendCommand(&packetCfg, maxWait); } // Position Accuracy // Change the Position Accuracy using UBX-CFG-NAV5 // Value provided in meters bool SFE_UBLOX_GNSS::setNAV5PositionAccuracy(uint16_t metres, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAV5; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current navigation model settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); payloadCfg[0] |= 0x10; // mask: set the posMask, leave other bits unchanged payloadCfg[18] = metres & 0xFF; payloadCfg[19] = metres >> 8; packetCfg.len = 36; packetCfg.startingSpot = 0; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Get the position accuracy using UBX-CFG-NAV5 // Returns meters. 0 if the sendCommand fails uint16_t SFE_UBLOX_GNSS::getNAV5PositionAccuracy(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAV5; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current navigation model settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return 0; uint16_t pAcc = ((uint16_t)payloadCfg[19]) << 8; pAcc |= payloadCfg[18]; return (pAcc); } // Dynamic Platform Model // Change the dynamic platform model using UBX-CFG-NAV5 // Possible values are: // PORTABLE,STATIONARY,PEDESTRIAN,AUTOMOTIVE,SEA, // AIRBORNE1g,AIRBORNE2g,AIRBORNE4g,WRIST,BIKE // WRIST is not supported in protocol versions less than 18 // BIKE is supported in protocol versions 19.2 bool SFE_UBLOX_GNSS::setDynamicModel(dynModel newDynamicModel, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAV5; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current navigation model settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); payloadCfg[0] |= 0x01; // mask: set only the dyn bit (0) payloadCfg[2] = newDynamicModel; // dynModel packetCfg.len = 36; packetCfg.startingSpot = 0; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Get the dynamic platform model using UBX-CFG-NAV5 // Returns DYN_MODEL_UNKNOWN (255) if the sendCommand fails uint8_t SFE_UBLOX_GNSS::getDynamicModel(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAV5; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current navigation model settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (DYN_MODEL_UNKNOWN); return (payloadCfg[2]); // Return the dynamic model } // Reset the odometer bool SFE_UBLOX_GNSS::resetOdometer(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_RESETODO; packetCfg.len = 0; packetCfg.startingSpot = 0; // This is a special case as we are only expecting an ACK but this is not a CFG message return (sendCommand(&packetCfg, maxWait, true) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Enable / disable the odometer bool SFE_UBLOX_GNSS::enableOdometer(bool enable, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ODO; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current odometer configuration. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); if (enable) payloadCfg[4] = payloadCfg[4] | UBX_CFG_ODO_USE_ODO; else payloadCfg[4] = payloadCfg[4] & ~UBX_CFG_ODO_USE_ODO; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Read the odometer configuration bool SFE_UBLOX_GNSS::getOdometerConfig(uint8_t *flags, uint8_t *odoCfg, uint8_t *cogMaxSpeed, uint8_t *cogMaxPosAcc, uint8_t *velLpGain, uint8_t *cogLpGain, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ODO; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current odometer configuration. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); *flags = payloadCfg[4]; *odoCfg = payloadCfg[5]; *cogMaxSpeed = payloadCfg[12]; *cogMaxPosAcc = payloadCfg[13]; *velLpGain = payloadCfg[16]; *cogLpGain = payloadCfg[17]; return true; } // Configure the odometer bool SFE_UBLOX_GNSS::setOdometerConfig(uint8_t flags, uint8_t odoCfg, uint8_t cogMaxSpeed, uint8_t cogMaxPosAcc, uint8_t velLpGain, uint8_t cogLpGain, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ODO; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current odometer configuration. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); payloadCfg[4] = flags; payloadCfg[5] = odoCfg; payloadCfg[12] = cogMaxSpeed; payloadCfg[13] = cogMaxPosAcc; payloadCfg[16] = velLpGain; payloadCfg[17] = cogLpGain; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Enable/Disable individual GNSS systems using UBX-CFG-GNSS bool SFE_UBLOX_GNSS::enableGNSS(bool enable, sfe_ublox_gnss_ids_e id, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_GNSS; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); uint8_t numConfigBlocks = payloadCfg[3]; // Extract the numConfigBlocks for (uint8_t block = 0; block < numConfigBlocks; block++) // Check each configuration block { if (payloadCfg[(block * 8) + 4] == (uint8_t)id) // Check the gnssId for this block. Do we have a match? { // We have a match so set/clear the enable bit in flags if (enable) payloadCfg[(block * 8) + 4 + 4] |= 0x01; // Set the enable bit in flags (Little Endian) else payloadCfg[(block * 8) + 4 + 4] &= 0xFE; // Clear the enable bit in flags (Little Endian) break; } } return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Check if an individual GNSS system is enabled bool SFE_UBLOX_GNSS::isGNSSenabled(sfe_ublox_gnss_ids_e id, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_GNSS; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); uint8_t numConfigBlocks = payloadCfg[3]; // Extract the numConfigBlocks for (uint8_t block = 0; block < numConfigBlocks; block++) // Check each configuration block { if (payloadCfg[(block * 8) + 4] == (uint8_t)id) // Check the gnssId for this block. Do we have a match? { // We have a match so check the enable bit in flags if ((payloadCfg[(block * 8) + 4 + 4] & 0x01) > 0) // Check the enable bit in flags (Little Endian) return true; } } return false; } // Reset ESF automatic IMU-mount alignment bool SFE_UBLOX_GNSS::resetIMUalignment(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_ESF; packetCfg.id = UBX_ESF_RESETALG; packetCfg.len = 0; packetCfg.startingSpot = 0; // This is a special case as we are only expecting an ACK but this is not a CFG message return (sendCommand(&packetCfg, maxWait, true) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // UBX-CFG-ESFALG is not documented. This was found using u-center. // Returns the state of the UBX-CFG-ESFALG 'Automatic IMU-mount Alignment' flag bool SFE_UBLOX_GNSS::getESFAutoAlignment(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ESFALG; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("getESFAutoAlignment failed")); } #endif return (false); // If command send fails then bail } return (payloadCfg[1] & 0b1); // Return Bit 0 } // Set the state of the UBX-CFG-ESFALG 'Automatic IMU-mount Alignment' flag bool SFE_UBLOX_GNSS::setESFAutoAlignment(bool enable, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ESFALG; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("getESFAutoAlignment failed")); } #endif return (false); // If command send fails then bail } // payloadCfg is now filled if (enable) payloadCfg[1] |= 0b1; else payloadCfg[1] &= ~(0b1); if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_SENT) // This time we are only expecting an ACK { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->println(F("setESFAutoAlignment failed")); } #endif return (false); } return (true); } // Get the time pulse parameters using UBX_CFG_TP5 bool SFE_UBLOX_GNSS::getTimePulseParameters(UBX_CFG_TP5_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TP5; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->tpIdx = extractByte(&packetCfg, 0); data->version = extractByte(&packetCfg, 1); data->antCableDelay = extractSignedInt(&packetCfg, 4); data->rfGroupDelay = extractSignedInt(&packetCfg, 6); data->freqPeriod = extractLong(&packetCfg, 8); data->freqPeriodLock = extractLong(&packetCfg, 12); data->pulseLenRatio = extractLong(&packetCfg, 16); data->pulseLenRatioLock = extractLong(&packetCfg, 20); data->userConfigDelay = extractSignedLong(&packetCfg, 24); data->flags.all = extractLong(&packetCfg, 28); return (true); } // Set the time pulse parameters using UBX_CFG_TP5 bool SFE_UBLOX_GNSS::setTimePulseParameters(UBX_CFG_TP5_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_TP5; packetCfg.len = UBX_CFG_TP5_LEN; packetCfg.startingSpot = 0; // Insert the data payloadCfg[0] = data->tpIdx; payloadCfg[1] = data->version; payloadCfg[4] = data->antCableDelay & 0xFF; // Little Endian payloadCfg[5] = data->antCableDelay >> 8; payloadCfg[6] = data->rfGroupDelay & 0xFF; // Little Endian payloadCfg[7] = data->rfGroupDelay >> 8; payloadCfg[8] = data->freqPeriod & 0xFF; // Little Endian payloadCfg[9] = (data->freqPeriod >> 8) & 0xFF; payloadCfg[10] = (data->freqPeriod >> 16) & 0xFF; payloadCfg[11] = (data->freqPeriod >> 24) & 0xFF; payloadCfg[12] = data->freqPeriodLock & 0xFF; // Little Endian payloadCfg[13] = (data->freqPeriodLock >> 8) & 0xFF; payloadCfg[14] = (data->freqPeriodLock >> 16) & 0xFF; payloadCfg[15] = (data->freqPeriodLock >> 24) & 0xFF; payloadCfg[16] = data->pulseLenRatio & 0xFF; // Little Endian payloadCfg[17] = (data->pulseLenRatio >> 8) & 0xFF; payloadCfg[18] = (data->pulseLenRatio >> 16) & 0xFF; payloadCfg[19] = (data->pulseLenRatio >> 24) & 0xFF; payloadCfg[20] = data->pulseLenRatioLock & 0xFF; // Little Endian payloadCfg[21] = (data->pulseLenRatioLock >> 8) & 0xFF; payloadCfg[22] = (data->pulseLenRatioLock >> 16) & 0xFF; payloadCfg[23] = (data->pulseLenRatioLock >> 24) & 0xFF; payloadCfg[24] = data->userConfigDelay & 0xFF; // Little Endian payloadCfg[25] = (data->userConfigDelay >> 8) & 0xFF; payloadCfg[26] = (data->userConfigDelay >> 16) & 0xFF; payloadCfg[27] = (data->userConfigDelay >> 24) & 0xFF; payloadCfg[28] = data->flags.all & 0xFF; // Little Endian payloadCfg[29] = (data->flags.all >> 8) & 0xFF; payloadCfg[30] = (data->flags.all >> 16) & 0xFF; payloadCfg[31] = (data->flags.all >> 24) & 0xFF; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Get the jamming/interference monitor configuration using UBX_CFG_ITFM bool SFE_UBLOX_GNSS::getJammingConfiguration(UBX_CFG_ITFM_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ITFM; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->config.all = extractLong(&packetCfg, 0); data->config2.all = extractLong(&packetCfg, 4); return (true); } // Set the jamming/interference monitor configuration using UBX_CFG_ITFM bool SFE_UBLOX_GNSS::setJammingConfiguration(UBX_CFG_ITFM_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_ITFM; packetCfg.len = UBX_CFG_ITFM_LEN; packetCfg.startingSpot = 0; payloadCfg[0] = data->config.all & 0xFF; // Little Endian payloadCfg[1] = (data->config.all >> 8) & 0xFF; payloadCfg[2] = (data->config.all >> 16) & 0xFF; payloadCfg[3] = (data->config.all >> 24) & 0xFF; payloadCfg[4] = data->config2.all & 0xFF; // Little Endian payloadCfg[5] = (data->config2.all >> 8) & 0xFF; payloadCfg[6] = (data->config2.all >> 16) & 0xFF; payloadCfg[7] = (data->config2.all >> 24) & 0xFF; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Get the RF information using UBX_MON_RF bool SFE_UBLOX_GNSS::getRFinformation(UBX_MON_RF_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_MON; packetCfg.id = UBX_MON_RF; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->header.version = extractByte(&packetCfg, 0); data->header.nBlocks = extractByte(&packetCfg, 1); // Extract the RF information blocks for (uint8_t block = 0; (block < data->header.nBlocks) && (block < UBX_MON_RF_MAX_BLOCKS); block++) { data->blocks[block].blockId = extractByte(&packetCfg, 4 + (block * 24)); data->blocks[block].flags.all = extractByte(&packetCfg, 5 + (block * 24)); data->blocks[block].antStatus = extractByte(&packetCfg, 6 + (block * 24)); data->blocks[block].antPower = extractByte(&packetCfg, 7 + (block * 24)); data->blocks[block].postStatus = extractLong(&packetCfg, 8 + (block * 24)); data->blocks[block].noisePerMS = extractInt(&packetCfg, 16 + (block * 24)); data->blocks[block].agcCnt = extractInt(&packetCfg, 18 + (block * 24)); data->blocks[block].jamInd = extractByte(&packetCfg, 20 + (block * 24)); data->blocks[block].ofsI = extractSignedChar(&packetCfg, 21 + (block * 24)); data->blocks[block].magI = extractByte(&packetCfg, 22 + (block * 24)); data->blocks[block].ofsQ = extractSignedChar(&packetCfg, 23 + (block * 24)); data->blocks[block].magQ = extractByte(&packetCfg, 24 + (block * 24)); } return (true); } // Get the hardware status (including jamming) using UBX_MON_HW bool SFE_UBLOX_GNSS::getHWstatus(UBX_MON_HW_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_MON; packetCfg.id = UBX_MON_HW; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->pinSel = extractLong(&packetCfg, 0); data->pinBank = extractLong(&packetCfg, 4); data->pinDir = extractLong(&packetCfg, 8); data->pinVal = extractLong(&packetCfg, 12); data->noisePerMS = extractInt(&packetCfg, 16); data->agcCnt = extractInt(&packetCfg, 18); data->aStatus = extractByte(&packetCfg, 20); data->aPower = extractByte(&packetCfg, 21); data->flags.all = extractByte(&packetCfg, 22); data->usedMask = extractLong(&packetCfg, 24); for (uint8_t pin = 0; pin < 17; pin++) { data->VP[pin] = extractByte(&packetCfg, 28 + pin); } data->jamInd = extractByte(&packetCfg, 45); data->pinIrq = extractLong(&packetCfg, 48); data->pullH = extractLong(&packetCfg, 52); data->pullL = extractLong(&packetCfg, 56); return (true); } // Get the extended hardware status using UBX_MON_HW2 bool SFE_UBLOX_GNSS::getHW2status(UBX_MON_HW2_data_t *data, uint16_t maxWait) { if (data == NULL) // Check if the user forgot to include the data pointer return (false); // Bail packetCfg.cls = UBX_CLASS_MON; packetCfg.id = UBX_MON_HW2; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Extract the data data->ofsI = extractSignedChar(&packetCfg, 0); data->magI = extractByte(&packetCfg, 1); data->ofsQ = extractSignedChar(&packetCfg, 2); data->magQ = extractByte(&packetCfg, 3); data->cfgSource = extractByte(&packetCfg, 4); data->lowLevCfg = extractLong(&packetCfg, 8); // Low-level configuration (obsolete for protocol versions greater than 15.00) data->postStatus = extractLong(&packetCfg, 20); return (true); } // UBX-CFG-NAVX5 - get/set the ackAiding byte. If ackAiding is 1, UBX-MGA-ACK messages will be sent by the module to acknowledge the MGA data uint8_t SFE_UBLOX_GNSS::getAckAiding(uint16_t maxWait) // Get the ackAiding byte - returns 255 if the sendCommand fails { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAVX5; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (255); // Extract the ackAiding byte // There are three versions of UBX-CFG-NAVX5 but ackAiding is always in byte 17 return (extractByte(&packetCfg, 17)); } bool SFE_UBLOX_GNSS::setAckAiding(uint8_t ackAiding, uint16_t maxWait) // Set the ackAiding byte { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAVX5; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Set the ackAiding byte // There are three versions of UBX-CFG-NAVX5 but ackAiding is always in byte 17 payloadCfg[17] = ackAiding; // There are three versions of UBX-CFG-NAVX5 but the ackAid flag is always in bit 10 of mask1 payloadCfg[2] = 0x00; // Clear the LS byte of mask1 payloadCfg[3] = 0x04; // Set _only_ the ackAid flag = bit 10 of mask1 = bit 2 of the MS byte payloadCfg[4] = 0x00; // Clear the LS byte of mask2, just in case payloadCfg[5] = 0x00; // Clear the LS byte of mask2, just in case return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // AssistNow Autonomous support // UBX-CFG-NAVX5 - get the AssistNow Autonomous configuration (aopCfg) - returns 255 if the sendCommand fails uint8_t SFE_UBLOX_GNSS::getAopCfg(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAVX5; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (255); // Extract the aopCfg byte // There are three versions of UBX-CFG-NAVX5 but aopCfg is always in byte 27 return (extractByte(&packetCfg, 27)); } // Set the aopCfg byte and the aopOrdMaxErr word bool SFE_UBLOX_GNSS::setAopCfg(uint8_t aopCfg, uint16_t aopOrbMaxErr, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NAVX5; packetCfg.len = 0; packetCfg.startingSpot = 0; if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // Set the aopCfg byte // There are three versions of UBX-CFG-NAVX5 but aopCfg is always in byte 27 and aopOrbMaxErr is always in bytes 30 & 31 payloadCfg[27] = aopCfg; payloadCfg[30] = (uint8_t)(aopOrbMaxErr & 0xFF); // aopOrbMaxErr LSB payloadCfg[31] = (uint8_t)(aopOrbMaxErr >> 8); // aopOrbMaxErr MSB // There are three versions of UBX-CFG-NAVX5 but the aop flag is always in bit 14 of mask1 payloadCfg[2] = 0x00; // Clear the LS byte of mask1 payloadCfg[3] = 0x40; // Set _only_ the aop flag = bit 14 of mask1 = bit 6 of the MS byte payloadCfg[4] = 0x00; // Clear the LS byte of mask2, just in case payloadCfg[5] = 0x00; // Clear the LS byte of mask2, just in case return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // SPARTN dynamic keys //"When the receiver boots, the host should send 'current' and 'next' keys in one message." - Use setDynamicSPARTNKeys for this. //"Every time the 'current' key is expired, 'next' takes its place." //"Therefore the host should then retrieve the new 'next' key and send only that." - Use setDynamicSPARTNKey for this. // The key can be provided in binary (uint8_t) format or in ASCII Hex (char) format, but in both cases keyLengthBytes _must_ represent the binary key length in bytes. bool SFE_UBLOX_GNSS::setDynamicSPARTNKey(uint8_t keyLengthBytes, uint16_t validFromWno, uint32_t validFromTow, const char *key) { uint8_t *binaryKey = new uint8_t[keyLengthBytes]; // Allocate memory to store the binaryKey if (binaryKey == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) _debugSerial->println(F("setDynamicSPARTNKey: binaryKey RAM allocation failed!")); #endif return (false); } bool ok = true; // Convert the ASCII Hex const char to binary uint8_t for (uint16_t i = 0; i < ((uint16_t)keyLengthBytes * 2); i += 2) { if ((key[i] >= '0') && (key[i] <= '9')) { binaryKey[i >> 1] = (key[i] - '0') << 4; } else if ((key[i] >= 'a') && (key[i] <= 'f')) { binaryKey[i >> 1] = (key[i] + 10 - 'a') << 4; } else if ((key[i] >= 'A') && (key[i] <= 'F')) { binaryKey[i >> 1] = (key[i] + 10 - 'A') << 4; } else { ok = false; } if ((key[i + 1] >= '0') && (key[i + 1] <= '9')) { binaryKey[i >> 1] |= key[i + 1] - '0'; } else if ((key[i + 1] >= 'a') && (key[i + 1] <= 'f')) { binaryKey[i >> 1] |= key[i + 1] + 10 - 'a'; } else if ((key[i + 1] >= 'A') && (key[i + 1] <= 'F')) { binaryKey[i >> 1] |= key[i + 1] + 10 - 'A'; } else { ok = false; } } if (ok) ok = setDynamicSPARTNKey(keyLengthBytes, validFromWno, validFromTow, (const uint8_t *)binaryKey); delete[] binaryKey; // Free the memory allocated for binaryKey return (ok); } bool SFE_UBLOX_GNSS::setDynamicSPARTNKey(uint8_t keyLengthBytes, uint16_t validFromWno, uint32_t validFromTow, const uint8_t *key) { // Check if there is room for the key in packetCfg. Resize the buffer if not. size_t payloadLength = (size_t)keyLengthBytes + 12; if (packetCfgPayloadSize < payloadLength) { if (!setPacketCfgPayloadSize(payloadLength)) // Check if the resize was successful { return (false); } } // Copy the key etc. into packetCfg packetCfg.cls = UBX_CLASS_RXM; packetCfg.id = UBX_RXM_SPARTNKEY; packetCfg.len = payloadLength; packetCfg.startingSpot = 0; payloadCfg[0] = 0x01; // version payloadCfg[1] = 0x01; // numKeys payloadCfg[2] = 0x00; // reserved0 payloadCfg[3] = 0x00; // reserved0 payloadCfg[4] = 0x00; // reserved1 payloadCfg[5] = keyLengthBytes; payloadCfg[6] = validFromWno & 0xFF; // validFromWno little-endian payloadCfg[7] = validFromWno >> 8; payloadCfg[8] = validFromTow & 0xFF; // validFromTow little-endian payloadCfg[9] = (validFromTow >> 8) & 0xFF; payloadCfg[10] = (validFromTow >> 16) & 0xFF; payloadCfg[11] = (validFromTow >> 24) & 0xFF; memcpy(&payloadCfg[12], key, keyLengthBytes); return (sendCommand(&packetCfg, 0) == SFE_UBLOX_STATUS_SUCCESS); // UBX-RXM-SPARTNKEY is silent. It does not ACK (or NACK) } bool SFE_UBLOX_GNSS::setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validFromWno1, uint32_t validFromTow1, const char *key1, uint8_t keyLengthBytes2, uint16_t validFromWno2, uint32_t validFromTow2, const char *key2) { uint8_t *binaryKey1 = new uint8_t[keyLengthBytes1]; // Allocate memory to store binaryKey1 if (binaryKey1 == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) _debugSerial->println(F("setDynamicSPARTNKeys: binaryKey1 RAM allocation failed!")); #endif return (false); } uint8_t *binaryKey2 = new uint8_t[keyLengthBytes2]; // Allocate memory to store binaryKey2 if (binaryKey2 == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) _debugSerial->println(F("setDynamicSPARTNKeys: binaryKey2 RAM allocation failed!")); #endif delete[] binaryKey1; return (false); } bool ok = true; // Convert the ASCII Hex const char to binary uint8_t for (uint16_t i = 0; i < ((uint16_t)keyLengthBytes1 * 2); i += 2) { if ((key1[i] >= '0') && (key1[i] <= '9')) { binaryKey1[i >> 1] = (key1[i] - '0') << 4; } else if ((key1[i] >= 'a') && (key1[i] <= 'f')) { binaryKey1[i >> 1] = (key1[i] + 10 - 'a') << 4; } else if ((key1[i] >= 'A') && (key1[i] <= 'F')) { binaryKey1[i >> 1] = (key1[i] + 10 - 'A') << 4; } else { ok = false; } if ((key1[i + 1] >= '0') && (key1[i + 1] <= '9')) { binaryKey1[i >> 1] |= key1[i + 1] - '0'; } else if ((key1[i + 1] >= 'a') && (key1[i + 1] <= 'f')) { binaryKey1[i >> 1] |= key1[i + 1] + 10 - 'a'; } else if ((key1[i + 1] >= 'A') && (key1[i + 1] <= 'F')) { binaryKey1[i >> 1] |= key1[i + 1] + 10 - 'A'; } else { ok = false; } } // Convert the ASCII Hex const char to binary uint8_t for (uint16_t i = 0; i < ((uint16_t)keyLengthBytes2 * 2); i += 2) { if ((key2[i] >= '0') && (key2[i] <= '9')) { binaryKey2[i >> 1] = (key2[i] - '0') << 4; } else if ((key2[i] >= 'a') && (key2[i] <= 'f')) { binaryKey2[i >> 1] = (key2[i] + 10 - 'a') << 4; } else if ((key2[i] >= 'A') && (key2[i] <= 'F')) { binaryKey2[i >> 1] = (key2[i] + 10 - 'A') << 4; } else { ok = false; } if ((key2[i + 1] >= '0') && (key2[i + 1] <= '9')) { binaryKey2[i >> 1] |= key2[i + 1] - '0'; } else if ((key2[i + 1] >= 'a') && (key2[i + 1] <= 'f')) { binaryKey2[i >> 1] |= key2[i + 1] + 10 - 'a'; } else if ((key2[i + 1] >= 'A') && (key2[i + 1] <= 'F')) { binaryKey2[i >> 1] |= key2[i + 1] + 10 - 'A'; } else { ok = false; } } if (ok) ok = setDynamicSPARTNKeys(keyLengthBytes1, validFromWno1, validFromTow1, (const uint8_t *)binaryKey1, keyLengthBytes2, validFromWno2, validFromTow2, (const uint8_t *)binaryKey2); delete[] binaryKey1; // Free the memory allocated for binaryKey1 delete[] binaryKey2; // Free the memory allocated for binaryKey2 return (ok); } bool SFE_UBLOX_GNSS::setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validFromWno1, uint32_t validFromTow1, const uint8_t *key1, uint8_t keyLengthBytes2, uint16_t validFromWno2, uint32_t validFromTow2, const uint8_t *key2) { // Check if there is room for the key in packetCfg. Resize the buffer if not. size_t payloadLength = (size_t)keyLengthBytes1 + (size_t)keyLengthBytes2 + 20; if (packetCfgPayloadSize < payloadLength) { if (!setPacketCfgPayloadSize(payloadLength)) // Check if the resize was successful { return (false); } } // Copy the key etc. into packetCfg packetCfg.cls = UBX_CLASS_RXM; packetCfg.id = UBX_RXM_SPARTNKEY; packetCfg.len = payloadLength; packetCfg.startingSpot = 0; payloadCfg[0] = 0x01; // version payloadCfg[1] = 0x02; // numKeys payloadCfg[2] = 0x00; // reserved0 payloadCfg[3] = 0x00; // reserved0 payloadCfg[4] = 0x00; // reserved1 payloadCfg[5] = keyLengthBytes1; payloadCfg[6] = validFromWno1 & 0xFF; // validFromWno little-endian payloadCfg[7] = validFromWno1 >> 8; payloadCfg[8] = validFromTow1 & 0xFF; // validFromTow little-endian payloadCfg[9] = (validFromTow1 >> 8) & 0xFF; payloadCfg[10] = (validFromTow1 >> 16) & 0xFF; payloadCfg[11] = (validFromTow1 >> 24) & 0xFF; payloadCfg[12] = 0x00; // reserved1 payloadCfg[13] = keyLengthBytes2; payloadCfg[14] = validFromWno2 & 0xFF; // validFromWno little-endian payloadCfg[15] = validFromWno2 >> 8; payloadCfg[16] = validFromTow2 & 0xFF; // validFromTow little-endian payloadCfg[17] = (validFromTow2 >> 8) & 0xFF; payloadCfg[18] = (validFromTow2 >> 16) & 0xFF; payloadCfg[19] = (validFromTow2 >> 24) & 0xFF; memcpy(&payloadCfg[20], key1, keyLengthBytes1); memcpy(&payloadCfg[20 + keyLengthBytes1], key2, keyLengthBytes2); return (sendCommand(&packetCfg, 0) == SFE_UBLOX_STATUS_SUCCESS); // UBX-RXM-SPARTNKEY is silent. It does not ACK (or NACK) } // CONFIGURATION INTERFACE (protocol v27 and above) // Form 32-bit key from group/id/size uint32_t SFE_UBLOX_GNSS::createKey(uint16_t group, uint16_t id, uint8_t size) { uint32_t key = 0; key |= (uint32_t)id; key |= (uint32_t)group << 16; key |= (uint32_t)size << 28; return (key); } // Given a key, load the payload with data that can then be extracted to 8, 16, or 32 bits // This function takes a full 32-bit key // Default layer is RAM // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P sfe_ublox_status_e SFE_UBLOX_GNSS::getVal(uint32_t key, uint8_t layer, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALGET; packetCfg.len = 4 + 4 * 1; // While multiple keys are allowed, we will send only one key at a time packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); // VALGET uses different memory layer definitions to VALSET // because it can only return the value for one layer. // So we need to fiddle the layer here. // And just to complicate things further, the ZED-F9P only responds // correctly to layer 0 (RAM) and layer 7 (Default)! uint8_t getLayer = 7; // 7 is the "Default Layer" if ((layer & VAL_LAYER_RAM) == VAL_LAYER_RAM) // Did the user request the RAM layer? { getLayer = 0; // Layer 0 is RAM } payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = getLayer; // Layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("key: 0x")); _debugSerial->print(key, HEX); _debugSerial->println(); } #endif // Send VALGET command with this key sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait); #ifndef SFE_UBLOX_REDUCED_PROG_MEM if (_printDebug == true) { _debugSerial->print(F("getVal: sendCommand returned: ")); _debugSerial->println(statusString(retVal)); } #endif // Verify the response is the correct length as compared to what the user called (did the module respond with 8-bits but the user called getVal32?) // Response is 8 bytes plus cfg data // if(packet->len > 8+1) // The response is now sitting in payload, ready for extraction return (retVal); } // Given a key, return its value // This function takes a full 32-bit key // Default layer is RAM // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::getVal8(uint32_t key, uint8_t layer, uint16_t maxWait) { if (getVal(key, layer, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (0); return (extractByte(&packetCfg, 8)); } uint16_t SFE_UBLOX_GNSS::getVal16(uint32_t key, uint8_t layer, uint16_t maxWait) { if (getVal(key, layer, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (0); return (extractInt(&packetCfg, 8)); } uint32_t SFE_UBLOX_GNSS::getVal32(uint32_t key, uint8_t layer, uint16_t maxWait) { if (getVal(key, layer, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (0); return (extractLong(&packetCfg, 8)); } uint64_t SFE_UBLOX_GNSS::getVal64(uint32_t key, uint8_t layer, uint16_t maxWait) { if (getVal(key, layer, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (0); return (extractLongLong(&packetCfg, 8)); } // Given a group, ID and size, return the value of this config spot // The 32-bit key is put together from group/ID/size. See other getVal to send key directly. // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait) { uint32_t key = createKey(group, id, size); return getVal8(key, layer, maxWait); } uint16_t SFE_UBLOX_GNSS::getVal16(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait) { uint32_t key = createKey(group, id, size); return getVal16(key, layer, maxWait); } uint32_t SFE_UBLOX_GNSS::getVal32(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait) { uint32_t key = createKey(group, id, size); return getVal32(key, layer, maxWait); } uint64_t SFE_UBLOX_GNSS::getVal64(uint16_t group, uint16_t id, uint8_t size, uint8_t layer, uint16_t maxWait) { uint32_t key = createKey(group, id, size); return getVal64(key, layer, maxWait); } // Given a key, set a 16-bit value // This function takes a full 32-bit key // Default layer is all: RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::setVal(uint32_t key, uint16_t value, uint8_t layer, uint16_t maxWait) { return setVal16(key, value, layer, maxWait); } // Given a key, set a 16-bit value // This function takes a full 32-bit key // Default layer is all: RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::setVal16(uint32_t key, uint16_t value, uint8_t layer, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 2; // 4 byte header, 4 byte key ID, 2 bytes of value packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Given a key, set an 8-bit value // This function takes a full 32-bit key // Default layer is all: RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::setVal8(uint32_t key, uint8_t value, uint8_t layer, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 1; // 4 byte header, 4 byte key ID, 1 byte value packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value; // Value // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Given a key, set a 32-bit value // This function takes a full 32-bit key // Default layer is all: RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::setVal32(uint32_t key, uint32_t value, uint8_t layer, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 4; // 4 byte header, 4 byte key ID, 4 bytes of value packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; payloadCfg[10] = value >> 8 * 2; payloadCfg[11] = value >> 8 * 3; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Given a key, set a 64-bit value // This function takes a full 32-bit key // Default layer is all: RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::setVal64(uint32_t key, uint64_t value, uint8_t layer, uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 8; // 4 byte header, 4 byte key ID, 8 bytes of value packetCfg.startingSpot = 0; // Clear packet payload memset(payloadCfg, 0, packetCfg.len); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; payloadCfg[10] = value >> 8 * 2; payloadCfg[11] = value >> 8 * 3; payloadCfg[12] = value >> 8 * 4; payloadCfg[13] = value >> 8 * 5; payloadCfg[14] = value >> 8 * 6; payloadCfg[15] = value >> 8 * 7; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Start defining a new UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 64-bit value // Default layer is RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::newCfgValset64(uint32_t key, uint64_t value, uint8_t layer) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 8; // 4 byte header, 4 byte key ID, 8 bytes of value packetCfg.startingSpot = 0; _numCfgKeyIDs = 1; // Clear all of packet payload memset(payloadCfg, 0, packetCfgPayloadSize); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; payloadCfg[10] = value >> 8 * 2; payloadCfg[11] = value >> 8 * 3; payloadCfg[12] = value >> 8 * 4; payloadCfg[13] = value >> 8 * 5; payloadCfg[14] = value >> 8 * 6; payloadCfg[15] = value >> 8 * 7; // All done return (true); } // Start defining a new UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 32-bit value // Default layer is RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::newCfgValset32(uint32_t key, uint32_t value, uint8_t layer) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 4; // 4 byte header, 4 byte key ID, 4 bytes of value packetCfg.startingSpot = 0; _numCfgKeyIDs = 1; // Clear all of packet payload memset(payloadCfg, 0, packetCfgPayloadSize); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; payloadCfg[10] = value >> 8 * 2; payloadCfg[11] = value >> 8 * 3; // All done return (true); } // Start defining a new UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 16-bit value // Default layer is RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::newCfgValset16(uint32_t key, uint16_t value, uint8_t layer) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 2; // 4 byte header, 4 byte key ID, 2 bytes of value packetCfg.startingSpot = 0; _numCfgKeyIDs = 1; // Clear all of packet payload memset(payloadCfg, 0, packetCfgPayloadSize); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value >> 8 * 0; // Value LSB payloadCfg[9] = value >> 8 * 1; // All done return (true); } // Start defining a new UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 8-bit value // Default layer is RAM+BBR+Flash // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::newCfgValset8(uint32_t key, uint8_t value, uint8_t layer) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4 + 4 + 1; // 4 byte header, 4 byte key ID, 1 byte value packetCfg.startingSpot = 0; _numCfgKeyIDs = 1; // Clear all of packet payload memset(payloadCfg, 0, packetCfgPayloadSize); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // Load key into outgoing payload payloadCfg[4] = key >> 8 * 0; // Key LSB payloadCfg[5] = key >> 8 * 1; payloadCfg[6] = key >> 8 * 2; payloadCfg[7] = key >> 8 * 3; // Load user's value payloadCfg[8] = value; // Value // All done return (true); } // Start defining a new (empty) UBX-CFG-VALSET ubxPacket // Configuration of modern u-blox modules is now done via getVal/setVal/delVal, ie protocol v27 and above found on ZED-F9P uint8_t SFE_UBLOX_GNSS::newCfgValset(uint8_t layer) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_VALSET; packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; // Clear all of packet payload memset(payloadCfg, 0, packetCfgPayloadSize); payloadCfg[0] = 0; // Message Version - set to 0 payloadCfg[1] = layer; // By default we ask for the BBR layer // All done return (true); } // Add another keyID and value to an existing UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 64-bit value uint8_t SFE_UBLOX_GNSS::addCfgValset64(uint32_t key, uint64_t value) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset64: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 12)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset64: packetCfgPayloadSize reached!")); #endif return false; } if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset64: key limit reached!")); #endif return false; } // Load key into outgoing payload payloadCfg[packetCfg.len + 0] = key >> 8 * 0; // Key LSB payloadCfg[packetCfg.len + 1] = key >> 8 * 1; payloadCfg[packetCfg.len + 2] = key >> 8 * 2; payloadCfg[packetCfg.len + 3] = key >> 8 * 3; // Load user's value payloadCfg[packetCfg.len + 4] = value >> 8 * 0; // Value LSB payloadCfg[packetCfg.len + 5] = value >> 8 * 1; payloadCfg[packetCfg.len + 6] = value >> 8 * 2; payloadCfg[packetCfg.len + 7] = value >> 8 * 3; payloadCfg[packetCfg.len + 8] = value >> 8 * 4; payloadCfg[packetCfg.len + 9] = value >> 8 * 5; payloadCfg[packetCfg.len + 10] = value >> 8 * 6; payloadCfg[packetCfg.len + 11] = value >> 8 * 7; // Update packet length: 4 byte key ID, 8 bytes of value packetCfg.len = packetCfg.len + 4 + 8; _numCfgKeyIDs++; // All done return (true); } // Add another keyID and value to an existing UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 32-bit value uint8_t SFE_UBLOX_GNSS::addCfgValset32(uint32_t key, uint32_t value) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset32: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 8)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset32: packetCfgPayloadSize reached!")); #endif return false; } if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset32: key limit reached!")); #endif return false; } // Load key into outgoing payload payloadCfg[packetCfg.len + 0] = key >> 8 * 0; // Key LSB payloadCfg[packetCfg.len + 1] = key >> 8 * 1; payloadCfg[packetCfg.len + 2] = key >> 8 * 2; payloadCfg[packetCfg.len + 3] = key >> 8 * 3; // Load user's value payloadCfg[packetCfg.len + 4] = value >> 8 * 0; // Value LSB payloadCfg[packetCfg.len + 5] = value >> 8 * 1; payloadCfg[packetCfg.len + 6] = value >> 8 * 2; payloadCfg[packetCfg.len + 7] = value >> 8 * 3; // Update packet length: 4 byte key ID, 4 bytes of value packetCfg.len = packetCfg.len + 4 + 4; _numCfgKeyIDs++; // All done return (true); } // Add another keyID and value to an existing UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 16-bit value uint8_t SFE_UBLOX_GNSS::addCfgValset16(uint32_t key, uint16_t value) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset16: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 6)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset16: packetCfgPayloadSize reached!")); #endif return false; } if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset16: key limit reached!")); #endif return false; } // Load key into outgoing payload payloadCfg[packetCfg.len + 0] = key >> 8 * 0; // Key LSB payloadCfg[packetCfg.len + 1] = key >> 8 * 1; payloadCfg[packetCfg.len + 2] = key >> 8 * 2; payloadCfg[packetCfg.len + 3] = key >> 8 * 3; // Load user's value payloadCfg[packetCfg.len + 4] = value >> 8 * 0; // Value LSB payloadCfg[packetCfg.len + 5] = value >> 8 * 1; // Update packet length: 4 byte key ID, 2 bytes of value packetCfg.len = packetCfg.len + 4 + 2; _numCfgKeyIDs++; // All done return (true); } // Add another keyID and value to an existing UBX-CFG-VALSET ubxPacket // This function takes a full 32-bit key and 8-bit value uint8_t SFE_UBLOX_GNSS::addCfgValset8(uint32_t key, uint8_t value) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset8: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 5)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset8: packetCfgPayloadSize reached!")); #endif return false; } if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("addCfgValset8: key limit reached!")); #endif return false; } // Load key into outgoing payload payloadCfg[packetCfg.len + 0] = key >> 8 * 0; // Key LSB payloadCfg[packetCfg.len + 1] = key >> 8 * 1; payloadCfg[packetCfg.len + 2] = key >> 8 * 2; payloadCfg[packetCfg.len + 3] = key >> 8 * 3; // Load user's value payloadCfg[packetCfg.len + 4] = value; // Value // Update packet length: 4 byte key ID, 1 byte value packetCfg.len = packetCfg.len + 4 + 1; _numCfgKeyIDs++; // All done return (true); } // Add a final keyID and value to an existing UBX-CFG-VALSET ubxPacket and send it // This function takes a full 32-bit key and 64-bit value uint8_t SFE_UBLOX_GNSS::sendCfgValset64(uint32_t key, uint64_t value, uint16_t maxWait) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset64: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 12)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset64: packetCfgPayloadSize reached!")); #endif } else if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset64: key limit reached!")); #endif } else // Load keyID and value into outgoing payload addCfgValset64(key, value); _numCfgKeyIDs = 0; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Add a final keyID and value to an existing UBX-CFG-VALSET ubxPacket and send it // This function takes a full 32-bit key and 32-bit value uint8_t SFE_UBLOX_GNSS::sendCfgValset32(uint32_t key, uint32_t value, uint16_t maxWait) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset32: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 8)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset32: packetCfgPayloadSize reached!")); #endif } else if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset32: key limit reached!")); #endif } else // Load keyID and value into outgoing payload addCfgValset32(key, value); _numCfgKeyIDs = 0; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Add a final keyID and value to an existing UBX-CFG-VALSET ubxPacket and send it // This function takes a full 32-bit key and 16-bit value uint8_t SFE_UBLOX_GNSS::sendCfgValset16(uint32_t key, uint16_t value, uint16_t maxWait) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset16: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 6)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset16: packetCfgPayloadSize reached!")); #endif } else if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset16: key limit reached!")); #endif } else // Load keyID and value into outgoing payload addCfgValset16(key, value); _numCfgKeyIDs = 0; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Add a final keyID and value to an existing UBX-CFG-VALSET ubxPacket and send it // This function takes a full 32-bit key and 8-bit value uint8_t SFE_UBLOX_GNSS::sendCfgValset8(uint32_t key, uint8_t value, uint16_t maxWait) { if ((_autoSendAtSpaceRemaining > 0) && (packetCfg.len >= (packetCfgPayloadSize - _autoSendAtSpaceRemaining))) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset8: autosend")); #endif sendCommand(&packetCfg); packetCfg.len = 4; // 4 byte header packetCfg.startingSpot = 0; _numCfgKeyIDs = 0; memset(&payloadCfg[4], 0, packetCfgPayloadSize - 4); } if (packetCfg.len >= (packetCfgPayloadSize - 5)) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset8: packetCfgPayloadSize reached!")); #endif } else if (_numCfgKeyIDs == CFG_VALSET_MAX_KEYS) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("sendCfgValset8: key limit reached!")); #endif } else // Load keyID and value into outgoing payload addCfgValset8(key, value); _numCfgKeyIDs = 0; // Send VALSET command with this key and value return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Send the UBX-CFG-VALSET ubxPacket uint8_t SFE_UBLOX_GNSS::sendCfgValset(uint16_t maxWait) { if (_numCfgKeyIDs == 0) return true; // Nothing to send... // Send VALSET command with this key and value bool success = sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT; // We are only expecting an ACK if (success) _numCfgKeyIDs = 0; return success; } // Return the number of keys in the CfgValset uint8_t SFE_UBLOX_GNSS::getCfgValsetLen() { return _numCfgKeyIDs; } // Return the number of free bytes remaining in packetCfgPayload size_t SFE_UBLOX_GNSS::getCfgValsetSpaceRemaining() { return getPacketCfgSpaceRemaining(); } //=-=-=-=-=-=-=-= "Automatic" Messages =-=-=-=-=-=-=-==-=-=-=-=-=-=-= //=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-==-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- // ***** NAV POSECEF automatic support bool SFE_UBLOX_GNSS::getNAVPOSECEF(uint16_t maxWait) { if (packetUBXNAVPOSECEF == NULL) initPacketUBXNAVPOSECEF(); // Check that RAM has been allocated for the POSECEF data if (packetUBXNAVPOSECEF == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPOSECEF->automaticFlags.flags.bits.automatic && packetUBXNAVPOSECEF->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_POSECEF); return packetUBXNAVPOSECEF->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVPOSECEF->automaticFlags.flags.bits.automatic && !packetUBXNAVPOSECEF->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_POSECEF; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVPOSECEF(bool enable, uint16_t maxWait) { return setAutoNAVPOSECEFrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVPOSECEF(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVPOSECEFrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVPOSECEFrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVPOSECEF == NULL) initPacketUBXNAVPOSECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVPOSECEF == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_POSECEF; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVPOSECEF->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVPOSECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVPOSECEF->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVPOSECEFcallback(void (*callbackPointer)(UBX_NAV_POSECEF_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVPOSECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVPOSECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPOSECEF->callbackData = new UBX_NAV_POSECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPOSECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVPOSECEFcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVPOSECEF->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVPOSECEFcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_POSECEF_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVPOSECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVPOSECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPOSECEF->callbackData = new UBX_NAV_POSECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPOSECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVPOSECEFcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVPOSECEF->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and POSECEF is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVPOSECEF(bool enabled, bool implicitUpdate) { if (packetUBXNAVPOSECEF == NULL) initPacketUBXNAVPOSECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVPOSECEF == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVPOSECEF->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVPOSECEF->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVPOSECEF->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVPOSECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVPOSECEF and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVPOSECEF() { packetUBXNAVPOSECEF = new UBX_NAV_POSECEF_t; // Allocate RAM for the main struct if (packetUBXNAVPOSECEF == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVPOSECEF: RAM alloc failed!")); #endif return (false); } packetUBXNAVPOSECEF->automaticFlags.flags.all = 0; packetUBXNAVPOSECEF->callbackPointer = NULL; packetUBXNAVPOSECEF->callbackPointerPtr = NULL; packetUBXNAVPOSECEF->callbackData = NULL; packetUBXNAVPOSECEF->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale. This is handy to get data alignment after CRC failure // or if there are no helper functions and the user wants to request fresh data void SFE_UBLOX_GNSS::flushNAVPOSECEF() { if (packetUBXNAVPOSECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPOSECEF->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVPOSECEF(bool enabled) { if (packetUBXNAVPOSECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPOSECEF->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV STATUS automatic support bool SFE_UBLOX_GNSS::getNAVSTATUS(uint16_t maxWait) { if (packetUBXNAVSTATUS == NULL) initPacketUBXNAVSTATUS(); // Check that RAM has been allocated for the STATUS data if (packetUBXNAVSTATUS == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVSTATUS->automaticFlags.flags.bits.automatic && packetUBXNAVSTATUS->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_STATUS); return packetUBXNAVSTATUS->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVSTATUS->automaticFlags.flags.bits.automatic && !packetUBXNAVSTATUS->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_STATUS; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getNAVSTATUS // works. bool SFE_UBLOX_GNSS::setAutoNAVSTATUS(bool enable, uint16_t maxWait) { return setAutoNAVSTATUSrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getNAVSTATUS // works. bool SFE_UBLOX_GNSS::setAutoNAVSTATUS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVSTATUSrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getNAVSTATUS // works. bool SFE_UBLOX_GNSS::setAutoNAVSTATUSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVSTATUS == NULL) initPacketUBXNAVSTATUS(); // Check that RAM has been allocated for the data if (packetUBXNAVSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_STATUS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVSTATUS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVSTATUS->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVSTATUScallback(void (*callbackPointer)(UBX_NAV_STATUS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVSTATUS->callbackData = new UBX_NAV_STATUS_data_t; // Allocate RAM for the main struct } if (packetUBXNAVSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVSTATUScallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVSTATUS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVSTATUScallbackPtr(void (*callbackPointerPtr)(UBX_NAV_STATUS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVSTATUS->callbackData = new UBX_NAV_STATUS_data_t; // Allocate RAM for the main struct } if (packetUBXNAVSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVSTATUScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVSTATUS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and STATUS is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVSTATUS(bool enabled, bool implicitUpdate) { if (packetUBXNAVSTATUS == NULL) initPacketUBXNAVSTATUS(); // Check that RAM has been allocated for the data if (packetUBXNAVSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVSTATUS->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVSTATUS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVSTATUS->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVSTATUS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVSTATUS() { packetUBXNAVSTATUS = new UBX_NAV_STATUS_t; // Allocate RAM for the main struct if (packetUBXNAVSTATUS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVSTATUS: RAM alloc failed!")); #endif return (false); } packetUBXNAVSTATUS->automaticFlags.flags.all = 0; packetUBXNAVSTATUS->callbackPointer = NULL; packetUBXNAVSTATUS->callbackPointerPtr = NULL; packetUBXNAVSTATUS->callbackData = NULL; packetUBXNAVSTATUS->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale. This is handy to get data alignment after CRC failure // or if there are no helper functions and the user wants to request fresh data void SFE_UBLOX_GNSS::flushNAVSTATUS() { if (packetUBXNAVSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSTATUS->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVSTATUS(bool enabled) { if (packetUBXNAVSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSTATUS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** DOP automatic support bool SFE_UBLOX_GNSS::getDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVDOP->automaticFlags.flags.bits.automatic && packetUBXNAVDOP->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getDOP: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_DOP); return packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVDOP->automaticFlags.flags.bits.automatic && !packetUBXNAVDOP->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getDOP: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getDOP: Polling")); // } // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_DOP; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getDOP: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getDOP retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getDOP // works. bool SFE_UBLOX_GNSS::setAutoDOP(bool enable, uint16_t maxWait) { return setAutoDOPrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getDOP // works. bool SFE_UBLOX_GNSS::setAutoDOP(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoDOPrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getDOP // works. bool SFE_UBLOX_GNSS::setAutoDOPrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the data if (packetUBXNAVDOP == NULL) // Only attempt this if RAM allocation was successful return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_DOP; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVDOP->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVDOP->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoDOPcallback(void (*callbackPointer)(UBX_NAV_DOP_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoDOP(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVDOP->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVDOP->callbackData = new UBX_NAV_DOP_data_t; // Allocate RAM for the main struct } if (packetUBXNAVDOP->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoDOPcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVDOP->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoDOPcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_DOP_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoDOP(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVDOP->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVDOP->callbackData = new UBX_NAV_DOP_data_t; // Allocate RAM for the main struct } if (packetUBXNAVDOP->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoDOPcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVDOP->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and DOP is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoDOP(bool enabled, bool implicitUpdate) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the data if (packetUBXNAVDOP == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVDOP->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVDOP->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVDOP->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVDOP->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVDOP and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVDOP() { packetUBXNAVDOP = new UBX_NAV_DOP_t; // Allocate RAM for the main struct if (packetUBXNAVDOP == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVDOP: RAM alloc failed!")); #endif return (false); } packetUBXNAVDOP->automaticFlags.flags.all = 0; packetUBXNAVDOP->callbackPointer = NULL; packetUBXNAVDOP->callbackPointerPtr = NULL; packetUBXNAVDOP->callbackData = NULL; packetUBXNAVDOP->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the DOP data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushDOP() { if (packetUBXNAVDOP == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVDOP->moduleQueried.moduleQueried.all = 0; // Mark all DOPs as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVDOP(bool enabled) { if (packetUBXNAVDOP == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVDOP->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** EOE automatic support bool SFE_UBLOX_GNSS::getNAVEOE(uint16_t maxWait) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the EOE data if (packetUBXNAVEOE == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVEOE->automaticFlags.flags.bits.automatic && packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getEOE: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_EOE); return packetUBXNAVEOE->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVEOE->automaticFlags.flags.bits.automatic && !packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getEOE: Exit immediately")); // } return (false); } else { // Note to self: NAV-EOE is "Periodic" (only). Not sure if it can be polled? // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_EOE; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. bool SFE_UBLOX_GNSS::setAutoNAVEOE(bool enable, uint16_t maxWait) { return setAutoNAVEOErate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. bool SFE_UBLOX_GNSS::setAutoNAVEOE(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVEOErate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getEOE // works. bool SFE_UBLOX_GNSS::setAutoNAVEOErate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data if (packetUBXNAVEOE == NULL) // Only attempt this if RAM allocation was successful return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_EOE; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVEOE->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVEOE->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVEOEcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_EOE_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVEOE(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVEOE->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVEOE->callbackData = new UBX_NAV_EOE_data_t; // Allocate RAM for the main struct } if (packetUBXNAVEOE->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVEOEcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVEOE->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and EOE is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVEOE(bool enabled, bool implicitUpdate) { if (packetUBXNAVEOE == NULL) initPacketUBXNAVEOE(); // Check that RAM has been allocated for the data if (packetUBXNAVEOE == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVEOE->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVEOE->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVEOE->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVEOE and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVEOE() { packetUBXNAVEOE = new UBX_NAV_EOE_t; // Allocate RAM for the main struct if (packetUBXNAVEOE == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVEOE: RAM alloc failed!")); #endif return (false); } packetUBXNAVEOE->automaticFlags.flags.all = 0; packetUBXNAVEOE->callbackPointerPtr = NULL; packetUBXNAVEOE->callbackData = NULL; packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the EOE data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushNAVEOE() { if (packetUBXNAVEOE == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVEOE->moduleQueried.moduleQueried.all = 0; // Mark all EOEs as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVEOE(bool enabled) { if (packetUBXNAVEOE == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVEOE->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** VEH ATT automatic support bool SFE_UBLOX_GNSS::getVehAtt(uint16_t maxWait) { return (getNAVATT(maxWait)); } bool SFE_UBLOX_GNSS::getNAVATT(uint16_t maxWait) { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data if (packetUBXNAVATT == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXNAVATT->automaticFlags.flags.bits.automatic && packetUBXNAVATT->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_ATT); return packetUBXNAVATT->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVATT->automaticFlags.flags.bits.automatic && !packetUBXNAVATT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting HNR PVT so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_ATT; 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); } return (false); // Trap. We should never get here... } // Enable or disable automatic NAV ATT message generation by the GNSS. This changes the way getVehAtt // works. bool SFE_UBLOX_GNSS::setAutoNAVATT(bool enable, uint16_t maxWait) { return setAutoNAVATTrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic NAV ATT message generation by the GNSS. This changes the way getVehAtt // works. bool SFE_UBLOX_GNSS::setAutoNAVATT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVATTrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic NAV ATT attitude message generation by the GNSS. This changes the way getVehAtt // works. bool SFE_UBLOX_GNSS::setAutoNAVATTrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the data if (packetUBXNAVATT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_ATT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVATT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVATT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVATT->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVATTcallback(void (*callbackPointer)(UBX_NAV_ATT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVATT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVATT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVATT->callbackData = new UBX_NAV_ATT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVATT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVATTcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVATT->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVATTcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_ATT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVATT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVATT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVATT->callbackData = new UBX_NAV_ATT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVATT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVATTcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVATT->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and NAV ATT attitude is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVATT(bool enabled, bool implicitUpdate) { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data if (packetUBXNAVATT == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVATT->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVATT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVATT->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVATT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVATT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVATT() { packetUBXNAVATT = new UBX_NAV_ATT_t; // Allocate RAM for the main struct if (packetUBXNAVATT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVATT: RAM alloc failed!")); #endif return (false); } packetUBXNAVATT->automaticFlags.flags.all = 0; packetUBXNAVATT->callbackPointer = NULL; packetUBXNAVATT->callbackPointerPtr = NULL; packetUBXNAVATT->callbackData = NULL; packetUBXNAVATT->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the ATT data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushNAVATT() { if (packetUBXNAVATT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVATT->moduleQueried.moduleQueried.all = 0; // Mark all ATT data as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVATT(bool enabled) { if (packetUBXNAVATT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVATT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** PVT automatic support // Get the latest Position/Velocity/Time solution and fill all global variables bool SFE_UBLOX_GNSS::getPVT(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->automaticFlags.flags.bits.automatic && packetUBXNAVPVT->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getPVT: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_PVT); return packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all; } else if (packetUBXNAVPVT->automaticFlags.flags.bits.automatic && !packetUBXNAVPVT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getPVT: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getPVT: Polling")); // } // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_PVT; packetCfg.len = 0; packetCfg.startingSpot = 0; // packetCfg.startingSpot = 20; //Begin listening at spot 20 so we can record up to 20+packetCfgPayloadSize = 84 bytes Note:now hard-coded in processUBX // 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getPVT: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getPVT retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVT // works. bool SFE_UBLOX_GNSS::setAutoPVT(bool enable, uint16_t maxWait) { return setAutoPVTrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVT // works. bool SFE_UBLOX_GNSS::setAutoPVT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoPVTrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVT // works. bool SFE_UBLOX_GNSS::setAutoPVTrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_PVT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVPVT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVPVT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. This changes the way getPVT works. bool SFE_UBLOX_GNSS::setAutoPVTcallback(void (*callbackPointer)(UBX_NAV_PVT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoPVT(true, false, maxWait); if (!result) return (result); // Bail if setAutoPVT failed if (packetUBXNAVPVT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPVT->callbackData = new UBX_NAV_PVT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPVT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoPVTcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVT->callbackPointer = callbackPointer; // RAM has been allocated so now update the pointer return (true); } bool SFE_UBLOX_GNSS::setAutoPVTcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_PVT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoPVT(true, false, maxWait); if (!result) return (result); // Bail if setAutoPVT failed if (packetUBXNAVPVT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPVT->callbackData = new UBX_NAV_PVT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPVT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoPVTcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVT->callbackPointerPtr = callbackPointerPtr; // RAM has been allocated so now update the pointer return (true); } // In case no config access to the GNSS is possible and PVT is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoPVT(bool enabled, bool implicitUpdate) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVPVT->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVPVT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVPVT->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVPVT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVPVT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVPVT() { packetUBXNAVPVT = new UBX_NAV_PVT_t; // Allocate RAM for the main struct if (packetUBXNAVPVT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVPVT: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVT->automaticFlags.flags.all = 0; packetUBXNAVPVT->callbackPointer = NULL; packetUBXNAVPVT->callbackPointerPtr = NULL; packetUBXNAVPVT->callbackData = NULL; packetUBXNAVPVT->moduleQueried.moduleQueried1.all = 0; packetUBXNAVPVT->moduleQueried.moduleQueried2.all = 0; return (true); } // Mark all the PVT data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushPVT() { if (packetUBXNAVPVT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPVT->moduleQueried.moduleQueried1.all = 0; // Mark all datums as stale (read before) packetUBXNAVPVT->moduleQueried.moduleQueried2.all = 0; } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVPVT(bool enabled) { if (packetUBXNAVPVT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPVT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV ODO automatic support bool SFE_UBLOX_GNSS::getNAVODO(uint16_t maxWait) { if (packetUBXNAVODO == NULL) initPacketUBXNAVODO(); // Check that RAM has been allocated for the ODO data if (packetUBXNAVODO == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVODO->automaticFlags.flags.bits.automatic && packetUBXNAVODO->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_ODO); return packetUBXNAVODO->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVODO->automaticFlags.flags.bits.automatic && !packetUBXNAVODO->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_ODO; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getODO // works. bool SFE_UBLOX_GNSS::setAutoNAVODO(bool enable, uint16_t maxWait) { return setAutoNAVODOrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getODO // works. bool SFE_UBLOX_GNSS::setAutoNAVODO(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVODOrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getODO // works. bool SFE_UBLOX_GNSS::setAutoNAVODOrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVODO == NULL) initPacketUBXNAVODO(); // Check that RAM has been allocated for the data if (packetUBXNAVODO == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_ODO; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVODO->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVODO->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVODO->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVODOcallback(void (*callbackPointer)(UBX_NAV_ODO_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVODO(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVODO->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVODO->callbackData = new UBX_NAV_ODO_data_t; // Allocate RAM for the main struct } if (packetUBXNAVODO->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVODOcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVODO->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVODOcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_ODO_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVODO(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVODO->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVODO->callbackData = new UBX_NAV_ODO_data_t; // Allocate RAM for the main struct } if (packetUBXNAVODO->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVODOcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVODO->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ODO is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVODO(bool enabled, bool implicitUpdate) { if (packetUBXNAVODO == NULL) initPacketUBXNAVODO(); // Check that RAM has been allocated for the data if (packetUBXNAVODO == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVODO->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVODO->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVODO->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVODO->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVODO and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVODO() { packetUBXNAVODO = new UBX_NAV_ODO_t; // Allocate RAM for the main struct if (packetUBXNAVODO == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVODO: RAM alloc failed!")); #endif return (false); } packetUBXNAVODO->automaticFlags.flags.all = 0; packetUBXNAVODO->callbackPointer = NULL; packetUBXNAVODO->callbackPointerPtr = NULL; packetUBXNAVODO->callbackData = NULL; packetUBXNAVODO->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVODO() { if (packetUBXNAVODO == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVODO->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVODO(bool enabled) { if (packetUBXNAVODO == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVODO->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV VELECEF automatic support bool SFE_UBLOX_GNSS::getNAVVELECEF(uint16_t maxWait) { if (packetUBXNAVVELECEF == NULL) initPacketUBXNAVVELECEF(); // Check that RAM has been allocated for the VELECEF data if (packetUBXNAVVELECEF == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_VELECEF); return packetUBXNAVVELECEF->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic && !packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_VELECEF; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVVELECEF(bool enable, uint16_t maxWait) { return setAutoNAVVELECEFrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVVELECEF(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVVELECEFrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVVELECEFrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVVELECEF == NULL) initPacketUBXNAVVELECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVVELECEF == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_VELECEF; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVVELECEF->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVVELECEFcallback(void (*callbackPointer)(UBX_NAV_VELECEF_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVVELECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVVELECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVVELECEF->callbackData = new UBX_NAV_VELECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVVELECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVVELECEFcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELECEF->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVVELECEFcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_VELECEF_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVVELECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVVELECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVVELECEF->callbackData = new UBX_NAV_VELECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVVELECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVVELECEFcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELECEF->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and VELECEF is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVVELECEF(bool enabled, bool implicitUpdate) { if (packetUBXNAVVELECEF == NULL) initPacketUBXNAVVELECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVVELECEF == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVVELECEF->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVVELECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVVELECEF and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVVELECEF() { packetUBXNAVVELECEF = new UBX_NAV_VELECEF_t; // Allocate RAM for the main struct if (packetUBXNAVVELECEF == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVVELECEF: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELECEF->automaticFlags.flags.all = 0; packetUBXNAVVELECEF->callbackPointer = NULL; packetUBXNAVVELECEF->callbackPointerPtr = NULL; packetUBXNAVVELECEF->callbackData = NULL; packetUBXNAVVELECEF->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVVELECEF() { if (packetUBXNAVVELECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVVELECEF->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVVELECEF(bool enabled) { if (packetUBXNAVVELECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVVELECEF->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV VELNED automatic support bool SFE_UBLOX_GNSS::getNAVVELNED(uint16_t maxWait) { if (packetUBXNAVVELNED == NULL) initPacketUBXNAVVELNED(); // Check that RAM has been allocated for the VELNED data if (packetUBXNAVVELNED == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVVELNED->automaticFlags.flags.bits.automatic && packetUBXNAVVELNED->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_VELNED); return packetUBXNAVVELNED->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVVELNED->automaticFlags.flags.bits.automatic && !packetUBXNAVVELNED->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_VELNED; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELNED // works. bool SFE_UBLOX_GNSS::setAutoNAVVELNED(bool enable, uint16_t maxWait) { return setAutoNAVVELNEDrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELNED // works. bool SFE_UBLOX_GNSS::setAutoNAVVELNED(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVVELNEDrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getVELNED // works. bool SFE_UBLOX_GNSS::setAutoNAVVELNEDrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVVELNED == NULL) initPacketUBXNAVVELNED(); // Check that RAM has been allocated for the data if (packetUBXNAVVELNED == NULL) // Only attempt this if RAM allocation was successful return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_VELNED; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVVELNED->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVVELNED->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVVELNED->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVVELNEDcallback(void (*callbackPointer)(UBX_NAV_VELNED_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVVELNED(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVVELNED->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVVELNED->callbackData = new UBX_NAV_VELNED_data_t; // Allocate RAM for the main struct } if (packetUBXNAVVELNED->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVVELNEDcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELNED->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVVELNEDcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_VELNED_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVVELNED(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVVELNED->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVVELNED->callbackData = new UBX_NAV_VELNED_data_t; // Allocate RAM for the main struct } if (packetUBXNAVVELNED->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVVELNEDcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELNED->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and VELNED is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVVELNED(bool enabled, bool implicitUpdate) { if (packetUBXNAVVELNED == NULL) initPacketUBXNAVVELNED(); // Check that RAM has been allocated for the data if (packetUBXNAVVELNED == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVVELNED->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVVELNED->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVVELNED->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVVELNED->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVVELNED and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVVELNED() { packetUBXNAVVELNED = new UBX_NAV_VELNED_t; // Allocate RAM for the main struct if (packetUBXNAVVELNED == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVVELNED: RAM alloc failed!")); #endif return (false); } packetUBXNAVVELNED->automaticFlags.flags.all = 0; packetUBXNAVVELNED->callbackPointer = NULL; packetUBXNAVVELNED->callbackPointerPtr = NULL; packetUBXNAVVELNED->callbackData = NULL; packetUBXNAVVELNED->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVVELNED() { if (packetUBXNAVVELNED == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere! packetUBXNAVVELNED->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVVELNED(bool enabled) { if (packetUBXNAVVELNED == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVVELNED->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV HPPOSECEF automatic support bool SFE_UBLOX_GNSS::getNAVHPPOSECEF(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.automatic && packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_HPPOSECEF); return packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.automatic && !packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_HPPOSECEF; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVHPPOSECEF(bool enable, uint16_t maxWait) { return setAutoNAVHPPOSECEFrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVHPPOSECEF(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVHPPOSECEFrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSECEF // works. bool SFE_UBLOX_GNSS::setAutoNAVHPPOSECEFrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVHPPOSECEF == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_HPPOSECEF; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVHPPOSECEFcallback(void (*callbackPointer)(UBX_NAV_HPPOSECEF_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVHPPOSECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVHPPOSECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVHPPOSECEF->callbackData = new UBX_NAV_HPPOSECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVHPPOSECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVHPPOSECEFcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSECEF->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVHPPOSECEFcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_HPPOSECEF_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVHPPOSECEF(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVHPPOSECEF->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVHPPOSECEF->callbackData = new UBX_NAV_HPPOSECEF_data_t; // Allocate RAM for the main struct } if (packetUBXNAVHPPOSECEF->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVHPPOSECEFcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSECEF->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and HPPOSECEF is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVHPPOSECEF(bool enabled, bool implicitUpdate) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the data if (packetUBXNAVHPPOSECEF == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVHPPOSECEF and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVHPPOSECEF() { packetUBXNAVHPPOSECEF = new UBX_NAV_HPPOSECEF_t; // Allocate RAM for the main struct if (packetUBXNAVHPPOSECEF == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVHPPOSECEF: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSECEF->automaticFlags.flags.all = 0; packetUBXNAVHPPOSECEF->callbackPointer = NULL; packetUBXNAVHPPOSECEF->callbackPointerPtr = NULL; packetUBXNAVHPPOSECEF->callbackData = NULL; packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVHPPOSECEF() { if (packetUBXNAVHPPOSECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVHPPOSECEF(bool enabled) { if (packetUBXNAVHPPOSECEF == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVHPPOSECEF->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV HPPOSLLH automatic support bool SFE_UBLOX_GNSS::getHPPOSLLH(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.automatic && packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getHPPOSLLH: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_HPPOSLLH); return packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.automatic && !packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getHPPOSLLH: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getHPPOSLLH: Polling")); // } // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_HPPOSLLH; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getHPPOSLLH: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getHPPOSLLH retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSLLH // works. bool SFE_UBLOX_GNSS::setAutoHPPOSLLH(bool enable, uint16_t maxWait) { return setAutoHPPOSLLHrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSLLH // works. bool SFE_UBLOX_GNSS::setAutoHPPOSLLH(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoHPPOSLLHrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getHPPOSLLH // works. bool SFE_UBLOX_GNSS::setAutoHPPOSLLHrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the data if (packetUBXNAVHPPOSLLH == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_HPPOSLLH; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoHPPOSLLHcallback(void (*callbackPointer)(UBX_NAV_HPPOSLLH_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHPPOSLLH(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVHPPOSLLH->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVHPPOSLLH->callbackData = new UBX_NAV_HPPOSLLH_data_t; // Allocate RAM for the main struct } if (packetUBXNAVHPPOSLLH->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHPPOSLLHcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSLLH->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoHPPOSLLHcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_HPPOSLLH_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHPPOSLLH(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVHPPOSLLH->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVHPPOSLLH->callbackData = new UBX_NAV_HPPOSLLH_data_t; // Allocate RAM for the main struct } if (packetUBXNAVHPPOSLLH->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHPPOSLLHcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSLLH->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and HPPOSLLH is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoHPPOSLLH(bool enabled, bool implicitUpdate) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the data if (packetUBXNAVHPPOSLLH == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVHPPOSLLH and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVHPPOSLLH() { packetUBXNAVHPPOSLLH = new UBX_NAV_HPPOSLLH_t; // Allocate RAM for the main struct if (packetUBXNAVHPPOSLLH == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVHPPOSLLH: RAM alloc failed!")); #endif return (false); } packetUBXNAVHPPOSLLH->automaticFlags.flags.all = 0; packetUBXNAVHPPOSLLH->callbackPointer = NULL; packetUBXNAVHPPOSLLH->callbackPointerPtr = NULL; packetUBXNAVHPPOSLLH->callbackData = NULL; packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the HPPOSLLH data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushHPPOSLLH() { if (packetUBXNAVHPPOSLLH == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere! packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVHPPOSLLH(bool enabled) { if (packetUBXNAVHPPOSLLH == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVHPPOSLLH->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** PVAT automatic support // Get the latest Position/Velocity/Time solution and fill all global variables bool SFE_UBLOX_GNSS::getNAVPVAT(uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVAT->automaticFlags.flags.bits.automatic && packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate) { checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_PVAT); return packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all; } else if (packetUBXNAVPVAT->automaticFlags.flags.bits.automatic && !packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_PVAT; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT // works. bool SFE_UBLOX_GNSS::setAutoNAVPVAT(bool enable, uint16_t maxWait) { return setAutoNAVPVATrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT // works. bool SFE_UBLOX_GNSS::setAutoNAVPVAT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVPVATrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getPVAT // works. bool SFE_UBLOX_GNSS::setAutoNAVPVATrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_PVAT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVPVAT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. This changes the way getPVAT works. bool SFE_UBLOX_GNSS::setAutoNAVPVATcallback(void (*callbackPointer)(UBX_NAV_PVAT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVPVAT(true, false, maxWait); if (!result) return (result); // Bail if setAutoPVAT failed if (packetUBXNAVPVAT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPVAT->callbackData = new UBX_NAV_PVAT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPVAT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVPVATcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVAT->callbackPointer = callbackPointer; // RAM has been allocated so now update the pointer return (true); } bool SFE_UBLOX_GNSS::setAutoNAVPVATcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_PVAT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVPVAT(true, false, maxWait); if (!result) return (result); // Bail if setAutoPVAT failed if (packetUBXNAVPVAT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVPVAT->callbackData = new UBX_NAV_PVAT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVPVAT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVPVATcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVAT->callbackPointerPtr = callbackPointerPtr; // RAM has been allocated so now update the pointer return (true); } // In case no config access to the GNSS is possible and PVAT is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVPVAT(bool enabled, bool implicitUpdate) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVPVAT->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVPVAT->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVPVAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVPVAT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVPVAT() { packetUBXNAVPVAT = new UBX_NAV_PVAT_t; // Allocate RAM for the main struct if (packetUBXNAVPVAT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVPVAT: RAM alloc failed!")); #endif return (false); } packetUBXNAVPVAT->automaticFlags.flags.all = 0; packetUBXNAVPVAT->callbackPointer = NULL; packetUBXNAVPVAT->callbackPointerPtr = NULL; packetUBXNAVPVAT->callbackData = NULL; packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0; packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0; return (true); } // Mark all the PVAT data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushNAVPVAT() { if (packetUBXNAVPVAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPVAT->moduleQueried.moduleQueried1.all = 0; // Mark all datums as stale (read before) packetUBXNAVPVAT->moduleQueried.moduleQueried2.all = 0; } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVPVAT(bool enabled) { if (packetUBXNAVPVAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVPVAT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV TIMEUTC automatic support bool SFE_UBLOX_GNSS::getNAVTIMEUTC(uint16_t maxWait) { if (packetUBXNAVTIMEUTC == NULL) initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the TIMEUTC data if (packetUBXNAVTIMEUTC == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_TIMEUTC); return packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic && !packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_TIMEUTC; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC // works. bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, uint16_t maxWait) { return setAutoNAVTIMEUTCrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC // works. bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTC(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVTIMEUTCrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMEUTC // works. bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVTIMEUTC == NULL) initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_TIMEUTC; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVTIMEUTCcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_TIMEUTC_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVTIMEUTC(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVTIMEUTC->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVTIMEUTC->callbackData = new UBX_NAV_TIMEUTC_data_t; // Allocate RAM for the main struct } if (packetUBXNAVTIMEUTC->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVTIMEUTCcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVTIMEUTC->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and TIMEUTC is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVTIMEUTC(bool enabled, bool implicitUpdate) { if (packetUBXNAVTIMEUTC == NULL) initPacketUBXNAVTIMEUTC(); // Check that RAM has been allocated for the data if (packetUBXNAVTIMEUTC == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVTIMEUTC->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVTIMEUTC->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVTIMEUTC and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVTIMEUTC() { packetUBXNAVTIMEUTC = new UBX_NAV_TIMEUTC_t; // Allocate RAM for the main struct if (packetUBXNAVTIMEUTC == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVTIMEUTC: RAM alloc failed!")); #endif return (false); } packetUBXNAVTIMEUTC->automaticFlags.flags.all = 0; packetUBXNAVTIMEUTC->callbackPointerPtr = NULL; packetUBXNAVTIMEUTC->callbackData = NULL; packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVTIMEUTC() { if (packetUBXNAVTIMEUTC == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVTIMEUTC->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVTIMEUTC(bool enabled) { if (packetUBXNAVTIMEUTC == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVTIMEUTC->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV CLOCK automatic support bool SFE_UBLOX_GNSS::getNAVCLOCK(uint16_t maxWait) { if (packetUBXNAVCLOCK == NULL) initPacketUBXNAVCLOCK(); // Check that RAM has been allocated for the CLOCK data if (packetUBXNAVCLOCK == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVCLOCK->automaticFlags.flags.bits.automatic && packetUBXNAVCLOCK->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_CLOCK); return packetUBXNAVCLOCK->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVCLOCK->automaticFlags.flags.bits.automatic && !packetUBXNAVCLOCK->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting CLOCK so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_CLOCK; 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); } } // Enable or disable automatic CLOCK message generation by the GNSS. This changes the way getNAVCLOCK // works. bool SFE_UBLOX_GNSS::setAutoNAVCLOCK(bool enable, uint16_t maxWait) { return setAutoNAVCLOCKrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic CLOCK message generation by the GNSS. This changes the way getNAVCLOCK // works. bool SFE_UBLOX_GNSS::setAutoNAVCLOCK(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVCLOCKrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic CLOCK message generation by the GNSS. This changes the way getNAVCLOCK // works. bool SFE_UBLOX_GNSS::setAutoNAVCLOCKrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVCLOCK == NULL) initPacketUBXNAVCLOCK(); // Check that RAM has been allocated for the data if (packetUBXNAVCLOCK == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_CLOCK; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVCLOCK->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVCLOCK->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVCLOCK->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVCLOCKcallback(void (*callbackPointer)(UBX_NAV_CLOCK_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVCLOCK(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVCLOCK->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVCLOCK->callbackData = new UBX_NAV_CLOCK_data_t; // Allocate RAM for the main struct } if (packetUBXNAVCLOCK->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVCLOCKcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVCLOCK->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVCLOCKcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_CLOCK_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVCLOCK(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVCLOCK->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVCLOCK->callbackData = new UBX_NAV_CLOCK_data_t; // Allocate RAM for the main struct } if (packetUBXNAVCLOCK->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVCLOCKcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVCLOCK->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and NAV CLOCK is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVCLOCK(bool enabled, bool implicitUpdate) { if (packetUBXNAVCLOCK == NULL) initPacketUBXNAVCLOCK(); // Check that RAM has been allocated for the CLOCK data if (packetUBXNAVCLOCK == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXNAVCLOCK->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVCLOCK->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVCLOCK->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVCLOCK->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVCLOCK and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVCLOCK() { packetUBXNAVCLOCK = new UBX_NAV_CLOCK_t; // Allocate RAM for the main struct if (packetUBXNAVCLOCK == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVCLOCK: RAM alloc failed!")); #endif return (false); } packetUBXNAVCLOCK->automaticFlags.flags.all = 0; packetUBXNAVCLOCK->callbackPointer = NULL; packetUBXNAVCLOCK->callbackPointerPtr = NULL; packetUBXNAVCLOCK->callbackData = NULL; packetUBXNAVCLOCK->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVCLOCK() { if (packetUBXNAVCLOCK == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVCLOCK->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVCLOCK(bool enabled) { if (packetUBXNAVCLOCK == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) 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 bool 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 bool SFE_UBLOX_GNSS::initPacketUBXNAVTIMELS() { packetUBXNAVTIMELS = new UBX_NAV_TIMELS_t; // Allocate RAM for the main struct if (packetUBXNAVTIMELS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVTIMELS: RAM alloc failed!")); #endif return (false); } packetUBXNAVTIMELS->automaticFlags.flags.all = 0; packetUBXNAVTIMELS->callbackPointer = NULL; packetUBXNAVTIMELS->callbackPointerPtr = NULL; packetUBXNAVTIMELS->callbackData = NULL; packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0; return (true); } // ***** NAV SVIN automatic support // Reads survey in status and sets the global variables // for status, position valid, observation time, and mean 3D StdDev // Returns true if commands was successful bool SFE_UBLOX_GNSS::getSurveyStatus(uint16_t maxWait) { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Abort if the RAM allocation failed return (false); if (packetUBXNAVSVIN->automaticFlags.flags.bits.automatic && packetUBXNAVSVIN->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_SVIN); return packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVSVIN->automaticFlags.flags.bits.automatic && !packetUBXNAVSVIN->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting SVIN so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_SVIN; 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); } } // Enable or disable automatic SVIN message generation by the GNSS. This changes the way getSurveyStatus // works. bool SFE_UBLOX_GNSS::setAutoNAVSVIN(bool enable, uint16_t maxWait) { return setAutoNAVSVINrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic SVIN message generation by the GNSS. This changes the way getSurveyStatus // works. bool SFE_UBLOX_GNSS::setAutoNAVSVIN(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVSVINrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic SVIN message generation by the GNSS. This changes the way getSurveyStatus // works. bool SFE_UBLOX_GNSS::setAutoNAVSVINrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the data if (packetUBXNAVSVIN == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_SVIN; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVSVIN->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVSVIN->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVSVINcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_SVIN_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVSVIN(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVSVIN->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVSVIN->callbackData = new UBX_NAV_SVIN_data_t; // Allocate RAM for the main struct } if (packetUBXNAVSVIN->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVSVINcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVSVIN->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and SVIN is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVSVIN(bool enabled, bool implicitUpdate) { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXNAVSVIN->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVSVIN->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVSVIN->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVSVIN->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVSVIN and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVSVIN() { packetUBXNAVSVIN = new UBX_NAV_SVIN_t; // Allocate RAM for the main struct if (packetUBXNAVSVIN == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVSVIN: RAM alloc failed!")); #endif return (false); } packetUBXNAVSVIN->automaticFlags.flags.all = 0; packetUBXNAVSVIN->callbackPointerPtr = NULL; packetUBXNAVSVIN->callbackData = NULL; packetUBXNAVSVIN->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVSVIN() { if (packetUBXNAVSVIN == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSVIN->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVSVIN(bool enabled) { if (packetUBXNAVSVIN == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSVIN->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV SAT automatic support // Signal information // Returns true if commands was successful bool SFE_UBLOX_GNSS::getNAVSAT(uint16_t maxWait) { if (packetUBXNAVSAT == NULL) initPacketUBXNAVSAT(); // Check that RAM has been allocated for the NAVSAT data if (packetUBXNAVSAT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVSAT->automaticFlags.flags.bits.automatic && packetUBXNAVSAT->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_SAT); return packetUBXNAVSAT->moduleQueried; } else if (packetUBXNAVSAT->automaticFlags.flags.bits.automatic && !packetUBXNAVSAT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting NAVSAT so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_SAT; 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); } } // Enable or disable automatic NAVSAT message generation by the GNSS. This changes the way getNAVSAT // works. bool SFE_UBLOX_GNSS::setAutoNAVSAT(bool enable, uint16_t maxWait) { return setAutoNAVSATrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic NAVSAT message generation by the GNSS. This changes the way getNAVSAT // works. bool SFE_UBLOX_GNSS::setAutoNAVSAT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoNAVSATrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic NAV SAT message generation by the GNSS. This changes the way getNAVSAT // works. bool SFE_UBLOX_GNSS::setAutoNAVSATrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVSAT == NULL) initPacketUBXNAVSAT(); // Check that RAM has been allocated for the data if (packetUBXNAVSAT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_SAT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVSAT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVSAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVSAT->moduleQueried = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoNAVSATcallback(void (*callbackPointer)(UBX_NAV_SAT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVSAT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVSAT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVSAT->callbackData = new UBX_NAV_SAT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVSAT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVSATcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVSAT->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoNAVSATcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_SAT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoNAVSAT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVSAT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVSAT->callbackData = new UBX_NAV_SAT_data_t; // Allocate RAM for the main struct } if (packetUBXNAVSAT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoNAVSATcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVSAT->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and NAV SAT is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoNAVSAT(bool enabled, bool implicitUpdate) { if (packetUBXNAVSAT == NULL) initPacketUBXNAVSAT(); // Check that RAM has been allocated for the NAVSAT data if (packetUBXNAVSAT == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXNAVSAT->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVSAT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVSAT->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVSAT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVSAT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVSAT() { packetUBXNAVSAT = new UBX_NAV_SAT_t; // Allocate RAM for the main struct if (packetUBXNAVSAT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVSAT: RAM alloc failed!")); #endif return (false); } packetUBXNAVSAT->automaticFlags.flags.all = 0; packetUBXNAVSAT->callbackPointer = NULL; packetUBXNAVSAT->callbackPointerPtr = NULL; packetUBXNAVSAT->callbackData = NULL; packetUBXNAVSAT->moduleQueried = false; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVSAT() { if (packetUBXNAVSAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSAT->moduleQueried = false; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVSAT(bool enabled) { if (packetUBXNAVSAT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVSAT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** NAV RELPOSNED automatic support // Relative Positioning Information in NED frame // Returns true if commands was successful // Note: // RELPOSNED on the M8 is only 40 bytes long // RELPOSNED on the F9 is 64 bytes long and contains much more information bool SFE_UBLOX_GNSS::getRELPOSNED(uint16_t maxWait) { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVRELPOSNED->automaticFlags.flags.bits.automatic && packetUBXNAVRELPOSNED->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_RELPOSNED); return packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVRELPOSNED->automaticFlags.flags.bits.automatic && !packetUBXNAVRELPOSNED->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting RELPOSNED so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_RELPOSNED; 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); } } // Enable or disable automatic RELPOSNED message generation by the GNSS. This changes the way getRELPOSNED // works. bool SFE_UBLOX_GNSS::setAutoRELPOSNED(bool enable, uint16_t maxWait) { return setAutoRELPOSNEDrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic RELPOSNED message generation by the GNSS. This changes the way getRELPOSNED // works. bool SFE_UBLOX_GNSS::setAutoRELPOSNED(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoRELPOSNEDrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic RELPOSNED message generation by the GNSS. This changes the way getRELPOSNED // works. bool SFE_UBLOX_GNSS::setAutoRELPOSNEDrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the data if (packetUBXNAVRELPOSNED == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_RELPOSNED; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVRELPOSNED->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVRELPOSNED->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoRELPOSNEDcallback(void (*callbackPointer)(UBX_NAV_RELPOSNED_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRELPOSNED(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVRELPOSNED->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVRELPOSNED->callbackData = new UBX_NAV_RELPOSNED_data_t; // Allocate RAM for the main struct } if (packetUBXNAVRELPOSNED->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRELPOSNEDcallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVRELPOSNED->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoRELPOSNEDcallbackPtr(void (*callbackPointerPtr)(UBX_NAV_RELPOSNED_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRELPOSNED(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVRELPOSNED->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVRELPOSNED->callbackData = new UBX_NAV_RELPOSNED_data_t; // Allocate RAM for the main struct } if (packetUBXNAVRELPOSNED->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRELPOSNEDcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVRELPOSNED->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and RELPOSNED is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoRELPOSNED(bool enabled, bool implicitUpdate) { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXNAVRELPOSNED->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVRELPOSNED->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVRELPOSNED->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVRELPOSNED->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVRELPOSNED and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVRELPOSNED() { packetUBXNAVRELPOSNED = new UBX_NAV_RELPOSNED_t; // Allocate RAM for the main struct if (packetUBXNAVRELPOSNED == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVRELPOSNED: RAM alloc failed!")); #endif return (false); } packetUBXNAVRELPOSNED->automaticFlags.flags.all = 0; packetUBXNAVRELPOSNED->callbackPointer = NULL; packetUBXNAVRELPOSNED->callbackPointerPtr = NULL; packetUBXNAVRELPOSNED->callbackData = NULL; packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushNAVRELPOSNED() { if (packetUBXNAVRELPOSNED == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logNAVRELPOSNED(bool enabled) { if (packetUBXNAVRELPOSNED == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVRELPOSNED->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** AOPSTATUS automatic support bool SFE_UBLOX_GNSS::getAOPSTATUS(uint16_t maxWait) { if (packetUBXNAVAOPSTATUS == NULL) initPacketUBXNAVAOPSTATUS(); // Check that RAM has been allocated for the AOPSTATUS data if (packetUBXNAVAOPSTATUS == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.automatic && packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getAOPSTATUS: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_NAV, UBX_NAV_AOPSTATUS); return packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.all; } else if (packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.automatic && !packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getAOPSTATUS: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getAOPSTATUS: Polling")); // } // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_NAV; packetCfg.id = UBX_NAV_AOPSTATUS; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getAOPSTATUS: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getAOPSTATUS retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getAOPSTATUS // works. bool SFE_UBLOX_GNSS::setAutoAOPSTATUS(bool enable, uint16_t maxWait) { return setAutoAOPSTATUSrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getAOPSTATUS // works. bool SFE_UBLOX_GNSS::setAutoAOPSTATUS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoAOPSTATUSrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getAOPSTATUS // works. bool SFE_UBLOX_GNSS::setAutoAOPSTATUSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXNAVAOPSTATUS == NULL) initPacketUBXNAVAOPSTATUS(); // Check that RAM has been allocated for the data if (packetUBXNAVAOPSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_NAV; payloadCfg[1] = UBX_NAV_AOPSTATUS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoAOPSTATUScallback(void (*callbackPointer)(UBX_NAV_AOPSTATUS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoAOPSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVAOPSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVAOPSTATUS->callbackData = new UBX_NAV_AOPSTATUS_data_t; // Allocate RAM for the main struct } if (packetUBXNAVAOPSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoAOPSTATUScallback: RAM alloc failed!")); #endif return (false); } packetUBXNAVAOPSTATUS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoAOPSTATUScallbackPtr(void (*callbackPointerPtr)(UBX_NAV_AOPSTATUS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoAOPSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXNAVAOPSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXNAVAOPSTATUS->callbackData = new UBX_NAV_AOPSTATUS_data_t; // Allocate RAM for the main struct } if (packetUBXNAVAOPSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoAOPSTATUScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXNAVAOPSTATUS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and AOPSTATUS is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoAOPSTATUS(bool enabled, bool implicitUpdate) { if (packetUBXNAVAOPSTATUS == NULL) initPacketUBXNAVAOPSTATUS(); // Check that RAM has been allocated for the data if (packetUBXNAVAOPSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.automatic != enabled || packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.automatic = enabled; packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXNAVAOPSTATUS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXNAVAOPSTATUS() { packetUBXNAVAOPSTATUS = new UBX_NAV_AOPSTATUS_t; // Allocate RAM for the main struct if (packetUBXNAVAOPSTATUS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXNAVAOPSTATUS: RAM alloc failed!")); #endif return (false); } packetUBXNAVAOPSTATUS->automaticFlags.flags.all = 0; packetUBXNAVAOPSTATUS->callbackPointer = NULL; packetUBXNAVAOPSTATUS->callbackPointerPtr = NULL; packetUBXNAVAOPSTATUS->callbackData = NULL; packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the AOPSTATUS data as read/stale. This is handy to get data alignment after CRC failure void SFE_UBLOX_GNSS::flushAOPSTATUS() { if (packetUBXNAVAOPSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.all = 0; // Mark all AOPSTATUSs as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logAOPSTATUS(bool enabled) { if (packetUBXNAVAOPSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXNAVAOPSTATUS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** RXM PMP automatic support // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool SFE_UBLOX_GNSS::setRXMPMPcallbackPtr(void (*callbackPointer)(UBX_RXM_PMP_data_t *)) { if (packetUBXRXMPMP == NULL) initPacketUBXRXMPMP(); // Check that RAM has been allocated for the data if (packetUBXRXMPMP == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXRXMPMP->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMPMP->callbackData = new UBX_RXM_PMP_data_t; // Allocate RAM for the main struct } if (packetUBXRXMPMP->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMPMPcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMPMP->callbackPointerPtr = callbackPointer; return (true); } // PRIVATE: Allocate RAM for packetUBXRXMPMP and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMPMP() { packetUBXRXMPMP = new UBX_RXM_PMP_t; // Allocate RAM for the main struct if (packetUBXRXMPMP == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMPMP: RAM alloc failed!")); #endif return (false); } packetUBXRXMPMP->automaticFlags.flags.all = 0; packetUBXRXMPMP->callbackPointerPtr = NULL; packetUBXRXMPMP->callbackData = NULL; return (true); } // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool SFE_UBLOX_GNSS::setRXMPMPmessageCallbackPtr(void (*callbackPointer)(UBX_RXM_PMP_message_data_t *)) { if (packetUBXRXMPMPmessage == NULL) initPacketUBXRXMPMPmessage(); // Check that RAM has been allocated for the data if (packetUBXRXMPMPmessage == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXRXMPMPmessage->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMPMPmessage->callbackData = new UBX_RXM_PMP_message_data_t; // Allocate RAM for the main struct } if (packetUBXRXMPMPmessage->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMPMPmessagecallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMPMPmessage->callbackPointerPtr = callbackPointer; return (true); } // PRIVATE: Allocate RAM for packetUBXRXMPMPmessage and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage() { packetUBXRXMPMPmessage = new UBX_RXM_PMP_message_t; // Allocate RAM for the main struct if (packetUBXRXMPMPmessage == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMPMPmessage: RAM alloc failed!")); #endif return (false); } packetUBXRXMPMPmessage->automaticFlags.flags.all = 0; packetUBXRXMPMPmessage->callbackPointerPtr = NULL; packetUBXRXMPMPmessage->callbackData = NULL; return (true); } // ***** RXM QZSSL6 automatic support // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool SFE_UBLOX_GNSS::setRXMQZSSL6messageCallbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_message_data_t *)) { if (packetUBXRXMQZSSL6message == NULL) initPacketUBXRXMQZSSL6message(); // Check that RAM has been allocated for the data if (packetUBXRXMQZSSL6message == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXRXMQZSSL6message->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMQZSSL6message->callbackData = new UBX_RXM_QZSSL6_message_data_t[UBX_RXM_QZSSL6_NUM_CHANNELS]; // Allocate RAM for the main struct } if (packetUBXRXMQZSSL6message->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMQZSSL6messagecallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMQZSSL6message->callbackPointerPtr = callbackPointer; return (true); } // PRIVATE: Allocate RAM for packetUBXRXMQZSSL6message and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMQZSSL6message() { packetUBXRXMQZSSL6message = new UBX_RXM_QZSSL6_message_t; // Allocate RAM for the main struct if (packetUBXRXMQZSSL6message == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMQZSSL6message: RAM alloc failed!")); #endif return (false); } packetUBXRXMQZSSL6message->automaticFlags.flags.all = 0; packetUBXRXMQZSSL6message->callbackPointerPtr = NULL; packetUBXRXMQZSSL6message->callbackData = NULL; return (true); } bool SFE_UBLOX_GNSS::setRXMCORcallbackPtr(void (*callbackPointer)(UBX_RXM_COR_data_t *)) { if (packetUBXRXMCOR == NULL) initPacketUBXRXMCOR(); // Check that RAM has been allocated for the data if (packetUBXRXMCOR == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXRXMCOR->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMCOR->callbackData = new UBX_RXM_COR_data_t; // Allocate RAM for the main struct } if (packetUBXRXMCOR->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMCORcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMCOR->callbackPointerPtr = callbackPointer; return (true); } // PRIVATE: Allocate RAM for packetUBXRXMCOR and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMCOR() { packetUBXRXMCOR = new UBX_RXM_COR_t; // Allocate RAM for the main struct if (packetUBXRXMCOR == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMCOR: RAM alloc failed!")); #endif return (false); } packetUBXRXMCOR->automaticFlags.flags.all = 0; packetUBXRXMCOR->callbackPointerPtr = NULL; packetUBXRXMCOR->callbackData = NULL; return (true); } // ***** RXM SFRBX automatic support bool SFE_UBLOX_GNSS::getRXMSFRBX(uint16_t maxWait) { if (packetUBXRXMSFRBX == NULL) initPacketUBXRXMSFRBX(); // Check that RAM has been allocated for the SFRBX data if (packetUBXRXMSFRBX == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXRXMSFRBX->automaticFlags.flags.bits.automatic && packetUBXRXMSFRBX->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_RXM, UBX_RXM_SFRBX); return packetUBXRXMSFRBX->moduleQueried; } else if (packetUBXRXMSFRBX->automaticFlags.flags.bits.automatic && !packetUBXRXMSFRBX->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // SFRBX is output-only. It cannot be polled... // Strictly, getRXMSFRBX should be deprecated. But, to keep the library backward compatible, return(false) here. // See issue #167 for details return (false); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMSFRBX // works. bool SFE_UBLOX_GNSS::setAutoRXMSFRBX(bool enable, uint16_t maxWait) { return setAutoRXMSFRBXrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMSFRBX // works. bool SFE_UBLOX_GNSS::setAutoRXMSFRBX(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoRXMSFRBXrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMSFRBX // works. bool SFE_UBLOX_GNSS::setAutoRXMSFRBXrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXRXMSFRBX == NULL) initPacketUBXRXMSFRBX(); // Check that RAM has been allocated for the data if (packetUBXRXMSFRBX == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_RXM; payloadCfg[1] = UBX_RXM_SFRBX; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXRXMSFRBX->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXRXMSFRBX->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXRXMSFRBX->moduleQueried = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoRXMSFRBXcallback(void (*callbackPointer)(UBX_RXM_SFRBX_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRXMSFRBX(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXRXMSFRBX->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMSFRBX->callbackData = new UBX_RXM_SFRBX_data_t; // Allocate RAM for the main struct } if (packetUBXRXMSFRBX->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMSFRBXcallback: RAM alloc failed!")); #endif return (false); } packetUBXRXMSFRBX->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoRXMSFRBXcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_SFRBX_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRXMSFRBX(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXRXMSFRBX->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMSFRBX->callbackData = new UBX_RXM_SFRBX_data_t; // Allocate RAM for the main struct } if (packetUBXRXMSFRBX->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMSFRBXcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMSFRBX->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and SFRBX is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoRXMSFRBX(bool enabled, bool implicitUpdate) { if (packetUBXRXMSFRBX == NULL) initPacketUBXRXMSFRBX(); // Check that RAM has been allocated for the data if (packetUBXRXMSFRBX == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXRXMSFRBX->automaticFlags.flags.bits.automatic != enabled || packetUBXRXMSFRBX->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXRXMSFRBX->automaticFlags.flags.bits.automatic = enabled; packetUBXRXMSFRBX->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXRXMSFRBX and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMSFRBX() { packetUBXRXMSFRBX = new UBX_RXM_SFRBX_t; // Allocate RAM for the main struct if (packetUBXRXMSFRBX == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMSFRBX: RAM alloc failed!")); #endif return (false); } packetUBXRXMSFRBX->automaticFlags.flags.all = 0; packetUBXRXMSFRBX->callbackPointer = NULL; packetUBXRXMSFRBX->callbackPointerPtr = NULL; packetUBXRXMSFRBX->callbackData = NULL; packetUBXRXMSFRBX->moduleQueried = false; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushRXMSFRBX() { if (packetUBXRXMSFRBX == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXRXMSFRBX->moduleQueried = false; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logRXMSFRBX(bool enabled) { if (packetUBXRXMSFRBX == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXRXMSFRBX->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** RXM RAWX automatic support bool SFE_UBLOX_GNSS::getRXMRAWX(uint16_t maxWait) { if (packetUBXRXMRAWX == NULL) initPacketUBXRXMRAWX(); // Check that RAM has been allocated for the RAWX data if (packetUBXRXMRAWX == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXRXMRAWX->automaticFlags.flags.bits.automatic && packetUBXRXMRAWX->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_RXM, UBX_RXM_RAWX); return packetUBXRXMRAWX->moduleQueried; } else if (packetUBXRXMRAWX->automaticFlags.flags.bits.automatic && !packetUBXRXMRAWX->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_RXM; packetCfg.id = UBX_RXM_RAWX; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMRAWX // works. bool SFE_UBLOX_GNSS::setAutoRXMRAWX(bool enable, uint16_t maxWait) { return setAutoRXMRAWXrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMRAWX // works. bool SFE_UBLOX_GNSS::setAutoRXMRAWX(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoRXMRAWXrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getRXMRAWX // works. bool SFE_UBLOX_GNSS::setAutoRXMRAWXrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXRXMRAWX == NULL) initPacketUBXRXMRAWX(); // Check that RAM has been allocated for the data if (packetUBXRXMRAWX == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_RXM; payloadCfg[1] = UBX_RXM_RAWX; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXRXMRAWX->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXRXMRAWX->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXRXMRAWX->moduleQueried = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoRXMRAWXcallback(void (*callbackPointer)(UBX_RXM_RAWX_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRXMRAWX(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXRXMRAWX->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMRAWX->callbackData = new UBX_RXM_RAWX_data_t; // Allocate RAM for the main struct } if (packetUBXRXMRAWX->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMRAWXcallback: RAM alloc failed!")); #endif return (false); } packetUBXRXMRAWX->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoRXMRAWXcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_RAWX_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoRXMRAWX(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXRXMRAWX->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXRXMRAWX->callbackData = new UBX_RXM_RAWX_data_t; // Allocate RAM for the main struct } if (packetUBXRXMRAWX->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoRXMRAWXcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXRXMRAWX->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and VELNED is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoRXMRAWX(bool enabled, bool implicitUpdate) { if (packetUBXRXMRAWX == NULL) initPacketUBXRXMRAWX(); // Check that RAM has been allocated for the data if (packetUBXRXMRAWX == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXRXMRAWX->automaticFlags.flags.bits.automatic != enabled || packetUBXRXMRAWX->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXRXMRAWX->automaticFlags.flags.bits.automatic = enabled; packetUBXRXMRAWX->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXRXMRAWX and initialize it bool SFE_UBLOX_GNSS::initPacketUBXRXMRAWX() { packetUBXRXMRAWX = new UBX_RXM_RAWX_t; // Allocate RAM for the main struct if (packetUBXRXMRAWX == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXRXMRAWX: RAM alloc failed!")); #endif return (false); } packetUBXRXMRAWX->automaticFlags.flags.all = 0; packetUBXRXMRAWX->callbackPointer = NULL; packetUBXRXMRAWX->callbackPointerPtr = NULL; packetUBXRXMRAWX->callbackData = NULL; packetUBXRXMRAWX->moduleQueried = false; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushRXMRAWX() { if (packetUBXRXMRAWX == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXRXMRAWX->moduleQueried = false; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logRXMRAWX(bool enabled) { if (packetUBXRXMRAWX == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXRXMRAWX->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** CFG automatic support // Get the latest CFG PRT - as used by isConnected // Here's the dilemma: // The NEO-D9S doesn't support NAV-RATE so, if we want to include the D9 without creating a special class for it, // we need to use something else as the 'isConnected' test. The D9 does support CFG-PRT so we'll use that. // BUT many users could already be using getPortSettings and expecting the settings to be returned in packetCfg. // So, for isConnected ONLY, we need to enable auto support for CFG-PRT and then disable it afterwards so the settings // go back to being returned in packetCfg... What a tangled web we weave...! bool SFE_UBLOX_GNSS::getPortSettingsInternal(uint8_t portID, uint16_t maxWait) { if (packetUBXCFGPRT == NULL) initPacketUBXCFGPRT(); // Check that RAM has been allocated for the data if (packetUBXCFGPRT == NULL) // Bail if the RAM allocation failed return (false); // The CFG PRT message will never be produced automatically - that would be pointless. // There is no setAutoCFGPRT function. We always need to poll explicitly. packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_PRT; packetCfg.len = 1; packetCfg.startingSpot = 0; payloadCfg[0] = portID; // The data is parsed as part of processing the response sfe_ublox_status_e result = sendCommand(&packetCfg, maxWait); bool retVal = false; if (result == SFE_UBLOX_STATUS_DATA_RECEIVED) retVal = true; if (result == SFE_UBLOX_STATUS_DATA_OVERWRITTEN) retVal = true; // Now disable automatic support for CFG-PRT (see above) delete packetUBXCFGPRT; packetUBXCFGPRT = NULL; return (retVal); } // PRIVATE: Allocate RAM for packetUBXCFGPRT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXCFGPRT() { packetUBXCFGPRT = new UBX_CFG_PRT_t; // Allocate RAM for the main struct if (packetUBXCFGPRT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXCFGPRT: RAM alloc failed!")); #endif return (false); } packetUBXCFGPRT->dataValid = false; return (true); } // Get the latest CFG RATE bool SFE_UBLOX_GNSS::getNavigationFrequencyInternal(uint16_t maxWait) { if (packetUBXCFGRATE == NULL) initPacketUBXCFGRATE(); // Check that RAM has been allocated for the data if (packetUBXCFGRATE == NULL) // Bail if the RAM allocation failed return (false); // 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; // 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 packetUBXCFGRATE and initialize it bool SFE_UBLOX_GNSS::initPacketUBXCFGRATE() { packetUBXCFGRATE = new UBX_CFG_RATE_t; // Allocate RAM for the main struct if (packetUBXCFGRATE == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXCFGRATE: RAM alloc failed!")); #endif return (false); } packetUBXCFGRATE->automaticFlags.flags.all = 0; // Redundant packetUBXCFGRATE->moduleQueried.moduleQueried.all = 0; // Mark all data as stale/read return (true); } // ***** TIM TM2 automatic support bool SFE_UBLOX_GNSS::getTIMTM2(uint16_t maxWait) { if (packetUBXTIMTM2 == NULL) initPacketUBXTIMTM2(); // Check that RAM has been allocated for the TM2 data if (packetUBXTIMTM2 == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXTIMTM2->automaticFlags.flags.bits.automatic && packetUBXTIMTM2->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data checkUbloxInternal(&packetCfg, UBX_CLASS_TIM, UBX_TIM_TM2); return packetUBXTIMTM2->moduleQueried.moduleQueried.bits.all; } else if (packetUBXTIMTM2->automaticFlags.flags.bits.automatic && !packetUBXTIMTM2->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... return (false); } else { // The GPS is not automatically reporting navigation position so we have to poll explicitly packetCfg.cls = UBX_CLASS_TIM; packetCfg.id = UBX_TIM_TM2; 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); } } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMTM2 // works. bool SFE_UBLOX_GNSS::setAutoTIMTM2(bool enable, uint16_t maxWait) { return setAutoTIMTM2rate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMTM2 // works. bool SFE_UBLOX_GNSS::setAutoTIMTM2(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoTIMTM2rate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic navigation message generation by the GNSS. This changes the way getTIMTM2 // works. bool SFE_UBLOX_GNSS::setAutoTIMTM2rate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXTIMTM2 == NULL) initPacketUBXTIMTM2(); // Check that RAM has been allocated for the data if (packetUBXTIMTM2 == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_TIM; payloadCfg[1] = UBX_TIM_TM2; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXTIMTM2->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXTIMTM2->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXTIMTM2->moduleQueried.moduleQueried.bits.all = false; return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoTIMTM2callback(void (*callbackPointer)(UBX_TIM_TM2_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoTIMTM2(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXTIMTM2->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXTIMTM2->callbackData = new UBX_TIM_TM2_data_t; // Allocate RAM for the main struct } if (packetUBXTIMTM2->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoTIMTM2callback: RAM alloc failed!")); #endif return (false); } packetUBXTIMTM2->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoTIMTM2callbackPtr(void (*callbackPointerPtr)(UBX_TIM_TM2_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoTIMTM2(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXTIMTM2->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXTIMTM2->callbackData = new UBX_TIM_TM2_data_t; // Allocate RAM for the main struct } if (packetUBXTIMTM2->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoTIMTM2callbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXTIMTM2->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and VELNED is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoTIMTM2(bool enabled, bool implicitUpdate) { if (packetUBXTIMTM2 == NULL) initPacketUBXTIMTM2(); // Check that RAM has been allocated for the data if (packetUBXTIMTM2 == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXTIMTM2->automaticFlags.flags.bits.automatic != enabled || packetUBXTIMTM2->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXTIMTM2->automaticFlags.flags.bits.automatic = enabled; packetUBXTIMTM2->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXTIMTM2 and initialize it bool SFE_UBLOX_GNSS::initPacketUBXTIMTM2() { packetUBXTIMTM2 = new UBX_TIM_TM2_t; // Allocate RAM for the main struct if (packetUBXTIMTM2 == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXTIMTM2: RAM alloc failed!")); #endif return (false); } packetUBXTIMTM2->automaticFlags.flags.all = 0; packetUBXTIMTM2->callbackPointer = NULL; packetUBXTIMTM2->callbackPointerPtr = NULL; packetUBXTIMTM2->callbackData = NULL; packetUBXTIMTM2->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushTIMTM2() { if (packetUBXTIMTM2 == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXTIMTM2->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logTIMTM2(bool enabled) { if (packetUBXTIMTM2 == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXTIMTM2->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** ESF ALG automatic support bool SFE_UBLOX_GNSS::getEsfAlignment(uint16_t maxWait) { return (getESFALG(maxWait)); } bool SFE_UBLOX_GNSS::getESFALG(uint16_t maxWait) { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the ESF alignment data if (packetUBXESFALG == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXESFALG->automaticFlags.flags.bits.automatic && packetUBXESFALG->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getEsfAlignment: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_ESF, UBX_ESF_ALG); return packetUBXESFALG->moduleQueried.moduleQueried.bits.all; } else if (packetUBXESFALG->automaticFlags.flags.bits.automatic && !packetUBXESFALG->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getEsfAlignment: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfAlignment: Polling")); // } // The GPS is not automatically reporting HNR PVT so we have to poll explicitly packetCfg.cls = UBX_CLASS_ESF; packetCfg.id = UBX_ESF_ALG; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfAlignment: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getEsfAlignment retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic ESF ALG message generation by the GNSS. This changes the way getEsfAlignment // works. bool SFE_UBLOX_GNSS::setAutoESFALG(bool enable, uint16_t maxWait) { return setAutoESFALGrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic ESF ALG message generation by the GNSS. This changes the way getEsfAlignment // works. bool SFE_UBLOX_GNSS::setAutoESFALG(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoESFALGrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic ESF ALG message generation by the GNSS. This changes the way getEsfAlignment // works. bool SFE_UBLOX_GNSS::setAutoESFALGrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the data if (packetUBXESFALG == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_ESF; payloadCfg[1] = UBX_ESF_ALG; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXESFALG->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXESFALG->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXESFALG->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFALGcallback(void (*callbackPointer)(UBX_ESF_ALG_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFALG(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFALG->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFALG->callbackData = new UBX_ESF_ALG_data_t; // Allocate RAM for the main struct } if (packetUBXESFALG->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFALGcallback: RAM alloc failed!")); #endif return (false); } packetUBXESFALG->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoESFALGcallbackPtr(void (*callbackPointerPtr)(UBX_ESF_ALG_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFALG(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFALG->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFALG->callbackData = new UBX_ESF_ALG_data_t; // Allocate RAM for the main struct } if (packetUBXESFALG->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFALGcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXESFALG->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ESF ALG is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoESFALG(bool enabled, bool implicitUpdate) { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the ESF alignment data if (packetUBXESFALG == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXESFALG->automaticFlags.flags.bits.automatic != enabled || packetUBXESFALG->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXESFALG->automaticFlags.flags.bits.automatic = enabled; packetUBXESFALG->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXESFALG and initialize it bool SFE_UBLOX_GNSS::initPacketUBXESFALG() { packetUBXESFALG = new UBX_ESF_ALG_t; // Allocate RAM for the main struct if (packetUBXESFALG == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXESFALG: RAM alloc failed!")); #endif return (false); } packetUBXESFALG->automaticFlags.flags.all = 0; packetUBXESFALG->callbackPointer = NULL; packetUBXESFALG->callbackPointerPtr = NULL; packetUBXESFALG->callbackData = NULL; packetUBXESFALG->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushESFALG() { if (packetUBXESFALG == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFALG->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logESFALG(bool enabled) { if (packetUBXESFALG == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFALG->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** ESF STATUS automatic support bool SFE_UBLOX_GNSS::getEsfInfo(uint16_t maxWait) { return (getESFSTATUS(maxWait)); } bool SFE_UBLOX_GNSS::getESFSTATUS(uint16_t maxWait) { if (packetUBXESFSTATUS == NULL) initPacketUBXESFSTATUS(); // Check that RAM has been allocated for the ESF status data if (packetUBXESFSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXESFSTATUS->automaticFlags.flags.bits.automatic && packetUBXESFSTATUS->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getEsfInfo: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_ESF, UBX_ESF_STATUS); return packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.all; } else if (packetUBXESFSTATUS->automaticFlags.flags.bits.automatic && !packetUBXESFSTATUS->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getEsfInfo: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfInfo: Polling")); // } // The GPS is not automatically reporting HNR PVT so we have to poll explicitly packetCfg.cls = UBX_CLASS_ESF; packetCfg.id = UBX_ESF_STATUS; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfInfo: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getEsfInfo retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic ESF STATUS message generation by the GNSS. This changes the way getESFInfo // works. bool SFE_UBLOX_GNSS::setAutoESFSTATUS(bool enable, uint16_t maxWait) { return setAutoESFSTATUSrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic ESF STATUS message generation by the GNSS. This changes the way getESFInfo // works. bool SFE_UBLOX_GNSS::setAutoESFSTATUS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoESFSTATUSrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic ESF STATUS message generation by the GNSS. This changes the way getESFInfo // works. bool SFE_UBLOX_GNSS::setAutoESFSTATUSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXESFSTATUS == NULL) initPacketUBXESFSTATUS(); // Check that RAM has been allocated for the data if (packetUBXESFSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_ESF; payloadCfg[1] = UBX_ESF_STATUS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXESFSTATUS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXESFSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFSTATUScallback(void (*callbackPointer)(UBX_ESF_STATUS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFSTATUS->callbackData = new UBX_ESF_STATUS_data_t; // Allocate RAM for the main struct } if (packetUBXESFSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFSTATUScallback: RAM alloc failed!")); #endif return (false); } packetUBXESFSTATUS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoESFSTATUScallbackPtr(void (*callbackPointerPtr)(UBX_ESF_STATUS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFSTATUS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFSTATUS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFSTATUS->callbackData = new UBX_ESF_STATUS_data_t; // Allocate RAM for the main struct } if (packetUBXESFSTATUS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFSTATUScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXESFSTATUS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ESF STATUS is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoESFSTATUS(bool enabled, bool implicitUpdate) { if (packetUBXESFSTATUS == NULL) initPacketUBXESFSTATUS(); // Check that RAM has been allocated for the ESF status data if (packetUBXESFSTATUS == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXESFSTATUS->automaticFlags.flags.bits.automatic != enabled || packetUBXESFSTATUS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXESFSTATUS->automaticFlags.flags.bits.automatic = enabled; packetUBXESFSTATUS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXESFSTATUS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXESFSTATUS() { packetUBXESFSTATUS = new UBX_ESF_STATUS_t; // Allocate RAM for the main struct if (packetUBXESFSTATUS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXESFSTATUS: RAM alloc failed!")); #endif return (false); } packetUBXESFSTATUS->automaticFlags.flags.all = 0; packetUBXESFSTATUS->callbackPointer = NULL; packetUBXESFSTATUS->callbackPointerPtr = NULL; packetUBXESFSTATUS->callbackData = NULL; packetUBXESFSTATUS->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushESFSTATUS() { if (packetUBXESFSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFSTATUS->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logESFSTATUS(bool enabled) { if (packetUBXESFSTATUS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFSTATUS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** ESF INS automatic support bool SFE_UBLOX_GNSS::getEsfIns(uint16_t maxWait) { return (getESFINS(maxWait)); } bool SFE_UBLOX_GNSS::getESFINS(uint16_t maxWait) { if (packetUBXESFINS == NULL) initPacketUBXESFINS(); // Check that RAM has been allocated for the ESF INS data if (packetUBXESFINS == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXESFINS->automaticFlags.flags.bits.automatic && packetUBXESFINS->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getEsfIns: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_ESF, UBX_ESF_INS); return packetUBXESFINS->moduleQueried.moduleQueried.bits.all; } else if (packetUBXESFINS->automaticFlags.flags.bits.automatic && !packetUBXESFINS->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getEsfIns: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfIns: Polling")); // } // The GPS is not automatically reporting HNR PVT so we have to poll explicitly packetCfg.cls = UBX_CLASS_ESF; packetCfg.id = UBX_ESF_INS; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getEsfIns: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getEsfIns retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic ESF INS message generation by the GNSS. This changes the way getESFIns // works. bool SFE_UBLOX_GNSS::setAutoESFINS(bool enable, uint16_t maxWait) { return setAutoESFINSrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic ESF INS message generation by the GNSS. This changes the way getESFIns // works. bool SFE_UBLOX_GNSS::setAutoESFINS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoESFINSrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic ESF INS message generation by the GNSS. This changes the way getESFIns // works. bool SFE_UBLOX_GNSS::setAutoESFINSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXESFINS == NULL) initPacketUBXESFINS(); // Check that RAM has been allocated for the data if (packetUBXESFINS == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_ESF; payloadCfg[1] = UBX_ESF_INS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXESFINS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXESFINS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXESFINS->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFINScallback(void (*callbackPointer)(UBX_ESF_INS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFINS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFINS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFINS->callbackData = new UBX_ESF_INS_data_t; // Allocate RAM for the main struct } if (packetUBXESFINS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFINScallback: RAM alloc failed!")); #endif return (false); } packetUBXESFINS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoESFINScallbackPtr(void (*callbackPointerPtr)(UBX_ESF_INS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFINS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFINS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFINS->callbackData = new UBX_ESF_INS_data_t; // Allocate RAM for the main struct } if (packetUBXESFINS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFINScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXESFINS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ESF INS is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoESFINS(bool enabled, bool implicitUpdate) { if (packetUBXESFINS == NULL) initPacketUBXESFINS(); // Check that RAM has been allocated for the ESF INS data if (packetUBXESFINS == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXESFINS->automaticFlags.flags.bits.automatic != enabled || packetUBXESFINS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXESFINS->automaticFlags.flags.bits.automatic = enabled; packetUBXESFINS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXESFINS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXESFINS() { packetUBXESFINS = new UBX_ESF_INS_t; // Allocate RAM for the main struct if (packetUBXESFINS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXESFINS: RAM alloc failed!")); #endif return (false); } packetUBXESFINS->automaticFlags.flags.all = 0; packetUBXESFINS->callbackPointer = NULL; packetUBXESFINS->callbackPointerPtr = NULL; packetUBXESFINS->callbackData = NULL; packetUBXESFINS->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushESFINS() { if (packetUBXESFINS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFINS->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logESFINS(bool enabled) { if (packetUBXESFINS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFINS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** ESF MEAS automatic support // Enable or disable automatic ESF MEAS message generation by the GNSS bool SFE_UBLOX_GNSS::setAutoESFMEAS(bool enable, uint16_t maxWait) { return setAutoESFMEASrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic ESF MEAS message generation by the GNSS bool SFE_UBLOX_GNSS::setAutoESFMEAS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoESFMEASrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic ESF MEAS message generation by the GNSS bool SFE_UBLOX_GNSS::setAutoESFMEASrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXESFMEAS == NULL) initPacketUBXESFMEAS(); // Check that RAM has been allocated for the data if (packetUBXESFMEAS == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_ESF; payloadCfg[1] = UBX_ESF_MEAS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXESFMEAS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXESFMEAS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFMEAScallback(void (*callbackPointer)(UBX_ESF_MEAS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFMEAS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFMEAS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFMEAS->callbackData = new UBX_ESF_MEAS_data_t; // Allocate RAM for the main struct } if (packetUBXESFMEAS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFMEAScallback: RAM alloc failed!")); #endif return (false); } packetUBXESFMEAS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoESFMEAScallbackPtr(void (*callbackPointerPtr)(UBX_ESF_MEAS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFMEAS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFMEAS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFMEAS->callbackData = new UBX_ESF_MEAS_data_t; // Allocate RAM for the main struct } if (packetUBXESFMEAS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFMEAScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXESFMEAS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ESF MEAS is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoESFMEAS(bool enabled, bool implicitUpdate) { if (packetUBXESFMEAS == NULL) initPacketUBXESFMEAS(); // Check that RAM has been allocated for the ESF MEAS data if (packetUBXESFMEAS == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXESFMEAS->automaticFlags.flags.bits.automatic != enabled || packetUBXESFMEAS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXESFMEAS->automaticFlags.flags.bits.automatic = enabled; packetUBXESFMEAS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXESFMEAS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXESFMEAS() { packetUBXESFMEAS = new UBX_ESF_MEAS_t; // Allocate RAM for the main struct if (packetUBXESFMEAS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXESFMEAS: RAM alloc failed!")); #endif return (false); } packetUBXESFMEAS->automaticFlags.flags.all = 0; packetUBXESFMEAS->callbackPointer = NULL; packetUBXESFMEAS->callbackPointerPtr = NULL; packetUBXESFMEAS->callbackData = NULL; return (true); } // Log this data in file buffer void SFE_UBLOX_GNSS::logESFMEAS(bool enabled) { if (packetUBXESFMEAS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFMEAS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** ESF RAW automatic support // ESF RAW messages are output only. They cannot be polled. // Enable or disable automatic ESF RAW message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFRAW(bool enable, uint16_t maxWait) { return setAutoESFRAWrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic ESF RAW message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFRAW(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoESFRAWrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic ESF RAW message generation by the GNSS. // Note: this function can only be used to enable or disable the messages. A rate of zero disables the messages. // A rate of 1 or more causes the messages to be generated at the full 100Hz. bool SFE_UBLOX_GNSS::setAutoESFRAWrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXESFRAW == NULL) initPacketUBXESFRAW(); // Check that RAM has been allocated for the data if (packetUBXESFRAW == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_ESF; payloadCfg[1] = UBX_ESF_RAW; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXESFRAW->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return ok; } // Enable automatic message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoESFRAWcallback(void (*callbackPointer)(UBX_ESF_RAW_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFRAW(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFRAW->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFRAW->callbackData = new UBX_ESF_RAW_data_t; // Allocate RAM for the main struct } if (packetUBXESFRAW->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFRAWcallback: RAM alloc failed!")); #endif return (false); } packetUBXESFRAW->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoESFRAWcallbackPtr(void (*callbackPointerPtr)(UBX_ESF_RAW_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoESFRAW(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXESFRAW->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXESFRAW->callbackData = new UBX_ESF_RAW_data_t; // Allocate RAM for the main struct } if (packetUBXESFRAW->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoESFRAWcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXESFRAW->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and ESF RAW is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoESFRAW(bool enabled, bool implicitUpdate) { if (packetUBXESFRAW == NULL) initPacketUBXESFRAW(); // Check that RAM has been allocated for the ESF RAW data if (packetUBXESFRAW == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXESFRAW->automaticFlags.flags.bits.automatic != enabled || packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXESFRAW->automaticFlags.flags.bits.automatic = enabled; packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXESFRAW and initialize it bool SFE_UBLOX_GNSS::initPacketUBXESFRAW() { packetUBXESFRAW = new UBX_ESF_RAW_t; // Allocate RAM for the main struct if (packetUBXESFRAW == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXESFRAW: RAM alloc failed!")); #endif return (false); } packetUBXESFRAW->automaticFlags.flags.all = 0; packetUBXESFRAW->callbackPointer = NULL; packetUBXESFRAW->callbackPointerPtr = NULL; packetUBXESFRAW->callbackData = NULL; return (true); } // Log this data in file buffer void SFE_UBLOX_GNSS::logESFRAW(bool enabled) { if (packetUBXESFRAW == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXESFRAW->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** HNR ATT automatic support bool SFE_UBLOX_GNSS::getHNRAtt(uint16_t maxWait) { return (getHNRATT(maxWait)); } // Get the HNR Attitude data // Returns true if the get HNR attitude is successful. Data is returned in hnrAtt // Note: if hnrAttQueried is true, it gets set to false by this function since we assume // that the user will read hnrAtt immediately after this. I.e. this function will // only return true _once_ after each auto HNR Att is processed bool SFE_UBLOX_GNSS::getHNRATT(uint16_t maxWait) { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the data if (packetUBXHNRATT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXHNRATT->automaticFlags.flags.bits.automatic && packetUBXHNRATT->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getHNRAtt: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_HNR, UBX_HNR_ATT); return packetUBXHNRATT->moduleQueried.moduleQueried.bits.all; } else if (packetUBXHNRATT->automaticFlags.flags.bits.automatic && !packetUBXHNRATT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getHNRAtt: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRAtt: Polling")); // } // The GPS is not automatically reporting HNR attitude so we have to poll explicitly packetCfg.cls = UBX_CLASS_HNR; packetCfg.id = UBX_HNR_ATT; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRAtt: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getHNRAtt retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic HNR attitude message generation by the GNSS. This changes the way getHNRAtt // works. bool SFE_UBLOX_GNSS::setAutoHNRATT(bool enable, uint16_t maxWait) { return setAutoHNRATTrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic HNR attitude message generation by the GNSS. This changes the way getHNRAtt // works. bool SFE_UBLOX_GNSS::setAutoHNRATT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoHNRATTrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic HNR attitude message generation by the GNSS. This changes the way getHNRAtt // works. bool SFE_UBLOX_GNSS::setAutoHNRATTrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the data if (packetUBXHNRATT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_HNR; payloadCfg[1] = UBX_HNR_ATT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXHNRATT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXHNRATT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXHNRATT->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoHNRATTcallback(void (*callbackPointer)(UBX_HNR_ATT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRATT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRATT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRATT->callbackData = new UBX_HNR_ATT_data_t; // Allocate RAM for the main struct } if (packetUBXHNRATT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRAttcallback: RAM alloc failed!")); #endif return (false); } packetUBXHNRATT->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoHNRATTcallbackPtr(void (*callbackPointerPtr)(UBX_HNR_ATT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRATT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRATT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRATT->callbackData = new UBX_HNR_ATT_data_t; // Allocate RAM for the main struct } if (packetUBXHNRATT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRAttcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXHNRATT->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and HNR attitude is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoHNRATT(bool enabled, bool implicitUpdate) { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the data if (packetUBXHNRATT == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXHNRATT->automaticFlags.flags.bits.automatic != enabled || packetUBXHNRATT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXHNRATT->automaticFlags.flags.bits.automatic = enabled; packetUBXHNRATT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXHNRATT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXHNRATT() { packetUBXHNRATT = new UBX_HNR_ATT_t; // Allocate RAM for the main struct if (packetUBXHNRATT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXHNRATT: RAM alloc failed!")); #endif return (false); } packetUBXHNRATT->automaticFlags.flags.all = 0; packetUBXHNRATT->callbackPointer = NULL; packetUBXHNRATT->callbackPointerPtr = NULL; packetUBXHNRATT->callbackData = NULL; packetUBXHNRATT->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushHNRATT() { if (packetUBXHNRATT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRATT->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logHNRATT(bool enabled) { if (packetUBXHNRATT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRATT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** HNR DYN automatic support bool SFE_UBLOX_GNSS::getHNRDyn(uint16_t maxWait) { return (getHNRINS(maxWait)); } // Get the HNR vehicle dynamics data // Returns true if the get HNR vehicle dynamics is successful. Data is returned in hnrVehDyn // Note: if hnrDynQueried is true, it gets set to false by this function since we assume // that the user will read hnrVehDyn immediately after this. I.e. this function will // only return true _once_ after each auto HNR Dyn is processed bool SFE_UBLOX_GNSS::getHNRINS(uint16_t maxWait) { if (packetUBXHNRINS == NULL) initPacketUBXHNRINS(); // Check that RAM has been allocated for the data if (packetUBXHNRINS == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXHNRINS->automaticFlags.flags.bits.automatic && packetUBXHNRINS->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getHNRINS: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_HNR, UBX_HNR_INS); return packetUBXHNRINS->moduleQueried.moduleQueried.bits.all; } else if (packetUBXHNRINS->automaticFlags.flags.bits.automatic && !packetUBXHNRINS->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getHNRINS: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRINS: Polling")); // } // The GPS is not automatically reporting HNR vehicle dynamics so we have to poll explicitly packetCfg.cls = UBX_CLASS_HNR; packetCfg.id = UBX_HNR_INS; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRINS: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getHNRINS retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic HNR vehicle dynamics message generation by the GNSS. This changes the way getHNRINS // works. bool SFE_UBLOX_GNSS::setAutoHNRINS(bool enable, uint16_t maxWait) { return setAutoHNRINSrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic HNR vehicle dynamics message generation by the GNSS. This changes the way getHNRINS // works. bool SFE_UBLOX_GNSS::setAutoHNRINS(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoHNRINSrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic HNR vehicle dynamics message generation by the GNSS. This changes the way getHNRINS // works. bool SFE_UBLOX_GNSS::setAutoHNRINSrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXHNRINS == NULL) initPacketUBXHNRINS(); // Check that RAM has been allocated for the data if (packetUBXHNRINS == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_HNR; payloadCfg[1] = UBX_HNR_INS; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXHNRINS->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXHNRINS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXHNRINS->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoHNRINScallback(void (*callbackPointer)(UBX_HNR_INS_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRINS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRINS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRINS->callbackData = new UBX_HNR_INS_data_t; // Allocate RAM for the main struct } if (packetUBXHNRINS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRINScallback: RAM alloc failed!")); #endif return (false); } packetUBXHNRINS->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoHNRINScallbackPtr(void (*callbackPointerPtr)(UBX_HNR_INS_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRINS(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRINS->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRINS->callbackData = new UBX_HNR_INS_data_t; // Allocate RAM for the main struct } if (packetUBXHNRINS->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRINScallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXHNRINS->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and HNR vehicle dynamics is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoHNRINS(bool enabled, bool implicitUpdate) { if (packetUBXHNRINS == NULL) initPacketUBXHNRINS(); // Check that RAM has been allocated for the data if (packetUBXHNRINS == NULL) // Bail if the RAM allocation failed return (false); bool changes = packetUBXHNRINS->automaticFlags.flags.bits.automatic != enabled || packetUBXHNRINS->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXHNRINS->automaticFlags.flags.bits.automatic = enabled; packetUBXHNRINS->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXHNRINS and initialize it bool SFE_UBLOX_GNSS::initPacketUBXHNRINS() { packetUBXHNRINS = new UBX_HNR_INS_t; // Allocate RAM for the main struct if (packetUBXHNRINS == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXHNRINS: RAM alloc failed!")); #endif return (false); } packetUBXHNRINS->automaticFlags.flags.all = 0; packetUBXHNRINS->callbackPointer = NULL; packetUBXHNRINS->callbackPointerPtr = NULL; packetUBXHNRINS->callbackData = NULL; packetUBXHNRINS->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushHNRINS() { if (packetUBXHNRINS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRINS->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logHNRINS(bool enabled) { if (packetUBXHNRINS == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRINS->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** HNR PVT automatic support // Get the HNR PVT data // Returns true if the get HNR PVT is successful. Data is returned in hnrPVT // Note: if hnrPVTQueried is true, it gets set to false by this function since we assume // that the user will read hnrPVT immediately after this. I.e. this function will // only return true _once_ after each auto HNR PVT is processed bool SFE_UBLOX_GNSS::getHNRPVT(uint16_t maxWait) { if (packetUBXHNRPVT == NULL) initPacketUBXHNRPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXHNRPVT == NULL) // Only attempt this if RAM allocation was successful return false; if (packetUBXHNRPVT->automaticFlags.flags.bits.automatic && packetUBXHNRPVT->automaticFlags.flags.bits.implicitUpdate) { // The GPS is automatically reporting, we just check whether we got unread data // if (_printDebug == true) // { // _debugSerial->println(F("getHNRPVT: Autoreporting")); // } checkUbloxInternal(&packetCfg, UBX_CLASS_HNR, UBX_HNR_PVT); return packetUBXHNRPVT->moduleQueried.moduleQueried.bits.all; } else if (packetUBXHNRPVT->automaticFlags.flags.bits.automatic && !packetUBXHNRPVT->automaticFlags.flags.bits.implicitUpdate) { // Someone else has to call checkUblox for us... // if (_printDebug == true) // { // _debugSerial->println(F("getHNRPVT: Exit immediately")); // } return (false); } else { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRPVT: Polling")); // } // The GPS is not automatically reporting HNR PVT so we have to poll explicitly packetCfg.cls = UBX_CLASS_HNR; packetCfg.id = UBX_HNR_PVT; 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) { // if (_printDebug == true) // { // _debugSerial->println(F("getHNRPVT: data in packetCfg was OVERWRITTEN by another message (but that's OK)")); // } return (true); } // if (_printDebug == true) // { // _debugSerial->print(F("getHNRPVT retVal: ")); // _debugSerial->println(statusString(retVal)); // } return (false); } return (false); // Trap. We should never get here... } // Enable or disable automatic HNR PVT message generation by the GNSS. This changes the way getHNRPVT // works. bool SFE_UBLOX_GNSS::setAutoHNRPVT(bool enable, uint16_t maxWait) { return setAutoHNRPVTrate(enable ? 1 : 0, true, maxWait); } // Enable or disable automatic HNR PVT message generation by the GNSS. This changes the way getHNRPVT // works. bool SFE_UBLOX_GNSS::setAutoHNRPVT(bool enable, bool implicitUpdate, uint16_t maxWait) { return setAutoHNRPVTrate(enable ? 1 : 0, implicitUpdate, maxWait); } // Enable or disable automatic HNR PVT message generation by the GNSS. This changes the way getHNRPVT // works. bool SFE_UBLOX_GNSS::setAutoHNRPVTrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait) { if (packetUBXHNRPVT == NULL) initPacketUBXHNRPVT(); // Check that RAM has been allocated for the data if (packetUBXHNRPVT == NULL) // Only attempt this if RAM allocation was successful return false; if (rate > 127) rate = 127; packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_MSG; packetCfg.len = 3; packetCfg.startingSpot = 0; payloadCfg[0] = UBX_CLASS_HNR; payloadCfg[1] = UBX_HNR_PVT; payloadCfg[2] = rate; // rate relative to navigation freq. bool ok = ((sendCommand(&packetCfg, maxWait)) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK if (ok) { packetUBXHNRPVT->automaticFlags.flags.bits.automatic = (rate > 0); packetUBXHNRPVT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } packetUBXHNRPVT->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale return ok; } // Enable automatic navigation message generation by the GNSS. bool SFE_UBLOX_GNSS::setAutoHNRPVTcallback(void (*callbackPointer)(UBX_HNR_PVT_data_t), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRPVT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRPVT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRPVT->callbackData = new UBX_HNR_PVT_data_t; // Allocate RAM for the main struct } if (packetUBXHNRPVT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRPVTcallback: RAM alloc failed!")); #endif return (false); } packetUBXHNRPVT->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setAutoHNRPVTcallbackPtr(void (*callbackPointerPtr)(UBX_HNR_PVT_data_t *), uint16_t maxWait) { // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. bool result = setAutoHNRPVT(true, false, maxWait); if (!result) return (result); // Bail if setAuto failed if (packetUBXHNRPVT->callbackData == NULL) // Check if RAM has been allocated for the callback copy { packetUBXHNRPVT->callbackData = new UBX_HNR_PVT_data_t; // Allocate RAM for the main struct } if (packetUBXHNRPVT->callbackData == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setAutoHNRPVTcallbackPtr: RAM alloc failed!")); #endif return (false); } packetUBXHNRPVT->callbackPointerPtr = callbackPointerPtr; return (true); } // In case no config access to the GNSS is possible and HNR PVT is send cyclically already // set config to suitable parameters bool SFE_UBLOX_GNSS::assumeAutoHNRPVT(bool enabled, bool implicitUpdate) { if (packetUBXHNRPVT == NULL) initPacketUBXHNRPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXHNRPVT == NULL) // Only attempt this if RAM allocation was successful return false; bool changes = packetUBXHNRPVT->automaticFlags.flags.bits.automatic != enabled || packetUBXHNRPVT->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; if (changes) { packetUBXHNRPVT->automaticFlags.flags.bits.automatic = enabled; packetUBXHNRPVT->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; } return changes; } // PRIVATE: Allocate RAM for packetUBXHNRPVT and initialize it bool SFE_UBLOX_GNSS::initPacketUBXHNRPVT() { packetUBXHNRPVT = new UBX_HNR_PVT_t; // Allocate RAM for the main struct if (packetUBXHNRPVT == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initPacketUBXHNRPVT: RAM alloc failed!")); #endif return (false); } packetUBXHNRPVT->automaticFlags.flags.all = 0; packetUBXHNRPVT->callbackPointer = NULL; packetUBXHNRPVT->callbackPointerPtr = NULL; packetUBXHNRPVT->callbackData = NULL; packetUBXHNRPVT->moduleQueried.moduleQueried.all = 0; return (true); } // Mark all the data as read/stale void SFE_UBLOX_GNSS::flushHNRPVT() { if (packetUBXHNRPVT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRPVT->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before) } // Log this data in file buffer void SFE_UBLOX_GNSS::logHNRPVT(bool enabled) { if (packetUBXHNRPVT == NULL) return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) packetUBXHNRPVT->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled; } // ***** Helper Functions for NMEA Logging / Processing // Set the mainTalkerId used by NMEA messages - allows all NMEA messages except GSV to be prefixed with GP instead of GN bool SFE_UBLOX_GNSS::setMainTalkerID(sfe_ublox_talker_ids_e id, uint16_t maxWait) { // Get the current extended NMEA protocol configuration (V1) packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NMEA; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); payloadCfg[9] = (uint8_t)id; packetCfg.len = 20; packetCfg.startingSpot = 0; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // Enable/Disable NMEA High Precision Mode - include extra decimal places in the Lat and Lon bool SFE_UBLOX_GNSS::setHighPrecisionMode(bool enable, uint16_t maxWait) { // Get the current extended NMEA protocol configuration (V1) packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_NMEA; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); if (enable) { payloadCfg[3] |= (1 << 3); // Set the highPrec flag payloadCfg[3] &= ~((1 << 0) | (1 << 2)); // Clear the compat and limit82 flags } else payloadCfg[3] &= ~(1 << 3); // Clear the highPrec flag packetCfg.len = 20; packetCfg.startingSpot = 0; return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK } // 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() { 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); } #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA // Initiate automatic storage of NMEA GPGGA messages // Get the most recent GPGGA message // Return 0 if the message has not been received from the module // Return 1 if the data is valid but has been read before // Return 2 if the data is valid and is fresh/unread uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPGGA(NMEA_GGA_data_t *data) { if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); // Check that RAM has been allocated for the message if (storageNMEAGPGGA == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGPGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGPGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } // Enable a callback on the arrival of a GPGGA message bool SFE_UBLOX_GNSS::setNMEAGPGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)) { if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); // Check that RAM has been allocated for the message if (storageNMEAGPGGA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPGGA->callbackCopy = new NMEA_GGA_data_t; } if (storageNMEAGPGGA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPGGAcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGPGGA->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGPGGAcallbackPtr(void (*callbackPointerPtr)(NMEA_GGA_data_t *)) { if (storageNMEAGPGGA == NULL) initStorageNMEAGPGGA(); // Check that RAM has been allocated for the message if (storageNMEAGPGGA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPGGA->callbackCopy = new NMEA_GGA_data_t; } if (storageNMEAGPGGA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPGGAcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGPGGA->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GPGGA messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGPGGA() { storageNMEAGPGGA = new NMEA_GPGGA_t; // Allocate RAM for the main struct if (storageNMEAGPGGA == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGPGGA: RAM alloc failed!")); #endif return (false); } storageNMEAGPGGA->workingCopy.length = 0; // Clear the data length memset(storageNMEAGPGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage storageNMEAGPGGA->completeCopy.length = 0; // Clear the data length memset(storageNMEAGPGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage storageNMEAGPGGA->callbackPointer = NULL; // Clear the callback pointers storageNMEAGPGGA->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGPGGA->callbackCopy = NULL; storageNMEAGPGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNGGA(NMEA_GGA_data_t *data) { if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); // Check that RAM has been allocated for the message if (storageNMEAGNGGA == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGNGGA->completeCopy, sizeof(NMEA_GGA_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGNGGA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } bool SFE_UBLOX_GNSS::setNMEAGNGGAcallback(void (*callbackPointer)(NMEA_GGA_data_t)) { if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); // Check that RAM has been allocated for the message if (storageNMEAGNGGA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNGGA->callbackCopy = new NMEA_GGA_data_t; } if (storageNMEAGNGGA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNGGAcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGNGGA->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGNGGAcallbackPtr(void (*callbackPointerPtr)(NMEA_GGA_data_t *)) { if (storageNMEAGNGGA == NULL) initStorageNMEAGNGGA(); // Check that RAM has been allocated for the message if (storageNMEAGNGGA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNGGA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNGGA->callbackCopy = new NMEA_GGA_data_t; } if (storageNMEAGNGGA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNGGAcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGNGGA->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GNGGA messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGNGGA() { storageNMEAGNGGA = new NMEA_GNGGA_t; // Allocate RAM for the main struct if (storageNMEAGNGGA == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGNGGA: RAM alloc failed!")); #endif return (false); } storageNMEAGNGGA->workingCopy.length = 0; // Clear the data length memset(storageNMEAGNGGA->workingCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage storageNMEAGNGGA->completeCopy.length = 0; // Clear the data length memset(storageNMEAGNGGA->completeCopy.nmea, 0, NMEA_GGA_MAX_LENGTH); // Clear the nmea storage storageNMEAGNGGA->callbackPointer = NULL; // Clear the callback pointers storageNMEAGNGGA->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGNGGA->callbackCopy = NULL; storageNMEAGNGGA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } // Initiate automatic storage of NMEA GPVTG messages // Get the most recent GPVTG message // Return 0 if the message has not been received from the module // Return 1 if the data is valid but has been read before // Return 2 if the data is valid and is fresh/unread uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPVTG(NMEA_VTG_data_t *data) { if (storageNMEAGPVTG == NULL) initStorageNMEAGPVTG(); // Check that RAM has been allocated for the message if (storageNMEAGPVTG == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGPVTG->completeCopy, sizeof(NMEA_VTG_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGPVTG->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGPVTG->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGPVTG->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } // Enable a callback on the arrival of a GPVTG message bool SFE_UBLOX_GNSS::setNMEAGPVTGcallback(void (*callbackPointer)(NMEA_VTG_data_t)) { if (storageNMEAGPVTG == NULL) initStorageNMEAGPVTG(); // Check that RAM has been allocated for the message if (storageNMEAGPVTG == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPVTG->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPVTG->callbackCopy = new NMEA_VTG_data_t; } if (storageNMEAGPVTG->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPVTGcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGPVTG->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGPVTGcallbackPtr(void (*callbackPointerPtr)(NMEA_VTG_data_t *)) { if (storageNMEAGPVTG == NULL) initStorageNMEAGPVTG(); // Check that RAM has been allocated for the message if (storageNMEAGPVTG == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPVTG->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPVTG->callbackCopy = new NMEA_VTG_data_t; } if (storageNMEAGPVTG->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPVTGcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGPVTG->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GPVTG messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGPVTG() { storageNMEAGPVTG = new NMEA_GPVTG_t; // Allocate RAM for the main struct if (storageNMEAGPVTG == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGPVTG: RAM alloc failed!")); #endif return (false); } storageNMEAGPVTG->workingCopy.length = 0; // Clear the data length memset(storageNMEAGPVTG->workingCopy.nmea, 0, NMEA_VTG_MAX_LENGTH); // Clear the nmea storage storageNMEAGPVTG->completeCopy.length = 0; // Clear the data length memset(storageNMEAGPVTG->completeCopy.nmea, 0, NMEA_VTG_MAX_LENGTH); // Clear the nmea storage storageNMEAGPVTG->callbackPointer = NULL; // Clear the callback pointers storageNMEAGPVTG->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGPVTG->callbackCopy = NULL; storageNMEAGPVTG->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNVTG(NMEA_VTG_data_t *data) { if (storageNMEAGNVTG == NULL) initStorageNMEAGNVTG(); // Check that RAM has been allocated for the message if (storageNMEAGNVTG == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGNVTG->completeCopy, sizeof(NMEA_VTG_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGNVTG->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGNVTG->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGNVTG->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } bool SFE_UBLOX_GNSS::setNMEAGNVTGcallback(void (*callbackPointer)(NMEA_VTG_data_t)) { if (storageNMEAGNVTG == NULL) initStorageNMEAGNVTG(); // Check that RAM has been allocated for the message if (storageNMEAGNVTG == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNVTG->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNVTG->callbackCopy = new NMEA_VTG_data_t; } if (storageNMEAGNVTG->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNVTGcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGNVTG->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGNVTGcallbackPtr(void (*callbackPointerPtr)(NMEA_VTG_data_t *)) { if (storageNMEAGNVTG == NULL) initStorageNMEAGNVTG(); // Check that RAM has been allocated for the message if (storageNMEAGNVTG == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNVTG->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNVTG->callbackCopy = new NMEA_VTG_data_t; } if (storageNMEAGNVTG->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNVTGcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGNVTG->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GNVTG messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGNVTG() { storageNMEAGNVTG = new NMEA_GNVTG_t; // Allocate RAM for the main struct if (storageNMEAGNVTG == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGNVTG: RAM alloc failed!")); #endif return (false); } storageNMEAGNVTG->workingCopy.length = 0; // Clear the data length memset(storageNMEAGNVTG->workingCopy.nmea, 0, NMEA_VTG_MAX_LENGTH); // Clear the nmea storage storageNMEAGNVTG->completeCopy.length = 0; // Clear the data length memset(storageNMEAGNVTG->completeCopy.nmea, 0, NMEA_VTG_MAX_LENGTH); // Clear the nmea storage storageNMEAGNVTG->callbackPointer = NULL; // Clear the callback pointers storageNMEAGNVTG->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGNVTG->callbackCopy = NULL; storageNMEAGNVTG->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } // Initiate automatic storage of NMEA GPRMC messages // Get the most recent GPRMC message // Return 0 if the message has not been received from the module // Return 1 if the data is valid but has been read before // Return 2 if the data is valid and is fresh/unread uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPRMC(NMEA_RMC_data_t *data) { if (storageNMEAGPRMC == NULL) initStorageNMEAGPRMC(); // Check that RAM has been allocated for the message if (storageNMEAGPRMC == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGPRMC->completeCopy, sizeof(NMEA_RMC_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGPRMC->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGPRMC->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGPRMC->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } // Enable a callback on the arrival of a GPRMC message bool SFE_UBLOX_GNSS::setNMEAGPRMCcallback(void (*callbackPointer)(NMEA_RMC_data_t)) { if (storageNMEAGPRMC == NULL) initStorageNMEAGPRMC(); // Check that RAM has been allocated for the message if (storageNMEAGPRMC == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPRMC->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPRMC->callbackCopy = new NMEA_RMC_data_t; } if (storageNMEAGPRMC->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPRMCcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGPRMC->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGPRMCcallbackPtr(void (*callbackPointerPtr)(NMEA_RMC_data_t *)) { if (storageNMEAGPRMC == NULL) initStorageNMEAGPRMC(); // Check that RAM has been allocated for the message if (storageNMEAGPRMC == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPRMC->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPRMC->callbackCopy = new NMEA_RMC_data_t; } if (storageNMEAGPRMC->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPRMCcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGPRMC->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GPRMC messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGPRMC() { storageNMEAGPRMC = new NMEA_GPRMC_t; // Allocate RAM for the main struct if (storageNMEAGPRMC == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGPRMC: RAM alloc failed!")); #endif return (false); } storageNMEAGPRMC->workingCopy.length = 0; // Clear the data length memset(storageNMEAGPRMC->workingCopy.nmea, 0, NMEA_RMC_MAX_LENGTH); // Clear the nmea storage storageNMEAGPRMC->completeCopy.length = 0; // Clear the data length memset(storageNMEAGPRMC->completeCopy.nmea, 0, NMEA_RMC_MAX_LENGTH); // Clear the nmea storage storageNMEAGPRMC->callbackPointer = NULL; // Clear the callback pointers storageNMEAGPRMC->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGPRMC->callbackCopy = NULL; storageNMEAGPRMC->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNRMC(NMEA_RMC_data_t *data) { if (storageNMEAGNRMC == NULL) initStorageNMEAGNRMC(); // Check that RAM has been allocated for the message if (storageNMEAGNRMC == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGNRMC->completeCopy, sizeof(NMEA_RMC_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGNRMC->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGNRMC->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGNRMC->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } bool SFE_UBLOX_GNSS::setNMEAGNRMCcallback(void (*callbackPointer)(NMEA_RMC_data_t)) { if (storageNMEAGNRMC == NULL) initStorageNMEAGNRMC(); // Check that RAM has been allocated for the message if (storageNMEAGNRMC == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNRMC->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNRMC->callbackCopy = new NMEA_RMC_data_t; } if (storageNMEAGNRMC->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNRMCcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGNRMC->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGNRMCcallbackPtr(void (*callbackPointerPtr)(NMEA_RMC_data_t *)) { if (storageNMEAGNRMC == NULL) initStorageNMEAGNRMC(); // Check that RAM has been allocated for the message if (storageNMEAGNRMC == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNRMC->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNRMC->callbackCopy = new NMEA_RMC_data_t; } if (storageNMEAGNRMC->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNRMCcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGNRMC->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GNRMC messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGNRMC() { storageNMEAGNRMC = new NMEA_GNRMC_t; // Allocate RAM for the main struct if (storageNMEAGNRMC == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGNRMC: RAM alloc failed!")); #endif return (false); } storageNMEAGNRMC->workingCopy.length = 0; // Clear the data length memset(storageNMEAGNRMC->workingCopy.nmea, 0, NMEA_RMC_MAX_LENGTH); // Clear the nmea storage storageNMEAGNRMC->completeCopy.length = 0; // Clear the data length memset(storageNMEAGNRMC->completeCopy.nmea, 0, NMEA_RMC_MAX_LENGTH); // Clear the nmea storage storageNMEAGNRMC->callbackPointer = NULL; // Clear the callback pointers storageNMEAGNRMC->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGNRMC->callbackCopy = NULL; storageNMEAGNRMC->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } // Initiate automatic storage of NMEA GPZDA messages // Get the most recent GPZDA message // Return 0 if the message has not been received from the module // Return 1 if the data is valid but has been read before // Return 2 if the data is valid and is fresh/unread uint8_t SFE_UBLOX_GNSS::getLatestNMEAGPZDA(NMEA_ZDA_data_t *data) { if (storageNMEAGPZDA == NULL) initStorageNMEAGPZDA(); // Check that RAM has been allocated for the message if (storageNMEAGPZDA == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGPZDA->completeCopy, sizeof(NMEA_ZDA_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGPZDA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGPZDA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGPZDA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } // Enable a callback on the arrival of a GPZDA message bool SFE_UBLOX_GNSS::setNMEAGPZDAcallback(void (*callbackPointer)(NMEA_ZDA_data_t)) { if (storageNMEAGPZDA == NULL) initStorageNMEAGPZDA(); // Check that RAM has been allocated for the message if (storageNMEAGPZDA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPZDA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPZDA->callbackCopy = new NMEA_ZDA_data_t; } if (storageNMEAGPZDA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPZDAcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGPZDA->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGPZDAcallbackPtr(void (*callbackPointerPtr)(NMEA_ZDA_data_t *)) { if (storageNMEAGPZDA == NULL) initStorageNMEAGPZDA(); // Check that RAM has been allocated for the message if (storageNMEAGPZDA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGPZDA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGPZDA->callbackCopy = new NMEA_ZDA_data_t; } if (storageNMEAGPZDA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGPZDAcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGPZDA->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GPZDA messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGPZDA() { storageNMEAGPZDA = new NMEA_GPZDA_t; // Allocate RAM for the main struct if (storageNMEAGPZDA == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGPZDA: RAM alloc failed!")); #endif return (false); } storageNMEAGPZDA->workingCopy.length = 0; // Clear the data length memset(storageNMEAGPZDA->workingCopy.nmea, 0, NMEA_ZDA_MAX_LENGTH); // Clear the nmea storage storageNMEAGPZDA->completeCopy.length = 0; // Clear the data length memset(storageNMEAGPZDA->completeCopy.nmea, 0, NMEA_ZDA_MAX_LENGTH); // Clear the nmea storage storageNMEAGPZDA->callbackPointer = NULL; // Clear the callback pointers storageNMEAGPZDA->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGPZDA->callbackCopy = NULL; storageNMEAGPZDA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } uint8_t SFE_UBLOX_GNSS::getLatestNMEAGNZDA(NMEA_ZDA_data_t *data) { if (storageNMEAGNZDA == NULL) initStorageNMEAGNZDA(); // Check that RAM has been allocated for the message if (storageNMEAGNZDA == NULL) // Bail if the RAM allocation failed return (false); checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Use a fake UBX class and ID. memcpy(data, &storageNMEAGNZDA->completeCopy, sizeof(NMEA_ZDA_data_t)); // Copy the complete copy uint8_t result = 0; if (storageNMEAGNZDA->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? { result = 1; if (storageNMEAGNZDA->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? { result = 2; storageNMEAGNZDA->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read } } return (result); } bool SFE_UBLOX_GNSS::setNMEAGNZDAcallback(void (*callbackPointer)(NMEA_ZDA_data_t)) { if (storageNMEAGNZDA == NULL) initStorageNMEAGNZDA(); // Check that RAM has been allocated for the message if (storageNMEAGNZDA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNZDA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNZDA->callbackCopy = new NMEA_ZDA_data_t; } if (storageNMEAGNZDA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNZDAcallback: RAM alloc failed!")); #endif return (false); } storageNMEAGNZDA->callbackPointer = callbackPointer; return (true); } bool SFE_UBLOX_GNSS::setNMEAGNZDAcallbackPtr(void (*callbackPointerPtr)(NMEA_ZDA_data_t *)) { if (storageNMEAGNZDA == NULL) initStorageNMEAGNZDA(); // Check that RAM has been allocated for the message if (storageNMEAGNZDA == NULL) // Bail if the RAM allocation failed return (false); if (storageNMEAGNZDA->callbackCopy == NULL) // Check if RAM has been allocated for the callback copy { storageNMEAGNZDA->callbackCopy = new NMEA_ZDA_data_t; } if (storageNMEAGNZDA->callbackCopy == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("setNMEAGNZDAcallbackPtr: RAM alloc failed!")); #endif return (false); } storageNMEAGNZDA->callbackPointerPtr = callbackPointerPtr; return (true); } // Private: allocate RAM for incoming NMEA GNZDA messages and initialize it bool SFE_UBLOX_GNSS::initStorageNMEAGNZDA() { storageNMEAGNZDA = new NMEA_GNZDA_t; // Allocate RAM for the main struct if (storageNMEAGNZDA == NULL) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("initStorageNMEAGNZDA: RAM alloc failed!")); #endif return (false); } storageNMEAGNZDA->workingCopy.length = 0; // Clear the data length memset(storageNMEAGNZDA->workingCopy.nmea, 0, NMEA_ZDA_MAX_LENGTH); // Clear the nmea storage storageNMEAGNZDA->completeCopy.length = 0; // Clear the data length memset(storageNMEAGNZDA->completeCopy.nmea, 0, NMEA_ZDA_MAX_LENGTH); // Clear the nmea storage storageNMEAGNZDA->callbackPointer = NULL; // Clear the callback pointers storageNMEAGNZDA->callbackPointerPtr = NULL; // Clear the callback pointers storageNMEAGNZDA->callbackCopy = NULL; storageNMEAGNZDA->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread return (true); } #endif // ***** CFG RATE Helper Functions // Set the rate at which the module will give us an updated navigation solution // Expects a number that is the updates per second. For example 1 = 1Hz, 2 = 2Hz, etc. // Max is 40Hz(?!) bool SFE_UBLOX_GNSS::setNavigationFrequency(uint8_t navFreq, uint16_t maxWait) { if (navFreq == 0) // Return now if navFreq is zero return (false); if (navFreq > 40) navFreq = 40; // Limit navFreq to 40Hz so i2cPollingWait is set correctly // Adjust the I2C polling timeout based on update rate // Do this even if the sendCommand fails i2cPollingWaitNAV = 1000 / (((int)navFreq) * 4); // This is the number of ms to wait between checks for new I2C data. Max is 250. Min is 6. i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR // Query the module packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RATE; packetCfg.len = 0; packetCfg.startingSpot = 0; // This will load the payloadCfg array with current settings of the given register if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // If command send fails then bail uint16_t measurementRate = 1000 / navFreq; // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[0] = measurementRate & 0xFF; // measRate LSB payloadCfg[1] = measurementRate >> 8; // measRate MSB bool 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 uint8_t SFE_UBLOX_GNSS::getNavigationFrequency(uint16_t maxWait) { if (packetUBXCFGRATE == NULL) initPacketUBXCFGRATE(); // Check that RAM has been allocated for the RATE data if (packetUBXCFGRATE == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXCFGRATE->moduleQueried.moduleQueried.bits.measRate == false) getNavigationFrequencyInternal(maxWait); packetUBXCFGRATE->moduleQueried.moduleQueried.bits.measRate = false; // Since we are about to give this to user, mark this data as stale packetUBXCFGRATE->moduleQueried.moduleQueried.bits.all = false; uint16_t measurementRate = packetUBXCFGRATE->data.measRate; if (measurementRate == 0) { #ifndef SFE_UBLOX_REDUCED_PROG_MEM if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging _debugSerial->println(F("getNavigationFrequency: zero measRate!")); #endif return (0); // Avoid divide-by-zero error } measurementRate = 1000 / measurementRate; // This may return an int when it's a float, but I'd rather not return 4 bytes return (measurementRate); } // Set the elapsed time between GNSS measurements in milliseconds, which defines the rate bool SFE_UBLOX_GNSS::setMeasurementRate(uint16_t rate, uint16_t maxWait) { if (rate < 25) // "Measurement rate should be greater than or equal to 25 ms." rate = 25; // Adjust the I2C polling timeout based on update rate if (rate >= 1000) i2cPollingWaitNAV = 250; else i2cPollingWaitNAV = rate / 4; // This is the number of ms to wait between checks for new I2C data i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR // Query the module packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RATE; packetCfg.len = 0; packetCfg.startingSpot = 0; // This will load the payloadCfg array with current settings of the given register if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // If command send fails then bail // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[0] = rate & 0xFF; // measRate LSB payloadCfg[1] = rate >> 8; // measRate MSB bool 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 uint16_t SFE_UBLOX_GNSS::getMeasurementRate(uint16_t maxWait) { if (packetUBXCFGRATE == NULL) initPacketUBXCFGRATE(); // Check that RAM has been allocated for the RATE data if (packetUBXCFGRATE == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXCFGRATE->moduleQueried.moduleQueried.bits.measRate == false) getNavigationFrequencyInternal(maxWait); packetUBXCFGRATE->moduleQueried.moduleQueried.bits.measRate = false; // Since we are about to give this to user, mark this data as stale packetUBXCFGRATE->moduleQueried.moduleQueried.bits.all = false; return (packetUBXCFGRATE->data.measRate); } // Set the ratio between the number of measurements and the number of navigation solutions. Unit is cycles. Max is 127. bool SFE_UBLOX_GNSS::setNavigationRate(uint16_t rate, uint16_t maxWait) { // Query the module packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_RATE; packetCfg.len = 0; packetCfg.startingSpot = 0; // This will load the payloadCfg array with current settings of the given register if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) // We are expecting data and an ACK return (false); // If command send fails then bail // payloadCfg is now loaded with current bytes. Change only the ones we need to payloadCfg[2] = rate & 0xFF; // navRate LSB payloadCfg[3] = rate >> 8; // navRate MSB bool 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 uint16_t SFE_UBLOX_GNSS::getNavigationRate(uint16_t maxWait) { if (packetUBXCFGRATE == NULL) initPacketUBXCFGRATE(); // Check that RAM has been allocated for the RATE data if (packetUBXCFGRATE == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXCFGRATE->moduleQueried.moduleQueried.bits.navRate == false) getNavigationFrequencyInternal(maxWait); packetUBXCFGRATE->moduleQueried.moduleQueried.bits.navRate = false; // Since we are about to give this to user, mark this data as stale packetUBXCFGRATE->moduleQueried.moduleQueried.bits.all = false; 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) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.gDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.gDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.gDOP); } uint16_t SFE_UBLOX_GNSS::getPositionDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.pDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.pDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.pDOP); } uint16_t SFE_UBLOX_GNSS::getTimeDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.tDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.tDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.tDOP); } uint16_t SFE_UBLOX_GNSS::getVerticalDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.vDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.vDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.vDOP); } uint16_t SFE_UBLOX_GNSS::getHorizontalDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.hDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.hDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.hDOP); } uint16_t SFE_UBLOX_GNSS::getNorthingDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.nDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.nDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.nDOP); } uint16_t SFE_UBLOX_GNSS::getEastingDOP(uint16_t maxWait) { if (packetUBXNAVDOP == NULL) initPacketUBXNAVDOP(); // Check that RAM has been allocated for the DOP data if (packetUBXNAVDOP == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVDOP->moduleQueried.moduleQueried.bits.eDOP == false) getDOP(maxWait); packetUBXNAVDOP->moduleQueried.moduleQueried.bits.eDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVDOP->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVDOP->data.eDOP); } // ***** ATT Helper Functions float SFE_UBLOX_GNSS::getATTroll(uint16_t maxWait) // Returned as degrees { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data if (packetUBXNAVATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXNAVATT->moduleQueried.moduleQueried.bits.roll == false) getNAVATT(maxWait); packetUBXNAVATT->moduleQueried.moduleQueried.bits.roll = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVATT->data.roll) / 100000.0); // Convert to degrees } float SFE_UBLOX_GNSS::getATTpitch(uint16_t maxWait) // Returned as degrees { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data if (packetUBXNAVATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXNAVATT->moduleQueried.moduleQueried.bits.pitch == false) getNAVATT(maxWait); packetUBXNAVATT->moduleQueried.moduleQueried.bits.pitch = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVATT->data.pitch) / 100000.0); // Convert to degrees } float SFE_UBLOX_GNSS::getATTheading(uint16_t maxWait) // Returned as degrees { if (packetUBXNAVATT == NULL) initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data if (packetUBXNAVATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXNAVATT->moduleQueried.moduleQueried.bits.heading == false) getNAVATT(maxWait); packetUBXNAVATT->moduleQueried.moduleQueried.bits.heading = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVATT->data.heading) / 100000.0); // Convert to degrees } // ***** PVT Helper Functions uint32_t SFE_UBLOX_GNSS::getTimeOfWeek(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.iTOW == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.iTOW = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.iTOW); } // Get the current year uint16_t SFE_UBLOX_GNSS::getYear(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.year == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.year = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.year); } // Get the current month uint8_t SFE_UBLOX_GNSS::getMonth(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.month == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.month = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.month); } // Get the current day uint8_t SFE_UBLOX_GNSS::getDay(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.day == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.day = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.day); } // Get the current hour uint8_t SFE_UBLOX_GNSS::getHour(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hour == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hour = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.hour); } // Get the current minute uint8_t SFE_UBLOX_GNSS::getMinute(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.min == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.min = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.min); } // Get the current second uint8_t SFE_UBLOX_GNSS::getSecond(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.sec == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.sec = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.sec); } // Get the current millisecond uint16_t SFE_UBLOX_GNSS::getMillisecond(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.iTOW == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.iTOW = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.iTOW % 1000); } // Get the current nanoseconds - includes milliseconds int32_t SFE_UBLOX_GNSS::getNanosecond(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.nano == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.nano = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.nano); } // Get the current Unix epoch time rounded to the nearest second uint32_t SFE_UBLOX_GNSS::getUnixEpoch(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.sec == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.year = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.month = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.day = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hour = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.min = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.sec = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; 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[packetUBXNAVPVT->data.year - 2020]; // Add on the number of days since 2020 t += (uint32_t)SFE_UBLOX_DAYS_SINCE_MONTH[packetUBXNAVPVT->data.year % 4 == 0 ? 0 : 1][packetUBXNAVPVT->data.month - 1]; // Add on the number of days since Jan 1st t += (uint32_t)packetUBXNAVPVT->data.day - 1; // Add on the number of days since the 1st of the month t *= 24; // Convert to hours t += (uint32_t)packetUBXNAVPVT->data.hour; // Add on the hour t *= 60; // Convert to minutes t += (uint32_t)packetUBXNAVPVT->data.min; // Add on the minute t *= 60; // Convert to seconds t += (uint32_t)packetUBXNAVPVT->data.sec; // Add on the second return t; } // Get the current Unix epoch including microseconds uint32_t SFE_UBLOX_GNSS::getUnixEpoch(uint32_t µsecond, uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.nano == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.year = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.month = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.day = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hour = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.min = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.sec = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.nano = false; packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; 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[packetUBXNAVPVT->data.year - 2020]; // Add on the number of days since 2020 t += (uint32_t)SFE_UBLOX_DAYS_SINCE_MONTH[packetUBXNAVPVT->data.year % 4 == 0 ? 0 : 1][packetUBXNAVPVT->data.month - 1]; // Add on the number of days since Jan 1st t += (uint32_t)packetUBXNAVPVT->data.day - 1; // Add on the number of days since the 1st of the month t *= 24; // Convert to hours t += (uint32_t)packetUBXNAVPVT->data.hour; // Add on the hour t *= 60; // Convert to minutes t += (uint32_t)packetUBXNAVPVT->data.min; // Add on the minute t *= 60; // Convert to seconds t += (uint32_t)packetUBXNAVPVT->data.sec; // Add on the second int32_t us = packetUBXNAVPVT->data.nano / 1000; // Convert nanos to micros microsecond = (uint32_t)us; // Could be -ve! // Adjust t if nano is negative if (us < 0) { microsecond = (uint32_t)(us + 1000000); // Make nano +ve t--; // Decrement t by 1 second } return t; } // Get the current date validity bool SFE_UBLOX_GNSS::getDateValid(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.validDate == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.validDate = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.valid.bits.validDate); } // Get the current time validity bool SFE_UBLOX_GNSS::getTimeValid(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.validTime == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.validTime = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.valid.bits.validTime); } // Check to see if the UTC time has been fully resolved bool SFE_UBLOX_GNSS::getTimeFullyResolved(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fullyResolved == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fullyResolved = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.valid.bits.fullyResolved); } // Get the confirmed date validity bool SFE_UBLOX_GNSS::getConfirmedDate(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.confirmedDate == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.confirmedDate = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.flags2.bits.confirmedDate); } // Get the confirmed time validity bool SFE_UBLOX_GNSS::getConfirmedTime(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.confirmedTime == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.confirmedTime = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.flags2.bits.confirmedTime); } // Get the current fix type // 0=no fix, 1=dead reckoning, 2=2D, 3=3D, 4=GNSS, 5=Time fix uint8_t SFE_UBLOX_GNSS::getFixType(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fixType == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.fixType = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.fixType); } // Get whether we have a valid fix (i.e within DOP & accuracy masks) bool SFE_UBLOX_GNSS::getGnssFixOk(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.gnssFixOK == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.gnssFixOK = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.flags.bits.gnssFixOK); } // Get whether differential corrections were applied bool SFE_UBLOX_GNSS::getDiffSoln(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.diffSoln == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.diffSoln = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.flags.bits.diffSoln); } // Get power save mode from NAV-PVT bool SFE_UBLOX_GNSS::getNAVPVTPSMMode(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.psmState== false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.psmState= false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.flags.bits.psmState); } // Get whether head vehicle valid or not bool SFE_UBLOX_GNSS::getHeadVehValid(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.headVehValid == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.headVehValid = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.flags.bits.headVehValid); } // Get the carrier phase range solution status // Useful when querying module to see if it has high-precision RTK fix // 0=No solution, 1=Float solution, 2=Fixed solution uint8_t SFE_UBLOX_GNSS::getCarrierSolutionType(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.carrSoln == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.carrSoln = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.flags.bits.carrSoln); } // Get the number of satellites used in fix uint8_t SFE_UBLOX_GNSS::getSIV(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.numSV == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.numSV = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.numSV); } // Get the current longitude in degrees // Returns a long representing the number of degrees *10^-7 int32_t SFE_UBLOX_GNSS::getLongitude(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.lon == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.lon = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.lon); } // Get the current latitude in degrees // Returns a long representing the number of degrees *10^-7 int32_t SFE_UBLOX_GNSS::getLatitude(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.lat == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.lat = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.lat); } // Get the current altitude in mm according to ellipsoid model int32_t SFE_UBLOX_GNSS::getAltitude(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.height == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.height = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.height); } // Get the current altitude in mm according to mean sea level // Ellipsoid model: https://www.esri.com/news/arcuser/0703/geoid1of3.html // Difference between Ellipsoid Model and Mean Sea Level: https://eos-gnss.com/elevation-for-beginners/ // Also see: https://portal.u-blox.com/s/question/0D52p00008HKDSkCAP/what-geoid-model-is-used-and-where-is-this-calculated // and: https://cddis.nasa.gov/926/egm96/egm96.html on 10x10 degree grid int32_t SFE_UBLOX_GNSS::getAltitudeMSL(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hMSL == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hMSL = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.hMSL); } int32_t SFE_UBLOX_GNSS::getHorizontalAccEst(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hAcc == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.hAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.hAcc); } int32_t SFE_UBLOX_GNSS::getVerticalAccEst(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.vAcc == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.vAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.vAcc); } int32_t SFE_UBLOX_GNSS::getNedNorthVel(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.velN == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.velN = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.velN); } int32_t SFE_UBLOX_GNSS::getNedEastVel(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.velE == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.velE = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.velE); } int32_t SFE_UBLOX_GNSS::getNedDownVel(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.velD == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.velD = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.velD); } // Get the ground speed in mm/s int32_t SFE_UBLOX_GNSS::getGroundSpeed(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.gSpeed == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.gSpeed = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.gSpeed); } // Get the heading of motion (as opposed to heading of car) in degrees * 10^-5 int32_t SFE_UBLOX_GNSS::getHeading(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headMot == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headMot = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.headMot); } uint32_t SFE_UBLOX_GNSS::getSpeedAccEst(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.sAcc == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.sAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.sAcc); } uint32_t SFE_UBLOX_GNSS::getHeadingAccEst(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headAcc == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.headAcc); } // Get the positional dillution of precision * 10^-2 (dimensionless) uint16_t SFE_UBLOX_GNSS::getPDOP(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.pDOP == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.pDOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.pDOP); } bool SFE_UBLOX_GNSS::getInvalidLlh(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return (false); if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.invalidLlh == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.invalidLlh = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return ((bool)packetUBXNAVPVT->data.flags3.bits.invalidLlh); } int32_t SFE_UBLOX_GNSS::getHeadVeh(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headVeh == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.headVeh = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.headVeh); } int16_t SFE_UBLOX_GNSS::getMagDec(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.magDec == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.magDec = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.magDec); } uint16_t SFE_UBLOX_GNSS::getMagAcc(uint16_t maxWait) { if (packetUBXNAVPVT == NULL) initPacketUBXNAVPVT(); // Check that RAM has been allocated for the PVT data if (packetUBXNAVPVT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.magAcc == false) getPVT(maxWait); packetUBXNAVPVT->moduleQueried.moduleQueried2.bits.magAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVT->data.magAcc); } // getGeoidSeparation is currently redundant. The geoid separation seems to only be provided in NMEA GGA and GNS messages. int32_t SFE_UBLOX_GNSS::getGeoidSeparation(uint16_t maxWait) { (void)maxWait; // Do something with maxWait just to get rid of the pesky compiler warning return (0); } // ***** HPPOSECEF Helper Functions // Get the current 3D high precision positional accuracy - a fun thing to watch // Returns a long representing the 3D accuracy in millimeters uint32_t SFE_UBLOX_GNSS::getPositionAccuracy(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.pAcc == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.pAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; uint32_t tempAccuracy = packetUBXNAVHPPOSECEF->data.pAcc; if ((tempAccuracy % 10) >= 5) tempAccuracy += 5; // Round fraction of mm up to next mm if .5 or above tempAccuracy /= 10; // Convert 0.1mm units to mm return (tempAccuracy); } // Get the current 3D high precision X coordinate // Returns a long representing the coordinate in cm int32_t SFE_UBLOX_GNSS::getHighResECEFX(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefX == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefX = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefX); } // Get the current 3D high precision Y coordinate // Returns a long representing the coordinate in cm int32_t SFE_UBLOX_GNSS::getHighResECEFY(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefY == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefY = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefY); } // Get the current 3D high precision Z coordinate // Returns a long representing the coordinate in cm int32_t SFE_UBLOX_GNSS::getHighResECEFZ(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZ == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZ = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefZ); } // Get the high precision component of the ECEF X coordinate // Returns a signed byte representing the component as 0.1*mm int8_t SFE_UBLOX_GNSS::getHighResECEFXHp(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefXHp == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefXHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefXHp); } // Get the high precision component of the ECEF Y coordinate // Returns a signed byte representing the component as 0.1*mm int8_t SFE_UBLOX_GNSS::getHighResECEFYHp(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefYHp == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefYHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefYHp); } // Get the high precision component of the ECEF Z coordinate // Returns a signed byte representing the component as 0.1*mm int8_t SFE_UBLOX_GNSS::getHighResECEFZHp(uint16_t maxWait) { if (packetUBXNAVHPPOSECEF == NULL) initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZHp == false) getNAVHPPOSECEF(maxWait); packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSECEF->data.ecefZHp); } // ***** HPPOSLLH Helper Functions uint32_t SFE_UBLOX_GNSS::getTimeOfWeekFromHPPOSLLH(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.iTOW == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.iTOW = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.iTOW); } int32_t SFE_UBLOX_GNSS::getHighResLongitude(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lon == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lon = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.lon); } int32_t SFE_UBLOX_GNSS::getHighResLatitude(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lat == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lat = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.lat); } int32_t SFE_UBLOX_GNSS::getElipsoid(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.height == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.height = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.height); } int32_t SFE_UBLOX_GNSS::getMeanSeaLevel(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hMSL == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hMSL = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.hMSL); } int8_t SFE_UBLOX_GNSS::getHighResLongitudeHp(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lonHp == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.lonHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.lonHp); } int8_t SFE_UBLOX_GNSS::getHighResLatitudeHp(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.latHp == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.latHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.latHp); } int8_t SFE_UBLOX_GNSS::getElipsoidHp(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.heightHp == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.heightHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.heightHp); } int8_t SFE_UBLOX_GNSS::getMeanSeaLevelHp(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hMSLHp == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hMSLHp = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.hMSLHp); } uint32_t SFE_UBLOX_GNSS::getHorizontalAccuracy(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hAcc == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.hAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.hAcc); } uint32_t SFE_UBLOX_GNSS::getVerticalAccuracy(uint16_t maxWait) { if (packetUBXNAVHPPOSLLH == NULL) initPacketUBXNAVHPPOSLLH(); // Check that RAM has been allocated for the HPPOSLLH data if (packetUBXNAVHPPOSLLH == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.vAcc == false) getHPPOSLLH(maxWait); packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.vAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVHPPOSLLH->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVHPPOSLLH->data.vAcc); } // ***** PVAT Helper Functions int32_t SFE_UBLOX_GNSS::getVehicleRoll(uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehRoll == false) getNAVPVAT(maxWait); packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehRoll = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVAT->data.vehRoll); } int32_t SFE_UBLOX_GNSS::getVehiclePitch(uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehPitch == false) getNAVPVAT(maxWait); packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehPitch = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVAT->data.vehPitch); } int32_t SFE_UBLOX_GNSS::getVehicleHeading(uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehHeading == false) getNAVPVAT(maxWait); packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.vehHeading = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVAT->data.vehHeading); } int32_t SFE_UBLOX_GNSS::getMotionHeading(uint16_t maxWait) { if (packetUBXNAVPVAT == NULL) initPacketUBXNAVPVAT(); // Check that RAM has been allocated for the PVAT data if (packetUBXNAVPVAT == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.motHeading == false) getNAVPVAT(maxWait); packetUBXNAVPVAT->moduleQueried.moduleQueried2.bits.motHeading = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVPVAT->moduleQueried.moduleQueried1.bits.all = false; return (packetUBXNAVPVAT->data.motHeading); } // ***** SVIN Helper Functions bool SFE_UBLOX_GNSS::getSurveyInActive(uint16_t maxWait) { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Bail if the RAM allocation failed return false; if (packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.active == false) getSurveyStatus(maxWait); packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.active = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all = false; return ((bool)packetUBXNAVSVIN->data.active); } bool SFE_UBLOX_GNSS::getSurveyInValid(uint16_t maxWait) { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Bail if the RAM allocation failed return false; if (packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.valid == false) getSurveyStatus(maxWait); packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.valid = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all = false; return ((bool)packetUBXNAVSVIN->data.valid); } uint32_t SFE_UBLOX_GNSS::getSurveyInObservationTimeFull(uint16_t maxWait) // Return the full uint32_t { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.dur == false) getSurveyStatus(maxWait); packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.dur = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVSVIN->data.dur); } uint16_t SFE_UBLOX_GNSS::getSurveyInObservationTime(uint16_t maxWait) // Truncated to 65535 seconds { // dur (Passed survey-in observation time) is U4 (uint32_t) seconds. Here we truncate to 16 bits uint32_t tmpObsTime = getSurveyInObservationTimeFull(maxWait); if (tmpObsTime <= 0xFFFF) { return ((uint16_t)tmpObsTime); } else { return (0xFFFF); } } float SFE_UBLOX_GNSS::getSurveyInMeanAccuracy(uint16_t maxWait) // Returned as m { if (packetUBXNAVSVIN == NULL) initPacketUBXNAVSVIN(); // Check that RAM has been allocated for the SVIN data if (packetUBXNAVSVIN == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.meanAcc == false) getSurveyStatus(maxWait); packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.meanAcc = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVSVIN->moduleQueried.moduleQueried.bits.all = false; // meanAcc is U4 (uint32_t) in 0.1mm. We convert this to float. uint32_t tempFloat = packetUBXNAVSVIN->data.meanAcc; 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.validTimeToLsEvent == false) getLeapSecondEvent(maxWait); 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; 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 ((bool)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) { 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.validCurrLs == false) getLeapSecondEvent(maxWait); 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; 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 { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosN == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosN = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.relPosN) / 100.0); // Convert to m } float SFE_UBLOX_GNSS::getRelPosE(uint16_t maxWait) // Returned as m { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosE == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosE = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.relPosE) / 100.0); // Convert to m } float SFE_UBLOX_GNSS::getRelPosD(uint16_t maxWait) // Returned as m { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosD == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.relPosD = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.relPosD) / 100.0); // Convert to m } float SFE_UBLOX_GNSS::getRelPosAccN(uint16_t maxWait) // Returned as m { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accN == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accN = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.accN) / 10000.0); // Convert to m } float SFE_UBLOX_GNSS::getRelPosAccE(uint16_t maxWait) // Returned as m { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accE == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accE = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.accE) / 10000.0); // Convert to m } float SFE_UBLOX_GNSS::getRelPosAccD(uint16_t maxWait) // Returned as m { if (packetUBXNAVRELPOSNED == NULL) initPacketUBXNAVRELPOSNED(); // Check that RAM has been allocated for the RELPOSNED data if (packetUBXNAVRELPOSNED == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accD == false) getRELPOSNED(maxWait); packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.accD = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVRELPOSNED->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXNAVRELPOSNED->data.accD) / 10000.0); // Convert to m } // ***** AOPSTATUS Helper Functions uint8_t SFE_UBLOX_GNSS::getAOPSTATUSuseAOP(uint16_t maxWait) { if (packetUBXNAVAOPSTATUS == NULL) initPacketUBXNAVAOPSTATUS(); // Check that RAM has been allocated for the AOPSTATUS data if (packetUBXNAVAOPSTATUS == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.useAOP == false) getAOPSTATUS(maxWait); packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.useAOP = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVAOPSTATUS->data.aopCfg.bits.useAOP); } uint8_t SFE_UBLOX_GNSS::getAOPSTATUSstatus(uint16_t maxWait) { if (packetUBXNAVAOPSTATUS == NULL) initPacketUBXNAVAOPSTATUS(); // Check that RAM has been allocated for the AOPSTATUS data if (packetUBXNAVAOPSTATUS == NULL) // Bail if the RAM allocation failed return 0; if (packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.status == false) getAOPSTATUS(maxWait); packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.status = false; // Since we are about to give this to user, mark this data as stale packetUBXNAVAOPSTATUS->moduleQueried.moduleQueried.bits.all = false; return (packetUBXNAVAOPSTATUS->data.status); } // ***** ESF Helper Functions float SFE_UBLOX_GNSS::getESFroll(uint16_t maxWait) // Returned as degrees { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the ESF ALG data if (packetUBXESFALG == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXESFALG->moduleQueried.moduleQueried.bits.roll == false) getESFALG(maxWait); packetUBXESFALG->moduleQueried.moduleQueried.bits.roll = false; // Since we are about to give this to user, mark this data as stale packetUBXESFALG->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXESFALG->data.roll) / 100.0); // Convert to degrees } float SFE_UBLOX_GNSS::getESFpitch(uint16_t maxWait) // Returned as degrees { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the ESF ALG data if (packetUBXESFALG == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXESFALG->moduleQueried.moduleQueried.bits.pitch == false) getESFALG(maxWait); packetUBXESFALG->moduleQueried.moduleQueried.bits.pitch = false; // Since we are about to give this to user, mark this data as stale packetUBXESFALG->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXESFALG->data.pitch) / 100.0); // Convert to degrees } float SFE_UBLOX_GNSS::getESFyaw(uint16_t maxWait) // Returned as degrees { if (packetUBXESFALG == NULL) initPacketUBXESFALG(); // Check that RAM has been allocated for the ESF ALG data if (packetUBXESFALG == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXESFALG->moduleQueried.moduleQueried.bits.yaw == false) getESFALG(maxWait); packetUBXESFALG->moduleQueried.moduleQueried.bits.yaw = false; // Since we are about to give this to user, mark this data as stale packetUBXESFALG->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXESFALG->data.yaw) / 100.0); // Convert to degrees } bool SFE_UBLOX_GNSS::getSensorFusionMeasurement(UBX_ESF_MEAS_sensorData_t *sensorData, UBX_ESF_MEAS_data_t ubxDataStruct, uint8_t sensor) { sensorData->data.all = ubxDataStruct.data[sensor].data.all; return (true); } bool SFE_UBLOX_GNSS::getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorData, UBX_ESF_RAW_data_t ubxDataStruct, uint8_t sensor) { sensorData->data.all = ubxDataStruct.data[sensor].data.all; sensorData->sTag = ubxDataStruct.data[sensor].sTag; return (true); } bool SFE_UBLOX_GNSS::getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensorStatus, uint8_t sensor, uint16_t maxWait) { if (packetUBXESFSTATUS == NULL) initPacketUBXESFSTATUS(); // Check that RAM has been allocated for the ESF STATUS data if (packetUBXESFSTATUS == NULL) // Bail if the RAM allocation failed return (false); if ((packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.status & (1 << sensor)) == 0) getESFSTATUS(maxWait); packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.status &= ~(1 << sensor); // Since we are about to give this to user, mark this data as stale packetUBXESFSTATUS->moduleQueried.moduleQueried.bits.all = false; sensorStatus->sensStatus1.all = packetUBXESFSTATUS->data.status[sensor].sensStatus1.all; sensorStatus->sensStatus2.all = packetUBXESFSTATUS->data.status[sensor].sensStatus2.all; sensorStatus->freq = packetUBXESFSTATUS->data.status[sensor].freq; sensorStatus->faults.all = packetUBXESFSTATUS->data.status[sensor].faults.all; return (true); } bool SFE_UBLOX_GNSS::getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensorStatus, UBX_ESF_STATUS_data_t ubxDataStruct, uint8_t sensor) { sensorStatus->sensStatus1.all = ubxDataStruct.status[sensor].sensStatus1.all; sensorStatus->sensStatus2.all = ubxDataStruct.status[sensor].sensStatus2.all; sensorStatus->freq = ubxDataStruct.status[sensor].freq; sensorStatus->faults.all = ubxDataStruct.status[sensor].faults.all; return (true); } // ***** HNR Helper Functions // Set the High Navigation Rate // Returns true if the setHNRNavigationRate is successful bool SFE_UBLOX_GNSS::setHNRNavigationRate(uint8_t rate, uint16_t maxWait) { if (rate == 0) // Return now if rate is zero return (false); if (rate > 40) rate = 40; // Limit rate to 40Hz so i2cPollingWait is set correctly // Adjust the I2C polling timeout based on update rate // Do this even if the sendCommand is not ACK'd i2cPollingWaitHNR = 1000 / (((int)rate) * 4); // This is the number of ms to wait between checks for new I2C data. Max 250. Min 6. i2cPollingWait = i2cPollingWaitNAV < i2cPollingWaitHNR ? i2cPollingWaitNAV : i2cPollingWaitHNR; // Set i2cPollingWait to the lower of NAV and HNR packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_HNR; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current HNR settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (false); // Load the new navigation rate into payloadCfg payloadCfg[0] = rate; // Update the navigation rate sfe_ublox_status_e result = sendCommand(&packetCfg, maxWait); // We are only expecting an ACK return (result == SFE_UBLOX_STATUS_DATA_SENT); } // Get the High Navigation Rate // Returns 0 if the getHNRNavigationRate fails uint8_t SFE_UBLOX_GNSS::getHNRNavigationRate(uint16_t maxWait) { packetCfg.cls = UBX_CLASS_CFG; packetCfg.id = UBX_CFG_HNR; packetCfg.len = 0; packetCfg.startingSpot = 0; // Ask module for the current HNR settings. Loads into payloadCfg. if (sendCommand(&packetCfg, maxWait) != SFE_UBLOX_STATUS_DATA_RECEIVED) return (0); // Return the navigation rate return (payloadCfg[0]); } float SFE_UBLOX_GNSS::getHNRroll(uint16_t maxWait) // Returned as degrees { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the HNR ATT data if (packetUBXHNRATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXHNRATT->moduleQueried.moduleQueried.bits.roll == false) getHNRATT(maxWait); packetUBXHNRATT->moduleQueried.moduleQueried.bits.roll = false; // Since we are about to give this to user, mark this data as stale packetUBXHNRATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXHNRATT->data.roll) / 100000.0); // Convert to degrees } float SFE_UBLOX_GNSS::getHNRpitch(uint16_t maxWait) // Returned as degrees { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the HNR ATT data if (packetUBXHNRATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXHNRATT->moduleQueried.moduleQueried.bits.pitch == false) getHNRATT(maxWait); packetUBXHNRATT->moduleQueried.moduleQueried.bits.pitch = false; // Since we are about to give this to user, mark this data as stale packetUBXHNRATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXHNRATT->data.pitch) / 100000.0); // Convert to degrees } float SFE_UBLOX_GNSS::getHNRheading(uint16_t maxWait) // Returned as degrees { if (packetUBXHNRATT == NULL) initPacketUBXHNRATT(); // Check that RAM has been allocated for the HNR ATT data if (packetUBXHNRATT == NULL) // Bail if the RAM allocation failed return (0); if (packetUBXHNRATT->moduleQueried.moduleQueried.bits.heading == false) getHNRATT(maxWait); packetUBXHNRATT->moduleQueried.moduleQueried.bits.heading = false; // Since we are about to give this to user, mark this data as stale packetUBXHNRATT->moduleQueried.moduleQueried.bits.all = false; return (((float)packetUBXHNRATT->data.heading) / 100000.0); // Convert to degrees } // Functions to extract signed and unsigned 8/16/32-bit data from a ubxPacket // From v2.0: These are public. The user can call these to extract data from custom packets // Given a spot in the payload array, extract eight bytes and build a uint64_t uint64_t SFE_UBLOX_GNSS::extractLongLong(ubxPacket *msg, uint16_t spotToStart) { uint64_t val = 0; val |= (uint64_t)msg->payload[spotToStart + 0] << 8 * 0; val |= (uint64_t)msg->payload[spotToStart + 1] << 8 * 1; val |= (uint64_t)msg->payload[spotToStart + 2] << 8 * 2; val |= (uint64_t)msg->payload[spotToStart + 3] << 8 * 3; val |= (uint64_t)msg->payload[spotToStart + 4] << 8 * 4; val |= (uint64_t)msg->payload[spotToStart + 5] << 8 * 5; val |= (uint64_t)msg->payload[spotToStart + 6] << 8 * 6; val |= (uint64_t)msg->payload[spotToStart + 7] << 8 * 7; return (val); } // Given a spot in the payload array, extract four bytes and build a long uint32_t SFE_UBLOX_GNSS::extractLong(ubxPacket *msg, uint16_t spotToStart) { uint32_t val = 0; val |= (uint32_t)msg->payload[spotToStart + 0] << 8 * 0; val |= (uint32_t)msg->payload[spotToStart + 1] << 8 * 1; val |= (uint32_t)msg->payload[spotToStart + 2] << 8 * 2; val |= (uint32_t)msg->payload[spotToStart + 3] << 8 * 3; return (val); } // Just so there is no ambiguity about whether a uint32_t will cast to a int32_t correctly... int32_t SFE_UBLOX_GNSS::extractSignedLong(ubxPacket *msg, uint16_t spotToStart) { union // Use a union to convert from uint32_t to int32_t { uint32_t unsignedLong; int32_t signedLong; } unsignedSigned; unsignedSigned.unsignedLong = extractLong(msg, spotToStart); return (unsignedSigned.signedLong); } // Given a spot in the payload array, extract two bytes and build an int uint16_t SFE_UBLOX_GNSS::extractInt(ubxPacket *msg, uint16_t spotToStart) { uint16_t val = 0; val |= (uint16_t)msg->payload[spotToStart + 0] << 8 * 0; val |= (uint16_t)msg->payload[spotToStart + 1] << 8 * 1; return (val); } // Just so there is no ambiguity about whether a uint16_t will cast to a int16_t correctly... int16_t SFE_UBLOX_GNSS::extractSignedInt(ubxPacket *msg, uint16_t spotToStart) { union // Use a union to convert from uint16_t to int16_t { uint16_t unsignedInt; int16_t signedInt; } stSignedInt; stSignedInt.unsignedInt = extractInt(msg, spotToStart); return (stSignedInt.signedInt); } // Given a spot, extract a byte from the payload uint8_t SFE_UBLOX_GNSS::extractByte(ubxPacket *msg, uint16_t spotToStart) { return (msg->payload[spotToStart]); } // Given a spot, extract a signed 8-bit value from the payload int8_t SFE_UBLOX_GNSS::extractSignedChar(ubxPacket *msg, uint16_t spotToStart) { union // Use a union to convert from uint8_t to int8_t { uint8_t unsignedByte; int8_t signedByte; } stSignedByte; stSignedByte.unsignedByte = extractByte(msg, spotToStart); return (stSignedByte.signedByte); }