diff --git a/examples/Callbacks/CallbackExample9_SEC_SIG/CallbackExample9_SEC_SIG.ino b/examples/Callbacks/CallbackExample9_SEC_SIG/CallbackExample9_SEC_SIG.ino new file mode 100644 index 0000000..5463ae9 --- /dev/null +++ b/examples/Callbacks/CallbackExample9_SEC_SIG/CallbackExample9_SEC_SIG.ino @@ -0,0 +1,192 @@ +/* + Configuring the GNSS to automatically send SEC SIG reports over I2C and display them using a callback + By: Paul Clark + SparkFun Electronics + Date: April 3rd, 2025 + License: MIT. See license file for more information. + + This example shows how to configure the u-blox GNSS to send SEC SIG reports automatically + and access the data via a callback. No more polling! + + Feel like supporting open source hardware? + Buy a board from SparkFun! + ZED-F9P RTK2: https://www.sparkfun.com/products/15136 + + Hardware Connections: + Plug a Qwiic cable into the GPS and a BlackBoard + If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425) + Open the serial monitor at 115200 baud to see the output +*/ + +#include //Needed for I2C to GPS + +#include //http://librarymanager/All#SparkFun_u-blox_GNSS_v3 +SFE_UBLOX_GNSS myGNSS; + +// Callback: newSECSIG will be called when new SEC SIG data arrives +// See u-blox_structs.h for the full definition of UBX_SEC_SIG_data_t +// _____ You can use any name you like for the callback. Use the same name when you call setAutoSECSIGcallback +// / _____ This _must_ be UBX_SEC_SIG_data_t +// | / _____ You can use any name you like for the struct +// | | / +// | | | +void newSECSIG(UBX_SEC_SIG_data_t *ubxDataStruct) +{ + Serial.println(); + + Serial.print(F("New SEC SIG data received. It contains version ")); + Serial.print(ubxDataStruct->version); + Serial.println(F(" data.")); + + if (ubxDataStruct->version == 1) + { + Serial.print(F("Jamming detection is ")); + bool jamDetEnabled = (bool)ubxDataStruct->versions.version1.jamFlags.bits.jamDetEnabled; + Serial.println(jamDetEnabled ? "enabled" : "disabled"); + if (jamDetEnabled) + { + Serial.print(F("Jamming state: ")); + switch (ubxDataStruct->versions.version1.jamFlags.bits.jammingState) + { + case 1: + Serial.println(F("no jamming indicated")); + break; + case 2: + Serial.println(F("warning (jamming indicated but fix OK)")); + break; + case 3: + Serial.println(F("critical (jamming indicated and no fix)")); + break; + case 0: + default: + Serial.println(F("unknown")); + break; + } + } + Serial.print(F("Spoofing detection is ")); + bool spfDetEnabled = (bool)ubxDataStruct->versions.version1.spfFlags.bits.spfDetEnabled; + Serial.println(spfDetEnabled ? "enabled" : "disabled"); + if (spfDetEnabled) + { + Serial.print(F("Spoofing state: ")); + switch (ubxDataStruct->versions.version1.spfFlags.bits.spoofingState) + { + case 1: + Serial.println(F("no spoofing indicated")); + break; + case 2: + Serial.println(F("spoofing indicated")); + break; + case 3: + Serial.println(F("spoofing affirmed")); + break; + case 0: + default: + Serial.println(F("unknown")); + break; + } + } + } + + else if (ubxDataStruct->version == 2) + { + Serial.print(F("Jamming detection is ")); + bool jamDetEnabled = (bool)ubxDataStruct->versions.version2.sigSecFlags.bits.jamDetEnabled; + Serial.println(jamDetEnabled ? "enabled" : "disabled"); + if (jamDetEnabled) + { + Serial.print(F("Jamming state: ")); + switch (ubxDataStruct->versions.version2.sigSecFlags.bits.jamState) + { + case 1: + Serial.println(F("no jamming indicated")); + break; + case 2: + Serial.println(F("warning (jamming indicated)")); + break; + case 0: + default: + Serial.println(F("unknown")); + break; + } + } + Serial.print(F("Spoofing detection is ")); + bool spfDetEnabled = (bool)ubxDataStruct->versions.version2.sigSecFlags.bits.spfDetEnabled; + Serial.println(spfDetEnabled ? "enabled" : "disabled"); + if (spfDetEnabled) + { + Serial.print(F("Spoofing state: ")); + switch (ubxDataStruct->versions.version2.sigSecFlags.bits.spfState) + { + case 1: + Serial.println(F("no spoofing indicated")); + break; + case 2: + Serial.println(F("spoofing indicated")); + break; + case 3: + Serial.println(F("spoofing affirmed")); + break; + case 0: + default: + Serial.println(F("unknown")); + break; + } + } + Serial.print(F("Number of jamming center frequencies: ")); + uint8_t jamNumCentFreqs = ubxDataStruct->versions.version2.jamNumCentFreqs; + Serial.println(jamNumCentFreqs); + if (jamNumCentFreqs > 0) + { + for (uint8_t i = 0; i < jamNumCentFreqs; i++) + { + Serial.print(F("Center frequency: ")); + Serial.print(ubxDataStruct->versions.version2.jamStateCentFreq[i].bits.centFreq); + Serial.print(F(" kHz ")); + if (ubxDataStruct->versions.version2.jamStateCentFreq[i].bits.jammed) + Serial.print("- jammed"); + Serial.println(); + } + } + } +} + +void setup() +{ + Serial.begin(115200); + while (!Serial); //Wait for user to open terminal + Serial.println("SparkFun u-blox Example"); + + Wire.begin(); + + //myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial + //myGNSS.enableDebugging(Serial, true); // Uncomment this line to enable only the major debug messages on Serial + + while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port + { + Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring.")); + } + + myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only. Stop the NMEA messages + myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR + + // Just to prove we can, poll the SEC SIG data manually + Serial.println("Polling SEC SIG data:"); + UBX_SEC_SIG_data_t secSig; + if (myGNSS.getSECSIG(&secSig)) + newSECSIG(&secSig); // Call the callback manually to print the data + else + Serial.println("getSECSIG failed!"); + + // Now enable automatic (periodic) SEC SIG messages with callback to newSECSIG + myGNSS.setAutoSECSIGcallbackPtr(&newSECSIG); +} + +void loop() +{ + myGNSS.checkUblox(); // Check for the arrival of new data and process it. + myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. + + Serial.print("."); + delay(50); +} diff --git a/keys/u-blox-F9-HPS-1.30_InterfaceDescription_UBX-22010984_keys_sorted.txt b/keys/u-blox-F9-HPS-1.40_InterfaceDescription_UBXDOC-963802114-13138_keys_sorted.txt similarity index 96% rename from keys/u-blox-F9-HPS-1.30_InterfaceDescription_UBX-22010984_keys_sorted.txt rename to keys/u-blox-F9-HPS-1.40_InterfaceDescription_UBXDOC-963802114-13138_keys_sorted.txt index 4e4ead6..f99707c 100644 --- a/keys/u-blox-F9-HPS-1.30_InterfaceDescription_UBX-22010984_keys_sorted.txt +++ b/keys/u-blox-F9-HPS-1.40_InterfaceDescription_UBXDOC-963802114-13138_keys_sorted.txt @@ -22,6 +22,10 @@ 0x101100d7 0x10170001 0x10170002 +0x10220001 +0x10220002 +0x10220003 +0x10220004 0x10240012 0x10240020 0x10240030 @@ -120,10 +124,9 @@ 0x10a30033 0x10a30034 0x10a30035 -0x10c70001 -0x10c70002 0x10f60009 0x10f60051 +0x10f6005d 0x2005000c 0x20050023 0x20050030 @@ -134,6 +137,7 @@ 0x20060016 0x2006001e 0x2006001f +0x20060030 0x2007000b 0x20090009 0x20110011 @@ -150,6 +154,11 @@ 0x20140011 0x20210003 0x20210004 +0x20220005 +0x20220021 +0x20220022 +0x20220031 +0x20220032 0x20240011 0x20240013 0x20240014 @@ -262,6 +271,11 @@ 0x2091006c 0x2091006d 0x2091006e +0x2091007e +0x2091007f +0x20910080 +0x20910081 +0x20910082 0x20910083 0x20910084 0x20910085 @@ -347,6 +361,11 @@ 0x209100e4 0x209100e5 0x209100e6 +0x209100e7 +0x209100e8 +0x209100e9 +0x209100ea +0x209100eb 0x209100ec 0x209100ed 0x209100ee @@ -718,17 +737,25 @@ 0x20a30063 0x20a30064 0x20a70001 -0x20c70003 0x30050001 0x30060007 0x3006000a 0x3006000b 0x30060017 0x30060018 +0x30060020 +0x30060021 +0x30060022 0x3006002e 0x3006002f 0x3007000a 0x3007000e +0x30070012 +0x30070013 +0x30070014 +0x30080002 +0x30080003 +0x30080004 0x30090008 0x30110017 0x301100b1 @@ -738,13 +765,16 @@ 0x301100b5 0x30210001 0x30210002 +0x30250016 0x3025003b +0x30360008 0x30370008 0x3065000a 0x3065000b 0x3065000c 0x30930033 0x30a20004 +0x30a3003c 0x30f6000a 0x30f6000b 0x40050002 @@ -798,7 +828,3 @@ 0x50650016 0x50650017 0x50650018 -0x50c70004 -0x50c70005 -0x50c70006 -0x50c70007 diff --git a/keys/u-blox_config_keys_sorted.txt b/keys/u-blox_config_keys_sorted.txt index d7b0363..ed3f00b 100644 --- a/keys/u-blox_config_keys_sorted.txt +++ b/keys/u-blox_config_keys_sorted.txt @@ -193,6 +193,7 @@ 0x20060016 0x2006001e 0x2006001f +0x20060030 0x2007000b 0x20090009 0x20110011 @@ -954,10 +955,19 @@ 0x3006000b 0x30060017 0x30060018 +0x30060020 +0x30060021 +0x30060022 0x3006002e 0x3006002f 0x3007000a 0x3007000e +0x30070012 +0x30070013 +0x30070014 +0x30080002 +0x30080003 +0x30080004 0x30090001 0x30090008 0x30110017 @@ -970,6 +980,7 @@ 0x30210001 0x30210002 0x30230002 +0x30250016 0x3025003b 0x30360008 0x30370008 diff --git a/keywords.txt b/keywords.txt index 7edc7c2..b956b00 100644 --- a/keywords.txt +++ b/keywords.txt @@ -620,6 +620,14 @@ assumeAutoHNRPVT KEYWORD2 flushHNRPVT KEYWORD2 logHNRPVT KEYWORD2 +getSECSIG KEYWORD2 +setAutoSECSIG KEYWORD2 +setAutoSECSIGrate KEYWORD2 +setAutoSECSIGcallbackPtr KEYWORD2 +assumeAutoSECSIG KEYWORD2 +flushSECSIG KEYWORD2 +logSECSIG KEYWORD2 + setNavigationFrequency KEYWORD2 getNavigationFrequency KEYWORD2 setMeasurementRate KEYWORD2 diff --git a/library.properties b/library.properties index 5ee6b5b..e25ec14 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS v3 -version=3.1.8 +version=3.1.9 author=SparkFun Electronics maintainer=SparkFun Electronics sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules

diff --git a/src/u-blox_Class_and_ID.h b/src/u-blox_Class_and_ID.h index 37fd31e..6c9ac0c 100644 --- a/src/u-blox_Class_and_ID.h +++ b/src/u-blox_Class_and_ID.h @@ -290,6 +290,7 @@ const uint8_t UBX_RXM_SPARTNKEY = 0x36; // Poll/transfer dynamic SPARTN keys // Class: SEC // The following are used to configure the SEC UBX messages (security feature messages). Descriptions from UBX messages overview (ZED_F9P Interface Description Document page 36) +const uint8_t UBX_SEC_SIG = 0x09; // Signal security information const uint8_t UBX_SEC_UNIQID = 0x03; // Unique chip ID // Class: TIM @@ -402,10 +403,11 @@ enum dynModel // Possible values for the dynamic platform model, which provide m DYN_MODEL_AIRBORNE1g, // Airborne <1g acceleration. Used for applications with a higher dynamic range and greater vertical acceleration than a passenger car. No 2D position fixes supported. DYN_MODEL_AIRBORNE2g, // Airborne <2g acceleration. Recommended for typical airborne environments. No 2D position fixes supported. DYN_MODEL_AIRBORNE4g, // Airborne <4g acceleration. Only recommended for extremely dynamic environments. No 2D position fixes supported. - DYN_MODEL_WRIST, // Not supported in protocol versions less than 18. Only recommended for wrist worn applications. Receiver will filter out arm motion. - DYN_MODEL_BIKE, // Supported in protocol versions 19.2. (not available in all products) - DYN_MODEL_MOWER, // Added in HPS 1.21 (not available in all products) - DYN_MODEL_ESCOOTER, // Added in HPS 1.21 (not available in all products) + DYN_MODEL_WRIST, // Wrist-worn watch. Not supported in protocol versions less than 18. Only recommended for wrist worn applications. Receiver will filter out arm motion. + DYN_MODEL_BIKE, // Motorbike. Supported in protocol versions 19.2. (not available in all products) + DYN_MODEL_MOWER, // Robotic lawn mower. Added in HPS 1.21 (not available in all products) + DYN_MODEL_ESCOOTER, // E-scooter. Added in HPS 1.21 (not available in all products) + DYN_MODEL_RAIL, // Rail vehicles (trains, trams). Added in HPS 1.40 (not available in all products) DYN_MODEL_UNKNOWN = 255 // getDynamicModel will return 255 if sendCommand fails }; diff --git a/src/u-blox_GNSS.cpp b/src/u-blox_GNSS.cpp index 7cb2379..815f1ef 100644 --- a/src/u-blox_GNSS.cpp +++ b/src/u-blox_GNSS.cpp @@ -550,6 +550,16 @@ void DevUBLOXGNSS::end(void) } #endif + if (packetUBXSECSIG != nullptr) + { + if (packetUBXSECSIG->callbackData != nullptr) + { + delete packetUBXSECSIG->callbackData; + } + delete packetUBXSECSIG; + packetUBXSECSIG = nullptr; + } + #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if (storageNMEAGPGGA != nullptr) { @@ -1576,6 +1586,14 @@ bool DevUBLOXGNSS::autoLookup(uint8_t Class, uint8_t ID, uint16_t *maxSize) } #endif break; + case UBX_CLASS_SEC: + if (ID == UBX_SEC_SIG) + { + if (maxSize != nullptr) + *maxSize = UBX_SEC_SIG_LEN_VERSION1 > UBX_SEC_SIG_MAX_LEN_VERSION2 ? UBX_SEC_SIG_LEN_VERSION1 : UBX_SEC_SIG_MAX_LEN_VERSION2; + return (packetUBXSECSIG != nullptr); + } + break; default: return false; break; @@ -1592,6 +1610,7 @@ void DevUBLOXGNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t req // by other threads without overwriting the requested / expected Class and ID. volatile static uint8_t storedClass = 0; volatile static uint8_t storedID = 0; + static size_t payloadAutoBytes; if (requestedClass || requestedID) // If either is non-zero, store the requested Class and ID { storedClass = requestedClass; @@ -1695,22 +1714,30 @@ void DevUBLOXGNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t req } #endif } - if (payloadAuto != nullptr) // Check if memory is already allocated - this should be impossible! + + // Determine the payload length + if ((!logBecauseAuto) && (logBecauseEnabled)) + maxPayload = SFE_UBX_MAX_LENGTH; + + // Increase the payloadAuto buffer size if necessary, by removing + // the previous buffer + if (payloadAuto && (payloadAutoBytes < maxPayload)) { -#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[] + delete[] payloadAuto; // Created with new[] below payloadAuto = nullptr; - packetAuto.payload = payloadAuto; + payloadAutoBytes = 0; } - if ((!logBecauseAuto) && (logBecauseEnabled)) - maxPayload = SFE_UBX_MAX_LENGTH; - payloadAuto = new uint8_t[maxPayload]; // Allocate RAM for payloadAuto + + // Allocate the payloadAuto buffer if necessary + if (payloadAuto == nullptr) + { + payloadAuto = new uint8_t[maxPayload]; + if (payloadAuto) + payloadAutoBytes = maxPayload; + } + packetAuto.payload = payloadAuto; + if (payloadAuto == nullptr) // Check if the alloc failed { #ifndef SFE_UBLOX_REDUCED_PROG_MEM @@ -3268,14 +3295,10 @@ void DevUBLOXGNSS::processUBX(uint8_t incoming, ubxPacket *incomingUBX, uint8_t } } - // Now that the packet is complete and has been processed, we need to delete the memory - // allocated for packetAuto + // Now that the packet is complete and has been processed, 'free' the memory for packetAuto + // but leave payloadAuto allocated (See #75) if (activePacketBuffer == SFE_UBLOX_PACKET_PACKETAUTO) - { - delete[] payloadAuto; // Created with new[] - payloadAuto = nullptr; - packetAuto.payload = payloadAuto; - } + packetAuto.payload = nullptr; } else // Load this byte into the payload array { @@ -4883,6 +4906,64 @@ void DevUBLOXGNSS::processUBXpacket(ubxPacket *msg) } break; #endif + case UBX_CLASS_SEC: + if (msg->id == UBX_SEC_SIG) + { + // Parse various byte fields into storage - but only if we have memory allocated for it + if (packetUBXSECSIG != nullptr) + { + packetUBXSECSIG->data.version = extractByte(msg, 0); + + // Check the version + if ((packetUBXSECSIG->data.version == 1) && (msg->len == UBX_SEC_SIG_LEN_VERSION1)) + { + packetUBXSECSIG->data.versions.version1.jamFlags.all = extractByte(msg, 4); + packetUBXSECSIG->data.versions.version1.spfFlags.all = extractByte(msg, 8); + } + else if (packetUBXSECSIG->data.version == 2) + { + packetUBXSECSIG->data.versions.version2.sigSecFlags.all = extractByte(msg, 1); + packetUBXSECSIG->data.versions.version2.jamNumCentFreqs = extractByte(msg, 3); + if (packetUBXSECSIG->data.versions.version2.jamNumCentFreqs > UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2) + { + #ifndef SFE_UBLOX_REDUCED_PROG_MEM + if (_printDebug == true) + { + _debugSerial.print(F("UBX_SEC_SIG: truncating ")); + _debugSerial.print(packetUBXSECSIG->data.versions.version2.jamNumCentFreqs); + _debugSerial.print(F(" center frequencies to ")); + _debugSerial.println(UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2); + } + #endif + packetUBXSECSIG->data.versions.version2.jamNumCentFreqs = UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2; + } + uint16_t centFreq = 0; + while (centFreq < packetUBXSECSIG->data.versions.version2.jamNumCentFreqs) + { + packetUBXSECSIG->data.versions.version2.jamStateCentFreq[centFreq].all = extractLong(msg, 4 + (centFreq * 4)); + centFreq++; + } + } + + // Mark all datums as fresh (not read before) + packetUBXSECSIG->moduleQueried = true; + + // Check if we need to copy the data for the callback + if ((packetUBXSECSIG->callbackData != nullptr) // If RAM has been allocated for the copy of the data + && (packetUBXSECSIG->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale + { + memcpy(&packetUBXSECSIG->callbackData->version, &packetUBXSECSIG->data.version, sizeof(UBX_SEC_SIG_data_t)); + packetUBXSECSIG->automaticFlags.flags.bits.callbackCopyValid = true; + } + + // Check if we need to copy the data into the file buffer + if (packetUBXSECSIG->automaticFlags.flags.bits.addToFileBuffer) + { + addedToFileBuffer = storePacket(msg); + } + } + } + break; } // Check if this UBX message should be added to the file buffer - if it has not been added already @@ -6155,6 +6236,17 @@ void DevUBLOXGNSS::checkCallbacks(void) } #endif + if (packetUBXSECSIG != nullptr) // If RAM has been allocated for message storage + if (packetUBXSECSIG->callbackData != nullptr) // If RAM has been allocated for the copy of the data + if (packetUBXSECSIG->automaticFlags.flags.bits.callbackCopyValid == true) // If the copy of the data is valid + { + if (packetUBXSECSIG->callbackPointerPtr != nullptr) // If the pointer to the callback has been defined + { + packetUBXSECSIG->callbackPointerPtr(packetUBXSECSIG->callbackData); // Call the callback + } + packetUBXSECSIG->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } + #ifndef SFE_UBLOX_DISABLE_AUTO_NMEA if (storageNMEAGPGGA != nullptr) // If RAM has been allocated for message storage if (storageNMEAGPGGA->callbackCopy != nullptr) // If RAM has been allocated for the copy of the data @@ -16291,6 +16383,192 @@ void DevUBLOXGNSS::logHNRPVT(bool enabled) } #endif +// ***** SEC-SIG automatic support + +// Get the latest security and fill all global variables +bool DevUBLOXGNSS::getSECSIG(uint16_t maxWait) +{ + if (packetUBXSECSIG == nullptr) + initPacketUBXSECSIG(); // Check that RAM has been allocated for the UBX data + if (packetUBXSECSIG == nullptr) // Bail if the RAM allocation failed + return (false); + + if (packetUBXSECSIG->automaticFlags.flags.bits.automatic && packetUBXSECSIG->automaticFlags.flags.bits.implicitUpdate) + { + checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Don't overwrite the requested Class and ID + return packetUBXSECSIG->moduleQueried; + } + else if (packetUBXSECSIG->automaticFlags.flags.bits.automatic && !packetUBXSECSIG->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_SEC; + packetCfg.id = UBX_SEC_SIG; + 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); + } +} + +// Get the latest security information +bool DevUBLOXGNSS::getSECSIG(UBX_SEC_SIG_data_t *data, uint16_t maxWait) +{ + if (data == nullptr) // Check if the user forgot to include the data pointer + return (getSECSIG(maxWait)); + + if (!getSECSIG(maxWait)) + return (false); + + memcpy(data, &packetUBXSECSIG->data, sizeof(UBX_SEC_SIG_data_t)); + + packetUBXSECSIG->moduleQueried = false; // Mark the data as stale + + return (true); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getSECSIG +// works. +bool DevUBLOXGNSS::setAutoSECSIG(bool enable, uint8_t layer, uint16_t maxWait) +{ + return setAutoSECSIGrate(enable ? 1 : 0, true, layer, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getSECSIG +// works. +bool DevUBLOXGNSS::setAutoSECSIG(bool enable, bool implicitUpdate, uint8_t layer, uint16_t maxWait) +{ + return setAutoSECSIGrate(enable ? 1 : 0, implicitUpdate, layer, maxWait); +} + +// Enable or disable automatic navigation message generation by the GNSS. This changes the way getSECSIG +// works. +bool DevUBLOXGNSS::setAutoSECSIGrate(uint8_t rate, bool implicitUpdate, uint8_t layer, uint16_t maxWait) +{ + if (packetUBXSECSIG == nullptr) + initPacketUBXSECSIG(); // Check that RAM has been allocated for the data + if (packetUBXSECSIG == nullptr) // Only attempt this if RAM allocation was successful + return false; + + if (rate > 127) + rate = 127; + + uint32_t key = UBLOX_CFG_MSGOUT_UBX_SEC_SIG_I2C; + if (_commType == COMM_TYPE_SPI) + key = UBLOX_CFG_MSGOUT_UBX_SEC_SIG_SPI; + else if (_commType == COMM_TYPE_SERIAL) + { + if (!_UART2) + key = UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART1; + else + key = UBLOX_CFG_MSGOUT_UBX_SEC_SIG_UART2; + } + + bool ok = setVal8(key, rate, layer, maxWait); + if (ok) + { + packetUBXSECSIG->automaticFlags.flags.bits.automatic = (rate > 0); + packetUBXSECSIG->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + packetUBXSECSIG->moduleQueried = false; + return ok; +} + +// Enable automatic navigation message generation by the GNSS. This changes the way getSECSIG works. +bool DevUBLOXGNSS::setAutoSECSIGcallbackPtr(void (*callbackPointerPtr)(UBX_SEC_SIG_data_t *), uint8_t layer, uint16_t maxWait) +{ + // Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually. + bool result = setAutoSECSIG(true, false, layer, maxWait); + if (!result) + return (result); // Bail if setAutoSECSIG failed + + if (packetUBXSECSIG->callbackData == nullptr) // Check if RAM has been allocated for the callback copy + { + packetUBXSECSIG->callbackData = new UBX_SEC_SIG_data_t; // Allocate RAM for the main struct + } + + if (packetUBXSECSIG->callbackData == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("setAutoSECSIGcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXSECSIG->callbackPointerPtr = callbackPointerPtr; // RAM has been allocated so now update the pointer + + return (true); +} + +// In case no config access to the GNSS is possible and SEC-SIG is send cyclically already +// set config to suitable parameters +bool DevUBLOXGNSS::assumeAutoSECSIG(bool enabled, bool implicitUpdate) +{ + if (packetUBXSECSIG == nullptr) + initPacketUBXSECSIG(); // Check that RAM has been allocated for the data + if (packetUBXSECSIG == nullptr) // Only attempt this if RAM allocation was successful + return false; + + bool changes = packetUBXSECSIG->automaticFlags.flags.bits.automatic != enabled || packetUBXSECSIG->automaticFlags.flags.bits.implicitUpdate != implicitUpdate; + if (changes) + { + packetUBXSECSIG->automaticFlags.flags.bits.automatic = enabled; + packetUBXSECSIG->automaticFlags.flags.bits.implicitUpdate = implicitUpdate; + } + return changes; +} + +// PRIVATE: Allocate RAM for packetUBXSECSIG and initialize it +bool DevUBLOXGNSS::initPacketUBXSECSIG() +{ + packetUBXSECSIG = new UBX_SEC_SIG_t; // Allocate RAM for the main struct + if (packetUBXSECSIG == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("initPacketUBXSECSIG: RAM alloc failed!")); +#endif + return (false); + } + packetUBXSECSIG->automaticFlags.flags.all = 0; + packetUBXSECSIG->callbackPointerPtr = nullptr; + packetUBXSECSIG->callbackData = nullptr; + packetUBXSECSIG->moduleQueried = false; + return (true); +} + +// Mark all the data as read/stale. This is handy to get data alignment after CRC failure +void DevUBLOXGNSS::flushSECSIG() +{ + if (packetUBXSECSIG == nullptr) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXSECSIG->moduleQueried = false; // Mark all datums as stale (read before) +} + +// Log this data in file buffer +void DevUBLOXGNSS::logSECSIG(bool enabled) +{ + if (packetUBXSECSIG == nullptr) + return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!) + packetUBXSECSIG->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 diff --git a/src/u-blox_GNSS.h b/src/u-blox_GNSS.h index 6d1fea5..149d44a 100644 --- a/src/u-blox_GNSS.h +++ b/src/u-blox_GNSS.h @@ -1096,7 +1096,19 @@ class DevUBLOXGNSS void logHNRPVT(bool enabled = true); // Log data to file buffer #endif - // Helper functions for CFG RATE +// UBX_SEC_SIG Signal security information + +bool getSECSIG(uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Query module for latest data. If autoSECSIG is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new SEC SIG is available. +bool getSECSIG(UBX_SEC_SIG_data_t * data = nullptr, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Query module for latest data. If autoSECSIG is disabled, performs an explicit poll and waits, if enabled does not block. Returns true if new SEC SIG is available. +bool setAutoSECSIG(bool enabled, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic (periodic) reports at the navigation frequency +bool setAutoSECSIG(bool enabled, bool implicitUpdate, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable/disable automatic (periodic) reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update +bool setAutoSECSIGrate(uint8_t rate, bool implicitUpdate = true, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Set the rate for automatic (periodic) reports +bool setAutoSECSIGcallbackPtr(void (*callbackPointerPtr)(UBX_SEC_SIG_data_t *), uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Enable automatic (periodic) reports at the navigation frequency. Data is accessed from the callback. +bool assumeAutoSECSIG(bool enabled, bool implicitUpdate = true); // In case no config access to the GPS is possible and SEC-SIG is send cyclically already +void flushSECSIG(); // Mark all the SEC-SIG data as read/stale +void logSECSIG(bool enabled = true); // Log data to file buffer + +// Helper functions for CFG RATE bool setNavigationFrequency(uint8_t navFreq, uint8_t layer = VAL_LAYER_RAM_BBR, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Set the number of nav solutions sent per second bool getNavigationFrequency(uint8_t *navFreq, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = kUBLOXGNSSDefaultMaxWait); // Get the number of nav solutions sent per second currently being output by module @@ -1418,6 +1430,8 @@ class DevUBLOXGNSS UBX_HNR_INS_t *packetUBXHNRINS = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary #endif + UBX_SEC_SIG_t *packetUBXSECSIG = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_MGA_ACK_DATA0_t *packetUBXMGAACK = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_MGA_DBD_t *packetUBXMGADBD = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1525,6 +1539,7 @@ class DevUBLOXGNSS bool initPacketUBXHNRATT(); // Allocate RAM for packetUBXHNRATT and initialize it bool initPacketUBXHNRINS(); // Allocate RAM for packetUBXHNRINS and initialize it bool initPacketUBXHNRPVT(); // Allocate RAM for packetUBXHNRPVT and initialize it + bool initPacketUBXSECSIG(); // Allocate RAM for packetUBXSECSIG and initialize it bool initPacketUBXMGAACK(); // Allocate RAM for packetUBXMGAACK and initialize it bool initPacketUBXMGADBD(); // Allocate RAM for packetUBXMGADBD and initialize it diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index 40bb690..97fcb06 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -243,8 +243,9 @@ const uint32_t UBLOX_CFG_LOGFILTER_POSITION_THRS = UBX_CFG_U2 | 0x40de0008; // CFG-MOT: Motion detector configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -const uint32_t UBLOX_CFG_MOT_GNSSSPEED_THRS = UBX_CFG_U1 | 0x20250038; // GNSS speed threshold below which platform is considered as stationary (a.k.a. static hold threshold) -const uint32_t UBLOX_CFG_MOT_GNSSDIST_THRS = UBX_CFG_U2 | 0x3025003b; // Distance above which GNSS-based stationary motion is exit (a.k.a. static hold distance threshold) +const uint32_t UBLOX_CFG_MOT_GNSSSPEED_THRS = UBX_CFG_U1 | 0x20250038; // GNSS speed threshold below which platform is considered as stationary (a.k.a. static hold threshold) +const uint32_t UBLOX_CFG_MOT_GNSSDIST_THRS = UBX_CFG_U2 | 0x3025003b; // Distance above which GNSS-based stationary motion is exit (a.k.a. static hold distance threshold) +const uint32_t UBLOX_CFG_MOT_IMU_FILT_WINDOW = UBX_CFG_U2 | 0x30250016; // Averaging window for IMU measurements in noisy setups. // CFG-MSGOUT: Message output configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -1152,7 +1153,10 @@ const uint32_t UBLOX_CFG_SEC_JAMDET_SENSITIVITY_HI = UBX_CFG_L | 0x10f60051; // // CFG-SFCORE: Sensor fusion (SF) core configuration (ZED-F9R) //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- -const uint32_t UBLOX_CFG_SFCORE_USE_SF = UBX_CFG_L | 0x10080001; // Use ADR/UDR sensor fusion +const uint32_t UBLOX_CFG_SFCORE_USE_SF = UBX_CFG_L | 0x10080001; // Use ADR/UDR sensor fusion +const uint32_t UBLOX_CFG_SFCORE_IMU2CRP_LA_X = UBX_CFG_I2 | 0x30080002; // X coordinate of IMU-to-CRP lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFCORE_IMU2CRP_LA_Y = UBX_CFG_I2 | 0x30080003; // Y coordinate of IMU-to-CRP lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFCORE_IMU2CRP_LA_Z = UBX_CFG_I2 | 0x30080004; // Z coordinate of IMU-to-CRP lever-arm in the installation frame // CFG-SFIMU: Sensor fusion (SF) inertial measurement unit (IMU) configuration (ZED-F9R) //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -1168,10 +1172,14 @@ const uint32_t UBLOX_CFG_SFIMU_ACCEL_ACCURACY = UBX_CFG_U2 | 0x30060018; const uint32_t UBLOX_CFG_SFIMU_IMU_EN = UBX_CFG_L | 0x1006001d; // IMU enabled const uint32_t UBLOX_CFG_SFIMU_IMU_I2C_SCL_PIO = UBX_CFG_U1 | 0x2006001e; // SCL PIO of the IMU I2C const uint32_t UBLOX_CFG_SFIMU_IMU_I2C_SDA_PIO = UBX_CFG_U1 | 0x2006001f; // SDA PIO of the IMU I2C +const uint32_t UBLOX_CFG_SFIMU_IMU_IMU2ANT_LA_X = UBX_CFG_I2 | 0x30060020; // X coordinate of IMU-to-ANT lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFIMU_IMU_IMU2ANT_LA_Y = UBX_CFG_I2 | 0x30060021; // Y coordinate of IMU-to-ANT lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFIMU_IMU_IMU2ANT_LA_Z = UBX_CFG_I2 | 0x30060022; // Z coordinate of IMU-to-ANT lever-arm in the installation frame const uint32_t UBLOX_CFG_SFIMU_AUTO_MNTALG_ENA = UBX_CFG_L | 0x10060027; // Enable automatic IMU-mount alignment const uint32_t UBLOX_CFG_SFIMU_IMU_MNTALG_YAW = UBX_CFG_U4 | 0x4006002d; // User-defined IMU-mount yaw angle [0, 360] const uint32_t UBLOX_CFG_SFIMU_IMU_MNTALG_PITCH = UBX_CFG_I2 | 0x3006002e; // User-defined IMU-mount pitch angle [-90, 90] const uint32_t UBLOX_CFG_SFIMU_IMU_MNTALG_ROLL = UBX_CFG_I2 | 0x3006002f; // User-defined IMU-mount roll angle [-180, 180] +const uint32_t UBLOX_CFG_SFIMU_IMU_MNTALG_TOLERANCE = UBX_CFG_E1 | 0x20060030; // User-defined IMU mount alignment angles tolerance level // CFG-SFODO: Sensor fusion (SF) odometer configuration (ZED-F9R) //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -1190,6 +1198,9 @@ const uint32_t UBLOX_CFG_SFODO_SPEED_BAND = UBX_CFG_U2 | 0x3007000e; // Sp const uint32_t UBLOX_CFG_SFODO_USE_WT_PIN = UBX_CFG_L | 0x1007000f; // Wheel tick signal enabled const uint32_t UBLOX_CFG_SFODO_DIR_PINPOL = UBX_CFG_L | 0x10070010; // Wheel tick direction pin polarity const uint32_t UBLOX_CFG_SFODO_DIS_AUTOSW = UBX_CFG_L | 0x10070011; // Disable automatic use of wheel tick or speed data received over the software interface +const uint32_t UBLOX_CFG_SFODO_IMU2VRP_LA_X = UBX_CFG_I2 | 0x30070012; // X coordinate of IMU-to-VRP lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFODO_IMU2VRP_LA_Y = UBX_CFG_I2 | 0x30070013; // Y coordinate of IMU-to-VRP lever-arm in the installation frame +const uint32_t UBLOX_CFG_SFODO_IMU2VRP_LA_Z = UBX_CFG_I2 | 0x30070014; // Z coordinate of IMU-to-VRP lever-arm in the installation frame const uint32_t UBLOX_CFG_SFODO_DIS_DIR_INFO = UBX_CFG_L | 0x1007001c; // Do not use directional information // CFG-SIGNAL: Satellite systems (GNSS) signal configuration diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index c786a89..3a183ed 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -2227,6 +2227,89 @@ typedef struct // SEC-specific structs +// UBX-SEC-SIG (0x27 0x09): Signal security information +// Note: there are two versions of UBX-SEC-SIG. +// Version 1 is 12 bytes. +// Version 2 is variable. +// Check the version byte before attempting to read data from the struct +const uint16_t UBX_SEC_SIG_LEN_VERSION1 = 12; +const uint16_t UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2 = 6; // ZED seems to default to 6 frequencies - without jamming +const uint16_t UBX_SEC_SIG_MAX_LEN_VERSION2 = 4 + (4 * UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2); + +typedef struct +{ + uint8_t version; // Message version (0x01 for this version) + union + { + struct + { + uint8_t reserved0[3]; + union + { + uint8_t all; + struct + { + uint8_t jamDetEnabled : 1; // Flag indicates whether jamming/interference detection is enabled + uint8_t jammingState : 2; // Jamming/interference state: + // 0: Unknown, 1: No jamming indicated + // 2: Warning; jamming indicated but fix OK + // 3: Critical; jamming indicated and no fix + } bits; + } jamFlags; + uint8_t reserved1[3]; + union + { + uint8_t all; + struct + { + uint8_t spfDetEnabled : 1; // Flag indicates whether spoofing detection is enabled + uint8_t spoofingState : 3; // Spoofing state: + // 0: Unknown, 1: No spoofing indicated + // 2: Spoofing indicated, 3: Spoofing affirmed + } bits; + } spfFlags; + uint8_t reserved2[3]; + } version1; + struct { + union + { + uint8_t all; + struct + { + uint8_t jamDetEnabled : 1; // Flag indicates whether jamming/interference detection is enabled + uint8_t jamState : 2; // Jamming/interference state: + // 0: Unknown, 1: No jamming indicated + // 2: Warning; jamming indicated but fix OK + uint8_t spfDetEnabled : 1; // Flag indicates whether spoofing detection is enabled + uint8_t spfState : 3; // Spoofing state: + // 0: Unknown, 1: No spoofing indicated + // 2: Spoofing indicated, 3: Spoofing affirmed + } bits; + } sigSecFlags; + uint8_t reserved0; + uint8_t jamNumCentFreqs; // The number of center frequencies provided + union + { + uint32_t all; + struct + { + uint32_t centFreq : 24; // Center frequency in [kHz], floored to the nearest kHz multiple + uint32_t jammed : 1; // Flag indicates whether signals on the given center frequency are considered jammed + } bits; + } jamStateCentFreq[UBX_SEC_SEG_MAX_CENT_FREQ_VERSION2]; + } version2; + } versions; +} UBX_SEC_SIG_data_t; + +typedef struct +{ + ubxAutomaticFlags automaticFlags; + UBX_SEC_SIG_data_t data; + bool moduleQueried; + void (*callbackPointerPtr)(UBX_SEC_SIG_data_t *); + UBX_SEC_SIG_data_t *callbackData; +} UBX_SEC_SIG_t; + // UBX-SEC-UNIQID (0x27 0x03): Unique chip ID // The ID is five bytes on the F9 and M9 (version 1) but six bytes on the M10 (version 2) const uint16_t UBX_SEC_UNIQID_LEN_VERSION1 = 9;