diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp index d74ff8b..4a066d3 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.cpp @@ -311,14 +311,14 @@ void SFE_UBLOX_GNSS::end(void) packetUBXRXMPMP = NULL; // Redundant? } - if (packetUBXRXMPMPmessage != NULL) + if (packetUBXRXMQZSSL6message != NULL) { - if (packetUBXRXMPMPmessage->callbackData != NULL) + if (packetUBXRXMQZSSL6message->callbackData != NULL) { - delete packetUBXRXMPMPmessage->callbackData; + delete packetUBXRXMQZSSL6message->callbackData; } - delete packetUBXRXMPMPmessage; - packetUBXRXMPMPmessage = NULL; // Redundant? + delete packetUBXRXMQZSSL6message; + packetUBXRXMQZSSL6message = NULL; // Redundant? } if (packetUBXRXMCOR != NULL) @@ -1370,6 +1370,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID) if ((packetUBXRXMPMP != NULL) || (packetUBXRXMPMPmessage != NULL)) result = true; break; + case UBX_RXM_QZSSL6: + if ((packetUBXRXMQZSSL6 != NULL) || (packetUBXRXMQZSSL6message != NULL)) + result = true; + break; case UBX_RXM_COR: if (packetUBXRXMCOR != NULL) result = true; @@ -1545,6 +1549,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID) 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; @@ -3830,6 +3837,55 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg) packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid } } + if (msg->id == UBX_RXM_QZSSL6) + // 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 QZSSL6 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 ((packetUBXRXMQZSSL6 != NULL) && (packetUBXRXMQZSSL6->callbackData != NULL) + //&& (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' + ) + { + packetUBXRXMQZSSL6->callbackData->version = extractByte(msg, 0); + packetUBXRXMQZSSL6->callbackData->svId = extractByte(msg, 1); + packetUBXRXMQZSSL6->callbackData->cno = extractInt(msg, 2); + packetUBXRXMQZSSL6->callbackData->timeTag = extractLong(msg, 4); + packetUBXRXMQZSSL6->callbackData->groupDelay = extractByte(msg, 8); + packetUBXRXMQZSSL6->callbackData->bitErrCorr = extractByte(msg, 9); + packetUBXRXMQZSSL6->callbackData->chInfo = extractInt(msg, 10); + packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 12); + packetUBXRXMQZSSL6->callbackData->reserved0[0] = extractByte(msg, 13); + for (uint16_t i = 0; (i < 250); i++) + { + packetUBXRXMQZSSL6->callbackData->msgBytes[i] = extractByte(msg, i + 14); + } + packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid + } + + // Full QZSSL6 message, including Class, ID and checksum + // By default, new QZSSL6 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 ((packetUBXRXMQZSSL6message != NULL) && (packetUBXRXMQZSSL6message->callbackData != NULL) + //&& (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old' + ) + { + packetUBXRXMQZSSL6message->callbackData->sync1 = UBX_SYNCH_1; + packetUBXRXMQZSSL6message->callbackData->sync2 = UBX_SYNCH_2; + packetUBXRXMQZSSL6message->callbackData->cls = UBX_CLASS_RXM; + packetUBXRXMQZSSL6message->callbackData->ID = UBX_RXM_QZSSL6; + packetUBXRXMQZSSL6message->callbackData->lengthLSB = msg->len & 0xFF; + packetUBXRXMQZSSL6message->callbackData->lengthMSB = msg->len >> 8; + + memcpy(packetUBXRXMQZSSL6message->callbackData->payload, msg->payload, msg->len); + + packetUBXRXMQZSSL6message->callbackData->checksumA = msg->checksumA; + packetUBXRXMQZSSL6message->callbackData->checksumB = msg->checksumB; + + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid + } + } else if (msg->id == UBX_RXM_COR) { // Parse various byte fields into storage - but only if we have memory allocated for it @@ -5444,6 +5500,28 @@ void SFE_UBLOX_GNSS::checkCallbacks(void) packetUBXRXMPMPmessage->callbackPointerPtr(packetUBXRXMPMPmessage->callbackData); // Call the callback packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale } + + if ((packetUBXRXMQZSSL6 != NULL) // If RAM has been allocated for message storage + && (packetUBXRXMQZSSL6->callbackData != NULL) // If RAM has been allocated for the copy of the data + && (packetUBXRXMQZSSL6->callbackPointerPtr != NULL) // If the pointer to the callback has been defined + && (packetUBXRXMQZSSL6->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6")); + packetUBXRXMQZSSL6->callbackPointerPtr(packetUBXRXMQZSSL6->callbackData); // Call the callback + packetUBXRXMQZSSL6->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 + && (packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid + { + // if (_printDebug == true) + // _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM QZSSL6 message")); + packetUBXRXMQZSSL6message->callbackPointerPtr(packetUBXRXMQZSSL6message->callbackData); // Call the callback + packetUBXRXMQZSSL6message->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale + } 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 @@ -12561,6 +12639,97 @@ bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage() 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::setRXMQZSSL6callbackPtr(void (*callbackPointer)(UBX_RXM_QZSSL6_data_t *)) +{ + if (packetUBXRXMQZSSL6 == NULL) + initPacketUBXRXMQZSSL6(); // Check that RAM has been allocated for the data + if (packetUBXRXMQZSSL6 == NULL) // Only attempt this if RAM allocation was successful + return false; + + if (packetUBXRXMQZSSL6->callbackData == NULL) // Check if RAM has been allocated for the callback copy + { + packetUBXRXMQZSSL6->callbackData = new UBX_RXM_QZSSL6_data_t; // Allocate RAM for the main struct + } + + if (packetUBXRXMQZSSL6->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("setAutoRXMQZSSL6callbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + packetUBXRXMQZSSL6->callbackPointerPtr = callbackPointer; + return (true); +} + +// PRIVATE: Allocate RAM for packetUBXRXMQZSSL6 and initialize it +bool SFE_UBLOX_GNSS::initPacketUBXRXMQZSSL6() +{ + packetUBXRXMQZSSL6 = new UBX_RXM_QZSSL6_t; // Allocate RAM for the main struct + if (packetUBXRXMQZSSL6 == NULL) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial->println(F("initPacketUBXRXMQZSSL6: RAM alloc failed!")); +#endif + return (false); + } + packetUBXRXMQZSSL6->automaticFlags.flags.all = 0; + packetUBXRXMQZSSL6->callbackPointerPtr = NULL; + packetUBXRXMQZSSL6->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::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; // 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) diff --git a/src/SparkFun_u-blox_GNSS_Arduino_Library.h b/src/SparkFun_u-blox_GNSS_Arduino_Library.h index a60ee2b..bad937c 100644 --- a/src/SparkFun_u-blox_GNSS_Arduino_Library.h +++ b/src/SparkFun_u-blox_GNSS_Arduino_Library.h @@ -397,6 +397,7 @@ const uint8_t UBX_NAV_AOPSTATUS = 0x60; // AssistNow Autonomous status const uint8_t UBX_RXM_COR = 0x34; // Differential correction input status const uint8_t UBX_RXM_MEASX = 0x14; // Satellite Measurements for RRLP const uint8_t UBX_RXM_PMP = 0x72; // PMP raw data (NEO-D9S) (two different versions) (packet size for version 0x01 is variable) +const uint8_t UBX_RXM_QZSSL6 = 0x73; // QZSSL6 data (NEO-D9C) const uint8_t UBX_RXM_PMREQ = 0x41; // Requests a Power Management task (two different packet sizes) const uint8_t UBX_RXM_RAWX = 0x15; // Multi-GNSS Raw Measurement Data const uint8_t UBX_RXM_RLM = 0x59; // Galileo SAR Short-RLM report (two different packet sizes) @@ -1163,6 +1164,13 @@ public: bool setRXMPMPcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! bool setRXMPMPmessageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_message_data_t *)); // Use this if you want all of the PMP message (including sync chars, checksum, etc.) to push to a GNSS + // Configure a callback for the UBX-RXM-QZSSL6 messages produced by the NEO-D9C + // Note: on the NEO-D9C, the UBX-RXM-QZSSL6 messages are enabled by default on all ports. + // You can disable them by calling (e.g.) setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C, 0) + // The NEO-D9C does not support UBX-CFG-MSG + bool setRXMQZSSL6callbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack! + bool setRXMQZSSL6messageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *)); // Use this if you want all of the QZSSL6 message (including sync chars, checksum, etc.) to push to a GNSS + bool setRXMCORcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_COR_data_t *)); // RXM COR bool getRXMSFRBX(uint16_t maxWait = defaultMaxWait); // RXM SFRBX @@ -1518,6 +1526,8 @@ public: UBX_RXM_PMP_t *packetUBXRXMPMP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_PMP_message_t *packetUBXRXMPMPmessage = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_RXM_QZSSL6_t *packetUBXRXMQZSSL6 = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary + UBX_RXM_QZSSL6_message_t *packetUBXRXMQZSSL6message = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_COR_t *packetUBXRXMCOR = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_SFRBX_t *packetUBXRXMSFRBX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary UBX_RXM_RAWX_t *packetUBXRXMRAWX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1617,6 +1627,8 @@ private: bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it + bool initPacketUBXRXMQZSSL6(); // Allocate RAM for packetUBXRXMQZSSL6and initialize it + bool initPacketUBXRXMQZSSL6message(); // Allocate RAM for packetUBXRXMQZSSL6raw and initialize it bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it bool initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it bool initPacketUBXRXMRAWX(); // Allocate RAM for packetUBXRXMRAWX and initialize it diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index d8893c2..b1827e7 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -826,6 +826,14 @@ const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_UART1 = 0x20910323; // Output rate o const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_UART2 = 0x20910324; // Output rate of the UBX_MON_PMP message on port UART2 const uint32_t UBLOX_CFG_MSGOUT_UBX_MON_PMP_USB = 0x20910325; // Output rate of the UBX_MON_PMP message on port USB +// Additional CFG_MSGOUT keys for the NEO-D9S +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_I2C = 0x20910339; // Output rate of the UBX_RXM_QZSSL6 message on port I2C +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_SPI = 0x2091033a; // Output rate of the UBX_RXM_QZSSL6 message on port SPI +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART1 = 0x2091033b; // Output rate of the UBX_RXM_QZSSL6 message on port UART1 +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_UART2 = 0x2091033c; // Output rate of the UBX_RXM_QZSSL6 message on port UART2 +const uint32_t UBLOX_CFG_MSGOUT_UBX_RXM_QZSSL6_USB = 0x2091033d; // Output rate of the UBX_RXM_QZSSL6 message on port USB + // CFG-NAV2: Secondary output configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const uint32_t UBLOX_CFG_NAV2_OUT_ENABLED = 0x10170001; // Enable secondary (NAV2) output @@ -928,6 +936,14 @@ const uint32_t UBLOX_CFG_PMP_DESCRAMBLER_INIT = 0x30b10015; // Descrambler init const uint32_t UBLOX_CFG_PMP_USE_PRESCRAMBLING = 0x10b10019; // Use prescrambling. Enables/disables the prescrambling. const uint32_t UBLOX_CFG_PMP_UNIQUE_WORD = 0x50b1001a; // Unique word. Defines value of unique word. +// CFG-QZSS-L6: QZSS system configuration configuration (NEO-D9C) +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- +const uint32_t UBLOX_CFG_QZSSL6_SVIDA = 0x20370020; // QZSS L6 SV Id to be decoded by channel A +const uint32_t UBLOX_CFG_QZSSL6_SVIDB = 0x20370030; // QZSS L6 SV Id to be decoded by channel B +const uint32_t UBLOX_CFG_QZSSL6_MSGA = 0x20370050; // QZSS L6 messages to be decoded by channel A +const uint32_t UBLOX_CFG_QZSSL6_MSGB = 0x20370060; // QZSS L6 messages to be decoded by channel B +const uint32_t UBLOX_CFG_QZSSL6_RSDECODER = 0x20370080; // QZSS L6 message Reed-Solomon decoder mode + // CFG-QZSS: QZSS system configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const uint32_t UBLOX_CFG_QZSS_USE_SLAS_DGNSS = 0x10370005; // Apply QZSS SLAS DGNSS corrections diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 019aebb..c194802 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -1599,6 +1599,54 @@ typedef struct UBX_RXM_PMP_message_data_t *callbackData; } UBX_RXM_PMP_message_t; +// UBX-RXM-QZSSL6 (0x02 0x73): QZSS L6 raw data (D9C modules) +const uint16_t UBX_RXM_QZSSL6_MAX_USER_DATA = 250; +const uint16_t UBX_RXM_QZSSL6_MAX_LEN = UBX_RXM_PMP_MAX_USER_DATA + 14; + +typedef struct +{ + uint8_t version; // Message version (0x00 / 0x01) + uint8_t svId; // Satellite identifier + uint16_t cno; // Mean C/N0 + uint32_t timeTag; // Time since startup when frame started : ms + uint8_t groupDelay; // L6 group delay w.r.t. L2 on channel + uint8_t bitErrCorr; // Number of bit errors corrected by Reed-Solomon decoder + uint16_t chInfo; // Information about receiver channel associated with a received QZSS L6 message + uint8_t reserved0[2]; // Reserved + uint8_t msgBytes[UBX_RXM_QZSSL6_MAX_USER_DATA]; // Bytes in a QZSS L6 message +} UBX_RXM_QZSSL6_data_t; + +// The QZSSL6 data can only be accessed via a callback. QZSSL6 cannot be polled. +typedef struct +{ + ubxAutomaticFlags automaticFlags; + void (*callbackPointerPtr)(UBX_RXM_QZSSL6_data_t *); + UBX_RXM_QZSSL6_data_t *callbackData; +} UBX_RXM_QZSSL6_t; + +// Define a struct to hold the entire QZSSL6 message so the whole thing can be pushed to a GNSS. +// Remember that the length of the payload could be variable (with version 1 messages). +typedef struct +{ + uint8_t sync1; // 0xB5 + uint8_t sync2; // 0x62 + uint8_t cls; + uint8_t ID; + uint8_t lengthLSB; + uint8_t lengthMSB; + uint8_t payload[UBX_RXM_QZSSL6_MAX_LEN]; + uint8_t checksumA; + uint8_t checksumB; +} UBX_RXM_QZSSL6_message_data_t; + +// The PMP data can only be accessed via a callback. QZSSL6 cannot be polled. +typedef struct +{ + ubxAutomaticFlags automaticFlags; + void (*callbackPointerPtr)(UBX_RXM_QZSSL6_message_data_t *); + UBX_RXM_QZSSL6_message_data_t *callbackData; +} UBX_RXM_QZSSL6_message_t; + // CFG-specific structs // UBX-CFG-PRT (0x06 0x00): Port configuration