diff --git a/Utils/UBX_Integrity_Checker.py b/Utils/UBX_Integrity_Checker.py index c45b830..588c831 100644 --- a/Utils/UBX_Integrity_Checker.py +++ b/Utils/UBX_Integrity_Checker.py @@ -1,12 +1,100 @@ # Checks the integrity of u-blox binary files # Written by: Paul Clark -# Last update: May 10th, 2023 +# Last update: May 12th, 2025 # Reads a UBX file and checks the integrity of UBX, NMEA and RTCM data -# Will rewind and re-sync if an error is found -# Will create a repaired file if desired -# Will print any GNTXT messages if desired + +# How it works: +# +# Each byte from the UBX input file fi is processed according to the ubx_nmea_state state machine +# +# For UBX messages: +# Sync Char 1: 0xB5 +# Sync Char 2: 0x62 +# Class byte +# ID byte +# Length: two bytes, little endian +# Payload: length bytes +# Checksum: two bytes +# E.g.: +# RXM_RAWX is class 0x02 ID 0x15 +# RXM_SFRBF is class 0x02 ID 0x13 +# TIM_TM2 is class 0x0d ID 0x03 +# NAV_POSLLH is class 0x01 ID 0x02 +# NAV_PVT is class 0x01 ID 0x07 +# NAV-STATUS is class 0x01 ID 0x03 +# Sync is lost when: +# 0x62 does not follow 0xB5 +# The checksum fails +# +# For NMEA messages: +# Starts with a '$' +# The next five characters indicate the message type (stored in nmea_char_1 to nmea_char_5) +# Message fields are comma-separated +# Followed by an '*' +# Then a two character checksum (the logical exclusive-OR of all characters between the $ and the * as ASCII hex) +# Ends with CR LF +# Sync is lost when: +# The message length is excessive +# The checksum fails +# CR does not follow the checksum +# LF does not follow CR +# +# For RTCM messages: +# Byte0 is 0xD3 +# Byte1 contains 6 unused bits plus the 2 MS bits of the message length +# Byte2 contains the remainder of the message length +# Byte3 contains the first 8 bits of the message type +# Byte4 contains the last 4 bits of the message type and (optionally) the first 4 bits of the sub type +# Byte5 contains (optionally) the last 8 bits of the sub type +# Payload +# Checksum: three bytes CRC-24Q (calculated from Byte0 to the end of the payload, with seed 0) +# Sync is lost when: +# The checksum fails +# +# This code will: +# Rewind and re-sync if an error is found (sync is lost) +# Create a repaired file if desired +# Print any GNTXT messages if desired +# +# If sync is lost: +# The ubx_nmea_state is set initially to sync_lost +# sync_lost_at records the file byte at which sync was lost +# resync_in_progress is set to True +# The code attempts to resync - searching for the next valid message +# Sync is re-established the next time a valid message is found. resync_in_progress is set to False +# +# Rewind: +# If (e.g.) a UBX payload byte is dropped by the logging software, +# the checksum bytes become misaligned and the checksum fails. +# We do not know how many bytes were dropped... +# If we attempt to re-sync immediately after the checksum failure +# - without rewinding - the next valid message will also be discarded +# as the first byte(s) of that message will already have been processed +# when the checksum failure is detected. +# To avoid this, rewind_to stores the position of the last known valid data. +# E.g. rewind_to stores the position of the UBX length MSB byte - the byte +# before the start of the payload. If a UBX payload byte is dropped and the +# checksum fails, the code will rewind to that byte and attempt to re-sync +# from there. The valid message following the erroneous one is then processed +# correctly. +# rewind_in_progress is set to True during a rewind and cleared when the next +# valid message is processed. rewind_in_progress prevents the code from +# rewinding more than once. The code will not rewind again until +# rewind_in_progress is cleared. +# +# Repair: +# This code can repair the file, copying only valid CRC-checked messages to the +# repair file fo. When sync is lost and a rewind occurs, we need to rewind the +# repair file to the start of the erroneous message, and overwrite it with +# subsequent valid data. +# rewind_repair_file_to contains the position of the end of the last valid +# message written to the repair file. The repair file is rewound to here +# during a rewind and re-sync. +# The repair file is rewound and truncated before being closed, to discard any +# possible partial message already copied to the file. + # SparkFun code, firmware, and software is released under the MIT License (http://opensource.org/licenses/MIT) # @@ -32,636 +120,593 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. + import sys import os -# Add byte to checksums sum1 and sum2 -def csum(byte, sum1, sum2): - sum1 = sum1 + byte - sum2 = sum2 + sum1 - sum1 = sum1 & 0xFF - sum2 = sum2 & 0xFF - return sum1,sum2 - -# Add byte to CRC-24Q (RTCM) checksum -def crc24q(byte, sum): - crc = sum # Seed is 0 - - crc ^= byte << 16 # XOR-in incoming - - for i in range(8): - crc <<= 1 - if (crc & 0x1000000): - # CRC-24Q Polynomial: - # gi = 1 for i = 0, 1, 3, 4, 5, 6, 7, 10, 11, 14, 17, 18, 23, 24 - # 0b 1 1000 0110 0100 1100 1111 1011 - crc ^= 0x1864CFB # CRC-24Q - - return crc & 0xFFFFFF - - -print('UBX Integrity Checker') -print() - -filename = '' - -if filename == '': - # Check if the bin filename was passed in argv - if len(sys.argv) > 1: filename = sys.argv[1] - -# Find first .ubx file in the current directory -firstfile = '' -for root, dirs, files in os.walk("."): - if len(files) > 0: - if root == ".": # Comment this line to check sub-directories too - for afile in files: - if afile[-4:] == '.ubx': - if firstfile == '': firstfile = os.path.join(root, afile) - -# Ask user for .bin filename offering firstfile as the default -if filename == '': filename = input('Enter the UBX filename (default: ' + firstfile + '): ') # Get the filename -if filename == '': filename = firstfile - -# Ask user if the data contains NMEA messages -response = input('Could this file contain any NMEA messages? (Y/n): ') # Get the response -if (response == '') or (response == 'Y') or (response == 'y'): - containsNMEA = True -else: - containsNMEA = False - -# Ask user if the data contains RTCM messages -response = input('Could this file contain any RTCM messages? (Y/n): ') # Get the response -if (response == '') or (response == 'Y') or (response == 'y'): - containsRTCM = True -else: - containsRTCM = False - -# Ask user if the file should be repaired -response = input('Do you want to repair the file? (y/N): ') # Get the response -if (response == '') or (response == 'N') or (response == 'n'): - repairFile = False -else: - repairFile = True - if (filename[-4] == '.'): - repairFilename = filename[:-4] + '.repair' + filename[-4:] - else: - repairFilename = filename + '.repair' - -# Ask user if GNTXT is to be printed -printGNTXT = False -if containsNMEA == True: - response = input('Do you want to print any GNTXT messages found? (y/N): ') # Get the response - if (response == 'Y') or (response == 'y'): - printGNTXT = True - -print() -print('Processing',filename) -print() -filesize = os.path.getsize(filename) # Record the file size - -# Try to open file for reading -try: - fi = open(filename,"rb") -except: - raise Exception('Invalid file!') - -# Try to open repair file for writing -if (repairFile): - try: - fo = open(repairFilename,"wb") - except: - raise Exception('Could not open repair file!') - -processed = -1 # The number of bytes processed -messages = {} # The collected message types -keepGoing = True - -# Sync 'state machine' -looking_for_B5_dollar_D3 = 0 # Looking for UBX 0xB5, NMEA '$' or RTCM 0xD3 -looking_for_62 = 1 # Looking for UBX 0x62 header byte -looking_for_class = 2 # Looking for UBX class byte -looking_for_ID = 3 # Looking for UBX ID byte -looking_for_length_LSB = 4 # Looking for UBX length bytes -looking_for_length_MSB = 5 -processing_UBX_payload = 6 # Processing the UBX payload. Keep going until length bytes have been processed -looking_for_checksum_A = 7 # Looking for UBX checksum bytes -looking_for_checksum_B = 8 -sync_lost = 9 # Go into this state if sync is lost (bad checksum etc.) -looking_for_asterix = 10 # Looking for NMEA '*' -looking_for_csum1 = 11 # Looking for NMEA checksum bytes -looking_for_csum2 = 12 -looking_for_term1 = 13 # Looking for NMEA terminating bytes (CR and LF) -looking_for_term2 = 14 -looking_for_RTCM_len1 = 15 # Looking for RTCM length byte (2 MS bits) -looking_for_RTCM_len2 = 16 # Looking for RTCM length byte (8 LS bits) -looking_for_RTCM_type1 = 17 # Looking for RTCM Type byte (8 MS bits, first byte of the payload) -looking_for_RTCM_type2 = 18 # Looking for RTCM Type byte (4 LS bits, second byte of the payload) -looking_for_RTCM_subtype = 19 # Looking for RTCM Sub-Type byte (8 LS bits, third byte of the payload) -processing_RTCM_payload = 20 # Processing RTCM payload bytes -looking_for_RTCM_csum1 = 21 # Looking for the first 8 bits of the CRC-24Q checksum -looking_for_RTCM_csum2 = 22 # Looking for the second 8 bits of the CRC-24Q checksum -looking_for_RTCM_csum3 = 23 # Looking for the third 8 bits of the CRC-24Q checksum - -ubx_nmea_state = sync_lost # Initialize the state machine - -# Storage for UBX messages -ubx_length = 0 -ubx_class = 0 -ubx_ID = 0 -ubx_checksum_A = 0 -ubx_checksum_B = 0 -ubx_expected_checksum_A = 0 -ubx_expected_checksum_B = 0 -longest_UBX = 0 # The length of the longest UBX message -longest_UBX_candidate = 0 # Candidate for the length of the longest valid UBX message - -# Storage for NMEA messages -nmea_length = 0 -nmea_char_1 = 0 # e.g. G -nmea_char_2 = 0 # e.g. P -nmea_char_3 = 0 # e.g. G -nmea_char_4 = 0 # e.g. G -nmea_char_5 = 0 # e.g. A -nmea_csum = 0 -nmea_csum1 = 0 -nmea_csum2 = 0 -nmea_expected_csum1 = 0 -nmea_expected_csum2 = 0 -longest_NMEA = 0 # The length of the longest valid NMEA message - -# Storage for RTCM messages -rtcm_length = 0 -rtcm_type = 0 -rtcm_subtype = 0 -rtcm_expected_csum = 0 -rtcm_actual_csum = 0 -longest_rtcm = 0 # The length of the longest valid RTCM message -longest_rtcm_candidate = 0 # Candidate for the length of the longest valid RTCM message - -max_nmea_len = 128 # Maximum length for an NMEA message: use this to detect if we have lost sync while receiving an NMEA message -max_rtcm_len = 1023 + 6 # Maximum length for a RTCM message: use this to detect if we have lost sync while receiving a RTCM message -sync_lost_at = -1 # Record where we lost sync -rewind_to = -1 # Keep a note of where we should rewind to if sync is lost -rewind_attempts = 0 # Keep a note of how many rewinds have been attempted -max_rewinds = 100 # Abort after this many rewinds -rewind_in_progress = False # Flag to indicate if a rewind is in progress -resyncs = 0 # Record the number of successful resyncs -resync_in_progress = False # Flag to indicate if a resync is in progress -message_start_byte = 0 # Record where the latest message started (for resync reporting) - -rewind_repair_file_to = 0 # Keep a note of where to rewind the repair file to if sync is lost -repaired_file_bytes = 0 # Keep a note of how many bytes have been written to the repair file - -try: - while keepGoing: - - # Read one byte from the file - fileBytes = fi.read(1) - if (len(fileBytes) == 0): - print('ERROR: Read zero bytes. End of file?! Or zero file size?!') - raise Exception('End of file?! Or zero file size?!') - c = fileBytes[0] - - processed = processed + 1 # Keep a record of how many bytes have been read and processed - - # Write the byte to the repair file if desired - if (repairFile): - fo.write(fileBytes) - repaired_file_bytes = repaired_file_bytes + 1 - - # Process data bytes according to ubx_nmea_state - # For UBX messages: - # Sync Char 1: 0xB5 - # Sync Char 2: 0x62 - # Class byte - # ID byte - # Length: two bytes, little endian - # Payload: length bytes - # Checksum: two bytes - # - # For NMEA messages: - # Starts with a '$' - # The next five characters indicate the message type (stored in nmea_char_1 to nmea_char_5) - # Message fields are comma-separated - # Followed by an '*' - # Then a two character checksum (the logical exclusive-OR of all characters between the $ and the * as ASCII hex) - # Ends with CR LF - # Only allow a new file to be opened when a complete packet has been processed and ubx_nmea_state has returned to "looking_for_B5_dollar_D3" - # Or when a data error is detected (sync_lost) - # - # For RTCM messages: - # Byte0 is 0xD3 - # Byte1 contains 6 unused bits plus the 2 MS bits of the message length - # Byte2 contains the remainder of the message length - # Byte3 contains the first 8 bits of the message type - # Byte4 contains the last 4 bits of the message type and (optionally) the first 4 bits of the sub type - # Byte5 contains (optionally) the last 8 bits of the sub type - # Payload - # Checksum: three bytes CRC-24Q (calculated from Byte0 to the end of the payload, with seed 0) - - # RXM_RAWX is class 0x02 ID 0x15 - # RXM_SFRBF is class 0x02 ID 0x13 - # TIM_TM2 is class 0x0d ID 0x03 - # NAV_POSLLH is class 0x01 ID 0x02 - # NAV_PVT is class 0x01 ID 0x07 - # NAV-STATUS is class 0x01 ID 0x03 - - if (ubx_nmea_state == looking_for_B5_dollar_D3) or (ubx_nmea_state == sync_lost): - if (c == 0xB5): # Have we found Sync Char 1 (0xB5) if we were expecting one? - if (ubx_nmea_state == sync_lost): - print("UBX Sync Char 1 (0xB5) found at byte "+str(processed)) - print() - ubx_nmea_state = looking_for_62 # Now look for Sync Char 2 (0x62) - message_start_byte = processed # Record the message start byte for resync reporting - elif (c == 0x24) and (containsNMEA == True): # Have we found an NMEA '$' if we were expecting one? - if (ubx_nmea_state == sync_lost): - print("NMEA $ found at byte "+str(processed)) - print() - ubx_nmea_state = looking_for_asterix # Now keep going until we receive an asterix - nmea_length = 0 # Reset nmea_length then use it to check for excessive message length - nmea_csum = 0 # Reset the nmea_csum. Update it as each character arrives - nmea_char_1 = 0x30 # Reset the first five NMEA chars to something invalid - nmea_char_2 = 0x30 - nmea_char_3 = 0x30 - nmea_char_4 = 0x30 - nmea_char_5 = 0x30 - message_start_byte = processed # Record the message start byte for resync reporting - nmea_string = None - if printGNTXT == True: - nmea_string = '$' - elif (c == 0xD3) and (containsRTCM == True): # Have we found 0xD3 if we were expecting one? - if (ubx_nmea_state == sync_lost): - print("RTCM 0xD3 found at byte "+str(processed)) - print() - ubx_nmea_state = looking_for_RTCM_len1 # Now keep going until we receive the checksum - rtcm_expected_csum = 0 # Reset the RTCM csum with a seed of 0. Update it as each character arrives - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - message_start_byte = processed # Record the message start byte for resync reporting - else: - #print("Was expecting Sync Char 0xB5, NMEA $ or RTCM 0xD3 but did not receive one!") - if (c == 0x24): - print("Warning: * found at byte "+str(processed)+"! Are you sure this file does not contain NMEA messages?") - print() - if (c == 0xD3): - print("Warning: 0xD3 found at byte "+str(processed)+"! Are you sure this file does not contain RTCM messages?") - print() - sync_lost_at = processed - ubx_nmea_state = sync_lost - - # UBX messages - elif (ubx_nmea_state == looking_for_62): - if (c == 0x62): # Have we found Sync Char 2 (0x62) when we were expecting one? - ubx_expected_checksum_A = 0 # Reset the expected checksum - ubx_expected_checksum_B = 0 - ubx_nmea_state = looking_for_class # Now look for Class byte - else: - print("Panic!! Was expecting Sync Char 2 (0x62) but did not receive one!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - elif (ubx_nmea_state == looking_for_class): - ubx_class = c - ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum - ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A - ubx_nmea_state = looking_for_ID # Now look for ID byte - elif (ubx_nmea_state == looking_for_ID): - ubx_ID = c - ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum - ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A - message_type = '0x%02X 0x%02X'%(ubx_class,ubx_ID) # Record the message type - ubx_nmea_state = looking_for_length_LSB # Now look for length LSB - elif (ubx_nmea_state == looking_for_length_LSB): - ubx_length = c # Store the length LSB - ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum - ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A - ubx_nmea_state = looking_for_length_MSB # Now look for length MSB - elif (ubx_nmea_state == looking_for_length_MSB): - ubx_length = ubx_length + (c * 256) # Add the length MSB - ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum - ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A - longest_UBX_candidate = ubx_length + 8 # Update the longest UBX message length candidate. Include the header, class, ID, length and checksum bytes - rewind_to = processed # If we lose sync due to dropped bytes then rewind to here - ubx_nmea_state = processing_UBX_payload # Now look for payload bytes (length: ubx_length) - elif (ubx_nmea_state == processing_UBX_payload): - ubx_length = ubx_length - 1 # Decrement length by one - ubx_expected_checksum_A = ubx_expected_checksum_A + c # Update the expected checksum - ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A - if (ubx_length == 0): - ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits - ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff - ubx_nmea_state = looking_for_checksum_A # If we have received length payload bytes, look for checksum bytes - elif (ubx_nmea_state == looking_for_checksum_A): - ubx_checksum_A = c - ubx_nmea_state = looking_for_checksum_B - elif (ubx_nmea_state == looking_for_checksum_B): - ubx_checksum_B = c - ubx_nmea_state = looking_for_B5_dollar_D3 # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error - if ((ubx_expected_checksum_A != ubx_checksum_A) or (ubx_expected_checksum_B != ubx_checksum_B)): - print("Panic!! UBX checksum error!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync.") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - else: - # Valid UBX message was received. Check if we have seen this message type before - if message_type in messages: - messages[message_type] += 1 # if we have, increment its count - else: - messages[message_type] = 1 # if we have not, set its count to 1 - if (longest_UBX_candidate > longest_UBX): # Update the longest UBX message length - longest_UBX = longest_UBX_candidate - rewind_in_progress = False # Clear rewind_in_progress - rewind_to = -1 - if (resync_in_progress == True): # Check if we are resyncing - resync_in_progress = False # Clear the flag now that a valid message has been received - resyncs += 1 # Increment the number of successful resyncs - print("Sync successfully re-established at byte "+str(processed)+". The UBX message started at byte "+str(message_start_byte)) - print() - if (repairFile): - fo.seek(rewind_repair_file_to) # Rewind the repaired file - repaired_file_bytes = rewind_repair_file_to - fi.seek(message_start_byte) # Copy the valid message into the repair file - repaired_bytes_to_write = processed - message_start_byte - fileBytes = fi.read(repaired_bytes_to_write) - fo.write(fileBytes) - repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write - else: - if (repairFile): - rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost - - # NMEA messages - elif (ubx_nmea_state == looking_for_asterix): - nmea_length = nmea_length + 1 # Increase the message length count - if nmea_string is not None: - nmea_string += chr(c) - if (nmea_length > max_nmea_len): # If the length is greater than max_nmea_len, something bad must have happened (sync_lost) - print("Panic!! Excessive NMEA message length!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - nmea_string = None - continue - # If this is one of the first five characters, store it - if (nmea_length <= 5): - if (nmea_length == 1): - nmea_char_1 = c +class UBX_Integrity_Checker(): + + def __init__(self, ubxFile:str = None, repairFile:str = None, printGNTXT:bool = False, maxRewinds:int = 100): + self.filename = ubxFile + self.repairFilename = repairFile + self.printGNTXT = printGNTXT + self.max_rewinds = maxRewinds # Abort after this many rewinds + + def setFilename(self, ubxFile:str): + self.filename = ubxFile + + def setRepairFilename(self, repairFile:str): + self.repairFilename = repairFile + + def setPrintGNTXT(self, printGNTXT:bool): + self.printGNTXT = printGNTXT + + def setMaxRewinds(self, maxRewinds:int): + self.max_rewinds = maxRewinds + + # Add byte to checksums sum1 and sum2 + def csum(self, byte, sum1, sum2): + sum1 = sum1 + byte + sum2 = sum2 + sum1 + sum1 = sum1 & 0xFF + sum2 = sum2 & 0xFF + return sum1,sum2 + + # Add byte to CRC-24Q (RTCM) checksum + def crc24q(self, byte, sum): + crc = sum # Seed is 0 + + crc ^= byte << 16 # XOR-in incoming + + for i in range(8): + crc <<= 1 + if (crc & 0x1000000): + # CRC-24Q Polynomial: + # gi = 1 for i = 0, 1, 3, 4, 5, 6, 7, 10, 11, 14, 17, 18, 23, 24 + # 0b 1 1000 0110 0100 1100 1111 1011 + crc ^= 0x1864CFB # CRC-24Q + + return crc & 0xFFFFFF + + + def checkIntegrity(self): + + print('UBX Integrity Checker') + print() + + if self.filename is None: + raise Exception('Invalid file!') + + print('Processing',self.filename) + print() + filesize = os.path.getsize(self.filename) # Record the file size + + # Try to open file for reading + try: + fi = open(self.filename,"rb") + except: + raise Exception('Invalid file!') + + # Try to open repair file for writing + fo = None + if (self.repairFilename is not None): + try: + fo = open(self.repairFilename,"wb") + except: + fi.close() + raise Exception('Could not open repair file!') + + processed = -1 # The number of bytes processed + messages = {} # The collected message types + keepGoing = True + + # Sync 'state machine' + looking_for_B5_dollar_D3 = 0 # Looking for UBX 0xB5, NMEA '$' or RTCM 0xD3 + looking_for_62 = 1 # Looking for UBX 0x62 header byte + looking_for_class = 2 # Looking for UBX class byte + looking_for_ID = 3 # Looking for UBX ID byte + looking_for_length_LSB = 4 # Looking for UBX length bytes + looking_for_length_MSB = 5 + processing_UBX_payload = 6 # Processing the UBX payload. Keep going until length bytes have been processed + looking_for_checksum_A = 7 # Looking for UBX checksum bytes + looking_for_checksum_B = 8 + sync_lost = 9 # Go into this state if sync is lost (bad checksum etc.) + looking_for_asterix = 10 # Looking for NMEA '*' + looking_for_csum1 = 11 # Looking for NMEA checksum bytes + looking_for_csum2 = 12 + looking_for_term1 = 13 # Looking for NMEA terminating bytes (CR and LF) + looking_for_term2 = 14 + looking_for_RTCM_len1 = 15 # Looking for RTCM length byte (2 MS bits) + looking_for_RTCM_len2 = 16 # Looking for RTCM length byte (8 LS bits) + looking_for_RTCM_type1 = 17 # Looking for RTCM Type byte (8 MS bits, first byte of the payload) + looking_for_RTCM_type2 = 18 # Looking for RTCM Type byte (4 LS bits, second byte of the payload) + looking_for_RTCM_subtype = 19 # Looking for RTCM Sub-Type byte (8 LS bits, third byte of the payload) + processing_RTCM_payload = 20 # Processing RTCM payload bytes + looking_for_RTCM_csum1 = 21 # Looking for the first 8 bits of the CRC-24Q checksum + looking_for_RTCM_csum2 = 22 # Looking for the second 8 bits of the CRC-24Q checksum + looking_for_RTCM_csum3 = 23 # Looking for the third 8 bits of the CRC-24Q checksum + + ubx_nmea_state = sync_lost # Initialize the state machine + + # Storage for UBX messages + ubx_length = 0 + ubx_class = 0 + ubx_ID = 0 + ubx_checksum_A = 0 + ubx_checksum_B = 0 + ubx_expected_checksum_A = 0 + ubx_expected_checksum_B = 0 + longest_UBX = 0 # The length of the longest UBX message + longest_UBX_candidate = 0 # Candidate for the length of the longest valid UBX message + + # Storage for NMEA messages + nmea_length = 0 + nmea_char_1 = 0 # e.g. G + nmea_char_2 = 0 # e.g. P + nmea_char_3 = 0 # e.g. G + nmea_char_4 = 0 # e.g. G + nmea_char_5 = 0 # e.g. A + nmea_csum = 0 + nmea_csum1 = 0 + nmea_csum2 = 0 + nmea_expected_csum1 = 0 + nmea_expected_csum2 = 0 + longest_NMEA = 0 # The length of the longest valid NMEA message + containsNMEA = False + + # Storage for RTCM messages + rtcm_length = 0 + rtcm_type = 0 + rtcm_subtype = 0 + rtcm_expected_csum = 0 + rtcm_actual_csum = 0 + longest_rtcm = 0 # The length of the longest valid RTCM message + longest_rtcm_candidate = 0 # Candidate for the length of the longest valid RTCM message + containsRTCM = False + + max_nmea_len = 128 # Maximum length for an NMEA message: use this to detect if we have lost sync while receiving an NMEA message + max_rtcm_len = 1023 + 6 # Maximum length for a RTCM message: use this to detect if we have lost sync while receiving a RTCM message + sync_lost_at = -1 # Record where we lost sync + rewind_to = -1 # Keep a note of where we should rewind to if sync is lost + rewind_attempts = 0 # Keep a note of how many rewinds have been attempted + rewind_in_progress = False # Flag to indicate if a rewind is in progress + resyncs = 0 # Record the number of successful resyncs + resync_in_progress = False # Flag to indicate if a resync is in progress + message_start_byte = 0 # Record where the latest message started (for resync reporting) + + rewind_repair_file_to = 0 # Keep a note of where to rewind the repair file to if sync is lost + repaired_file_bytes = 0 # Keep a note of how many bytes have been written to the repair file + + try: + while keepGoing: + + # Read one byte from the file + fileBytes = fi.read(1) + if (len(fileBytes) == 0): + print('ERROR: Read zero bytes. End of file?! Or zero file size?!') + raise Exception('End of file?! Or zero file size?!') + c = fileBytes[0] + + processed = processed + 1 # Keep a record of how many bytes have been read and processed + + # Write the byte to the repair file if desired + if fo: + fo.write(fileBytes) + repaired_file_bytes = repaired_file_bytes + 1 + + # Process each byte through the state machine + if (ubx_nmea_state == looking_for_B5_dollar_D3) or (ubx_nmea_state == sync_lost): + if (c == 0xB5): # Have we found Sync Char 1 (0xB5) if we were expecting one? + if (ubx_nmea_state == sync_lost): + print("UBX Sync Char 1 (0xB5) found at byte "+str(processed)) + print() + ubx_nmea_state = looking_for_62 # Now look for Sync Char 2 (0x62) + message_start_byte = processed # Record the message start byte for resync reporting + elif (c == 0x24): # Have we found an NMEA '$' if we were expecting one? + if (ubx_nmea_state == sync_lost): + print("NMEA $ found at byte "+str(processed)) + print() + ubx_nmea_state = looking_for_asterix # Now keep going until we receive an asterix + containsNMEA = True + nmea_length = 0 # Reset nmea_length then use it to check for excessive message length + nmea_csum = 0 # Reset the nmea_csum. Update it as each character arrives + nmea_char_1 = 0x30 # Reset the first five NMEA chars to something invalid + nmea_char_2 = 0x30 + nmea_char_3 = 0x30 + nmea_char_4 = 0x30 + nmea_char_5 = 0x30 + message_start_byte = processed # Record the message start byte for resync reporting + nmea_string = None + if self.printGNTXT == True: + nmea_string = '$' + elif (c == 0xD3): # Have we found 0xD3 if we were expecting one? + if (ubx_nmea_state == sync_lost): + print("RTCM 0xD3 found at byte "+str(processed)) + print() + ubx_nmea_state = looking_for_RTCM_len1 # Now keep going until we receive the checksum + containsRTCM = True + rtcm_expected_csum = 0 # Reset the RTCM csum with a seed of 0. Update it as each character arrives + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + message_start_byte = processed # Record the message start byte for resync reporting + else: + #print("Was expecting Sync Char 0xB5, NMEA $ or RTCM 0xD3 but did not receive one!") + sync_lost_at = processed + ubx_nmea_state = sync_lost + + # UBX messages + elif (ubx_nmea_state == looking_for_62): + if (c == 0x62): # Have we found Sync Char 2 (0x62) when we were expecting one? + ubx_expected_checksum_A = 0 # Reset the expected checksum + ubx_expected_checksum_B = 0 + ubx_nmea_state = looking_for_class # Now look for Class byte + else: + print("Panic!! Was expecting Sync Char 2 (0x62) but did not receive one!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + elif (ubx_nmea_state == looking_for_class): + ubx_class = c + ubx_expected_checksum_A, ubx_expected_checksum_B = self.csum(c, ubx_expected_checksum_A, ubx_expected_checksum_B) + ubx_nmea_state = looking_for_ID # Now look for ID byte + elif (ubx_nmea_state == looking_for_ID): + ubx_ID = c + ubx_expected_checksum_A, ubx_expected_checksum_B = self.csum(c, ubx_expected_checksum_A, ubx_expected_checksum_B) + message_type = '0x%02X 0x%02X'%(ubx_class,ubx_ID) # Record the message type + ubx_nmea_state = looking_for_length_LSB # Now look for length LSB + elif (ubx_nmea_state == looking_for_length_LSB): + ubx_length = c # Store the length LSB + ubx_expected_checksum_A, ubx_expected_checksum_B = self.csum(c, ubx_expected_checksum_A, ubx_expected_checksum_B) + ubx_nmea_state = looking_for_length_MSB # Now look for length MSB + elif (ubx_nmea_state == looking_for_length_MSB): + ubx_length = ubx_length + (c * 256) # Add the length MSB + ubx_expected_checksum_A, ubx_expected_checksum_B = self.csum(c, ubx_expected_checksum_A, ubx_expected_checksum_B) + longest_UBX_candidate = ubx_length + 8 # Update the longest UBX message length candidate. Include the header, class, ID, length and checksum bytes rewind_to = processed # If we lose sync due to dropped bytes then rewind to here - elif (nmea_length == 2): - nmea_char_2 = c - elif (nmea_length == 3): - nmea_char_3 = c - elif (nmea_length == 4): - nmea_char_4 = c - else: # ubx_length == 5 - nmea_char_5 = c - message_type = chr(nmea_char_1) + chr(nmea_char_2) + chr(nmea_char_3) + chr(nmea_char_4) + chr(nmea_char_5) # Record the message type - if (message_type == "PUBX,"): # Remove the comma from PUBX - message_type = "PUBX" - if (message_type != "GNTXT"): # Reset nmea_string if this is not GNTXT + if ubx_length > 0: + ubx_nmea_state = processing_UBX_payload # Now look for payload bytes (length: ubx_length) + else: + ubx_nmea_state = looking_for_checksum_A # Zero length message. Go straight to checksum A + elif (ubx_nmea_state == processing_UBX_payload): + ubx_length = ubx_length - 1 # Decrement length by one + ubx_expected_checksum_A, ubx_expected_checksum_B = self.csum(c, ubx_expected_checksum_A, ubx_expected_checksum_B) + if (ubx_length == 0): + ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits + ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff + ubx_nmea_state = looking_for_checksum_A # If we have received length payload bytes, look for checksum bytes + elif (ubx_nmea_state == looking_for_checksum_A): + ubx_checksum_A = c + ubx_nmea_state = looking_for_checksum_B + elif (ubx_nmea_state == looking_for_checksum_B): + ubx_checksum_B = c + ubx_nmea_state = looking_for_B5_dollar_D3 # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error + if ((ubx_expected_checksum_A != ubx_checksum_A) or (ubx_expected_checksum_B != ubx_checksum_B)): + print("Panic!! UBX checksum error!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync.") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + else: + # Valid UBX message was received. Check if we have seen this message type before + if message_type in messages: + messages[message_type] += 1 # if we have, increment its count + else: + messages[message_type] = 1 # if we have not, set its count to 1 + if (longest_UBX_candidate > longest_UBX): # Update the longest UBX message length + longest_UBX = longest_UBX_candidate + rewind_in_progress = False # Clear rewind_in_progress + rewind_to = -1 + if (resync_in_progress == True): # Check if we are resyncing + resync_in_progress = False # Clear the flag now that a valid message has been received + resyncs += 1 # Increment the number of successful resyncs + print("Sync successfully re-established at byte "+str(processed)+". The UBX message started at byte "+str(message_start_byte)) + print() + if (fo): + fo.seek(rewind_repair_file_to) # Rewind the repaired file + repaired_file_bytes = rewind_repair_file_to + fi.seek(message_start_byte) # Copy the valid message into the repair file + repaired_bytes_to_write = 1 + processed - message_start_byte + fileBytes = fi.read(repaired_bytes_to_write) + fo.write(fileBytes) + repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write + rewind_repair_file_to = repaired_file_bytes + else: + if (fo): + rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost + + # NMEA messages + elif (ubx_nmea_state == looking_for_asterix): + nmea_length = nmea_length + 1 # Increase the message length count + if nmea_string is not None: + nmea_string += chr(c) + if (nmea_length > max_nmea_len): # If the length is greater than max_nmea_len, something bad must have happened (sync_lost) + print("Panic!! Excessive NMEA message length!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + nmea_string = None + continue + # If this is one of the first five characters, store it + if (nmea_length <= 5): + if (nmea_length == 1): + nmea_char_1 = c + rewind_to = processed # If we lose sync due to dropped bytes then rewind to here + elif (nmea_length == 2): + nmea_char_2 = c + elif (nmea_length == 3): + nmea_char_3 = c + elif (nmea_length == 4): + nmea_char_4 = c + else: # ubx_length == 5 + nmea_char_5 = c + if (nmea_char_5 == ','): # Check for a 4-character message type (e.g. PUBX) + message_type = chr(nmea_char_1) + chr(nmea_char_2) + chr(nmea_char_3) + chr(nmea_char_4) + else: + message_type = chr(nmea_char_1) + chr(nmea_char_2) + chr(nmea_char_3) + chr(nmea_char_4) + chr(nmea_char_5) # Record the message type + if (message_type != "GNTXT"): # Reset nmea_string if this is not GNTXT + nmea_string = None + # Now check if this is an '*' + if (c == 0x2A): + # Asterix received + # Don't exOR it into the checksum + # Instead calculate what the expected checksum should be (nmea_csum in ASCII hex) + nmea_expected_csum1 = ((nmea_csum & 0xf0) >> 4) + 0x30 # Convert MS nibble to ASCII hex + if (nmea_expected_csum1 >= 0x3A): # : follows 9 so add 7 to convert to A-F + nmea_expected_csum1 += 7 + nmea_expected_csum2 = (nmea_csum & 0x0f) + 0x30 # Convert LS nibble to ASCII hex + if (nmea_expected_csum2 >= 0x3A): # : follows 9 so add 7 to convert to A-F + nmea_expected_csum2 += 7 + # Next, look for the first csum character + ubx_nmea_state = looking_for_csum1 + continue # Don't include the * in the checksum + # Now update the checksum + # The checksum is the exclusive-OR of all characters between the $ and the * + nmea_csum = nmea_csum ^ c + elif (ubx_nmea_state == looking_for_csum1): + # Store the first NMEA checksum character + nmea_csum1 = c + if nmea_string is not None: + nmea_string += chr(c) + ubx_nmea_state = looking_for_csum2 + elif (ubx_nmea_state == looking_for_csum2): + # Store the second NMEA checksum character + nmea_csum2 = c + # Now check if the checksum is correct + if ((nmea_csum1 != nmea_expected_csum1) or (nmea_csum2 != nmea_expected_csum2)): + # The checksum does not match so sync_lost + print("Panic!! NMEA checksum error!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + nmea_string = None + else: + # Checksum was valid so wait for the terminators + if nmea_string is not None: + nmea_string += chr(c) + ubx_nmea_state = looking_for_term1 + elif (ubx_nmea_state == looking_for_term1): + # Check if this is CR + if (c != 0x0D): + print("Panic!! NMEA CR not found!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + nmea_string = None + else: + if nmea_string is not None: + nmea_string += chr(c) + ubx_nmea_state = looking_for_term2 + elif (ubx_nmea_state == looking_for_term2): + # Check if this is LF + if (c != 0x0A): + print("Panic!! NMEA LF not found!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost nmea_string = None - # Now check if this is an '*' - if (c == 0x2A): - # Asterix received - # Don't exOR it into the checksum - # Instead calculate what the expected checksum should be (nmea_csum in ASCII hex) - nmea_expected_csum1 = ((nmea_csum & 0xf0) >> 4) + 0x30 # Convert MS nibble to ASCII hex - if (nmea_expected_csum1 >= 0x3A): # : follows 9 so add 7 to convert to A-F - nmea_expected_csum1 += 7 - nmea_expected_csum2 = (nmea_csum & 0x0f) + 0x30 # Convert LS nibble to ASCII hex - if (nmea_expected_csum2 >= 0x3A): # : follows 9 so add 7 to convert to A-F - nmea_expected_csum2 += 7 - # Next, look for the first csum character - ubx_nmea_state = looking_for_csum1 - continue # Don't include the * in the checksum - # Now update the checksum - # The checksum is the exclusive-OR of all characters between the $ and the * - nmea_csum = nmea_csum ^ c - elif (ubx_nmea_state == looking_for_csum1): - # Store the first NMEA checksum character - nmea_csum1 = c - if nmea_string is not None: - nmea_string += chr(c) - ubx_nmea_state = looking_for_csum2 - elif (ubx_nmea_state == looking_for_csum2): - # Store the second NMEA checksum character - nmea_csum2 = c - # Now check if the checksum is correct - if ((nmea_csum1 != nmea_expected_csum1) or (nmea_csum2 != nmea_expected_csum2)): - # The checksum does not match so sync_lost - print("Panic!! NMEA checksum error!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - nmea_string = None - else: - # Checksum was valid so wait for the terminators - if nmea_string is not None: - nmea_string += chr(c) - ubx_nmea_state = looking_for_term1 - elif (ubx_nmea_state == looking_for_term1): - # Check if this is CR - if (c != 0x0D): - print("Panic!! NMEA CR not found!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - nmea_string = None - else: - if nmea_string is not None: - nmea_string += chr(c) - ubx_nmea_state = looking_for_term2 - elif (ubx_nmea_state == looking_for_term2): - # Check if this is LF - if (c != 0x0A): - print("Panic!! NMEA LF not found!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - nmea_string = None - else: - # Valid NMEA message was received. Check if we have seen this message type before - if message_type in messages: - messages[message_type] += 1 # if we have, increment its count - else: - messages[message_type] = 1 # if we have not, set its count to 1 - if (nmea_length > longest_NMEA): # Update the longest NMEA message length - longest_NMEA = nmea_length - # Print GNTXT - if nmea_string is not None and printGNTXT == True: - nmea_string += chr(c) - print(nmea_string) - nmea_string = None - # LF was received so go back to looking for B5 or a $ - ubx_nmea_state = looking_for_B5_dollar_D3 - rewind_in_progress = False # Clear rewind_in_progress - rewind_to = -1 - if (resync_in_progress == True): # Check if we are resyncing - resync_in_progress = False # Clear the flag now that a valid message has been received - resyncs += 1 # Increment the number of successful resyncs - print("Sync successfully re-established at byte "+str(processed)+". The NMEA message started at byte "+str(message_start_byte)) - print() - if (repairFile): - fo.seek(rewind_repair_file_to) # Rewind the repaired file - repaired_file_bytes = rewind_repair_file_to - fi.seek(message_start_byte) # Copy the valid message into the repair file - repaired_bytes_to_write = processed - message_start_byte - fileBytes = fi.read(repaired_bytes_to_write) - fo.write(fileBytes) - repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write - else: - if (repairFile): - rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost - - # RTCM messages - elif (ubx_nmea_state == looking_for_RTCM_len1): - rtcm_length = (c & 0x03) << 8 # Extract length - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - rewind_to = processed # If we lose sync due to dropped bytes then rewind to here - ubx_nmea_state = looking_for_RTCM_len2 - elif (ubx_nmea_state == looking_for_RTCM_len2): - rtcm_length |= c # Extract length - longest_rtcm_candidate = rtcm_length + 6 # Update the longest RTCM message length candidate. Include the header, length and checksum bytes - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - if (rtcm_length > 0): - ubx_nmea_state = looking_for_RTCM_type1 - else: - ubx_nmea_state = looking_for_RTCM_csum1 - elif (ubx_nmea_state == looking_for_RTCM_type1): - rtcm_type = c << 4 # Extract type - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - rtcm_length = rtcm_length - 1 # Decrement length by one - if (rtcm_length > 0): - ubx_nmea_state = looking_for_RTCM_type2 - else: - ubx_nmea_state = looking_for_RTCM_csum1 - elif (ubx_nmea_state == looking_for_RTCM_type2): - rtcm_type |= c >> 4 # Extract type - message_type = '%04i'%rtcm_type # Record the message type - rtcm_subtype = (c & 0x0F) << 8 # Extract sub-type - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - rtcm_length = rtcm_length - 1 # Decrement length by one - if (rtcm_length > 0): - ubx_nmea_state = looking_for_RTCM_subtype - else: - ubx_nmea_state = looking_for_RTCM_csum1 - elif (ubx_nmea_state == looking_for_RTCM_subtype): - rtcm_subtype |= c # Extract sub-type - if (rtcm_type == 4072): # Record the sub-type but only for 4072 messages - message_type = message_type + '_%i'%rtcm_subtype - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - rtcm_length = rtcm_length - 1 # Decrement length by one - if (rtcm_length > 0): - ubx_nmea_state = processing_RTCM_payload - else: - ubx_nmea_state = looking_for_RTCM_csum1 - elif (ubx_nmea_state == processing_RTCM_payload): - rtcm_expected_csum = crc24q(c, rtcm_expected_csum) # Update expected checksum - rtcm_length = rtcm_length - 1 # Decrement length by one - if (rtcm_length == 0): - ubx_nmea_state = looking_for_RTCM_csum1 - elif (ubx_nmea_state == looking_for_RTCM_csum1): - rtcm_actual_csum = c << 8 - ubx_nmea_state = looking_for_RTCM_csum2 - elif (ubx_nmea_state == looking_for_RTCM_csum2): - rtcm_actual_csum |= c - rtcm_actual_csum <<= 8 - ubx_nmea_state = looking_for_RTCM_csum3 - elif (ubx_nmea_state == looking_for_RTCM_csum3): - rtcm_actual_csum |= c - ubx_nmea_state = looking_for_B5_dollar_D3 # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error - if (rtcm_expected_csum != rtcm_actual_csum): - print("Panic!! RTCM checksum error!") - print("Sync lost at byte "+str(processed)+". Attemting to re-sync.") - print() - sync_lost_at = processed - resync_in_progress = True - ubx_nmea_state = sync_lost - else: - # Valid RTCM message was received. Check if we have seen this message type before - if (longest_rtcm_candidate >= 8): # Message must contain at least 2 (+6) bytes to include a valid mesage type - if message_type in messages: - messages[message_type] += 1 # if we have, increment its count else: - messages[message_type] = 1 # if we have not, set its count to 1 - if (longest_rtcm_candidate > longest_rtcm): # Update the longest RTCM message length - longest_rtcm = longest_rtcm_candidate - rewind_in_progress = False # Clear rewind_in_progress - rewind_to = -1 - if (resync_in_progress == True): # Check if we are resyncing - resync_in_progress = False # Clear the flag now that a valid message has been received - resyncs += 1 # Increment the number of successful resyncs - print("Sync successfully re-established at byte "+str(processed)+". The RTCM message started at byte "+str(message_start_byte)) - print() - if (repairFile): - fo.seek(rewind_repair_file_to) # Rewind the repaired file - repaired_file_bytes = rewind_repair_file_to - fi.seek(message_start_byte) # Copy the valid message into the repair file - repaired_bytes_to_write = processed - message_start_byte - fileBytes = fi.read(repaired_bytes_to_write) - fo.write(fileBytes) - repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write - else: - if (repairFile): - rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost - - - # Check if the end of the file has been reached - if (processed >= filesize - 1): keepGoing = False - - # Check if we should attempt to rewind - # Don't rewind if we have not yet seen a valid message - # Don't rewind if a rewind is already in progress - if (ubx_nmea_state == sync_lost) and (len(messages) > 0) and (rewind_in_progress == False) and (rewind_to >= 0): - rewind_attempts += 1 # Increment the number of rewind attempts - if (rewind_attempts > max_rewinds): # Only rewind up to max_rewind times - print("Panic! Maximum rewind attempts reached! Aborting...") - keepGoing = False - else: - print("Sync has been lost. Currently processing byte "+str(processed)+". Rewinding to byte "+str(rewind_to)) - print() - fi.seek(rewind_to) # Rewind the file - processed = rewind_to - 1 # Rewind processed too! (-1 is needed as processed is incremented at the start of the loop) - rewind_in_progress = True # Flag that a rewind is in progress - - -finally: - fi.close() # Close the file - - if (repairFile): - fo.close() - - # Print the file statistics - print() - processed += 1 - print('Processed',processed,'bytes') - print('File size was',filesize) - if (processed != filesize): - print('FILE SIZE MISMATCH!!') - print('Longest valid UBX message was %i bytes'%longest_UBX) - if (containsNMEA == True): - print('Longest valid NMEA message was %i characters'%longest_NMEA) - if (containsRTCM == True): - print('Longest valid RTCM message was %i bytes'%longest_rtcm) - if len(messages) > 0: - print('Message types and totals were:') - for key in messages.keys(): - spaces = ' ' * (9 - len(key)) - print('Message type:',key,spaces,'Total:',messages[key]) - if (resyncs > 0): - print('Number of successful resyncs:',resyncs) - print() - if (repairFile): - print('Repaired data written to:', repairFilename) - print() - print('Bye!') + # Valid NMEA message was received. Check if we have seen this message type before + if message_type in messages: + messages[message_type] += 1 # if we have, increment its count + else: + messages[message_type] = 1 # if we have not, set its count to 1 + if (nmea_length > longest_NMEA): # Update the longest NMEA message length + longest_NMEA = nmea_length + # Print GNTXT + if nmea_string is not None and self.printGNTXT == True: + nmea_string += chr(c) + print(nmea_string) + nmea_string = None + # LF was received so go back to looking for B5 or a $ + ubx_nmea_state = looking_for_B5_dollar_D3 + rewind_in_progress = False # Clear rewind_in_progress + rewind_to = -1 + if (resync_in_progress == True): # Check if we are resyncing + resync_in_progress = False # Clear the flag now that a valid message has been received + resyncs += 1 # Increment the number of successful resyncs + print("Sync successfully re-established at byte "+str(processed)+". The NMEA message started at byte "+str(message_start_byte)) + print() + if (fo): + fo.seek(rewind_repair_file_to) # Rewind the repaired file + repaired_file_bytes = rewind_repair_file_to + fi.seek(message_start_byte) # Copy the valid message into the repair file + repaired_bytes_to_write = 1 + processed - message_start_byte + fileBytes = fi.read(repaired_bytes_to_write) + fo.write(fileBytes) + repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write + rewind_repair_file_to = repaired_file_bytes + else: + if (fo): + rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost + + # RTCM messages + elif (ubx_nmea_state == looking_for_RTCM_len1): + rtcm_length = (c & 0x03) << 8 # Extract length + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + rewind_to = processed # If we lose sync due to dropped bytes then rewind to here + ubx_nmea_state = looking_for_RTCM_len2 + elif (ubx_nmea_state == looking_for_RTCM_len2): + rtcm_length |= c # Extract length + longest_rtcm_candidate = rtcm_length + 6 # Update the longest RTCM message length candidate. Include the header, length and checksum bytes + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + if (rtcm_length > 0): + ubx_nmea_state = looking_for_RTCM_type1 + else: + ubx_nmea_state = looking_for_RTCM_csum1 + elif (ubx_nmea_state == looking_for_RTCM_type1): + rtcm_type = c << 4 # Extract type + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + rtcm_length = rtcm_length - 1 # Decrement length by one + if (rtcm_length > 0): + ubx_nmea_state = looking_for_RTCM_type2 + else: + ubx_nmea_state = looking_for_RTCM_csum1 + elif (ubx_nmea_state == looking_for_RTCM_type2): + rtcm_type |= c >> 4 # Extract type + message_type = '%04i'%rtcm_type # Record the message type + rtcm_subtype = (c & 0x0F) << 8 # Extract sub-type + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + rtcm_length = rtcm_length - 1 # Decrement length by one + if (rtcm_length > 0): + ubx_nmea_state = looking_for_RTCM_subtype + else: + ubx_nmea_state = looking_for_RTCM_csum1 + elif (ubx_nmea_state == looking_for_RTCM_subtype): + rtcm_subtype |= c # Extract sub-type + if (rtcm_type == 4072): # Record the sub-type but only for 4072 messages + message_type = message_type + '_%i'%rtcm_subtype + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + rtcm_length = rtcm_length - 1 # Decrement length by one + if (rtcm_length > 0): + ubx_nmea_state = processing_RTCM_payload + else: + ubx_nmea_state = looking_for_RTCM_csum1 + elif (ubx_nmea_state == processing_RTCM_payload): + rtcm_expected_csum = self.crc24q(c, rtcm_expected_csum) # Update expected checksum + rtcm_length = rtcm_length - 1 # Decrement length by one + if (rtcm_length == 0): + ubx_nmea_state = looking_for_RTCM_csum1 + elif (ubx_nmea_state == looking_for_RTCM_csum1): + rtcm_actual_csum = c << 8 + ubx_nmea_state = looking_for_RTCM_csum2 + elif (ubx_nmea_state == looking_for_RTCM_csum2): + rtcm_actual_csum |= c + rtcm_actual_csum <<= 8 + ubx_nmea_state = looking_for_RTCM_csum3 + elif (ubx_nmea_state == looking_for_RTCM_csum3): + rtcm_actual_csum |= c + ubx_nmea_state = looking_for_B5_dollar_D3 # All bytes received so go back to looking for a new Sync Char 1 unless there is a checksum error + if (rtcm_expected_csum != rtcm_actual_csum): + print("Panic!! RTCM checksum error!") + print("Sync lost at byte "+str(processed)+". Attemting to re-sync.") + print() + sync_lost_at = processed + resync_in_progress = True + ubx_nmea_state = sync_lost + else: + # Valid RTCM message was received. Check if we have seen this message type before + if (longest_rtcm_candidate >= 8): # Message must contain at least 2 (+6) bytes to include a valid mesage type + if message_type in messages: + messages[message_type] += 1 # if we have, increment its count + else: + messages[message_type] = 1 # if we have not, set its count to 1 + if (longest_rtcm_candidate > longest_rtcm): # Update the longest RTCM message length + longest_rtcm = longest_rtcm_candidate + rewind_in_progress = False # Clear rewind_in_progress + rewind_to = -1 + if (resync_in_progress == True): # Check if we are resyncing + resync_in_progress = False # Clear the flag now that a valid message has been received + resyncs += 1 # Increment the number of successful resyncs + print("Sync successfully re-established at byte "+str(processed)+". The RTCM message started at byte "+str(message_start_byte)) + print() + if (fo): + fo.seek(rewind_repair_file_to) # Rewind the repaired file + repaired_file_bytes = rewind_repair_file_to + fi.seek(message_start_byte) # Copy the valid message into the repair file + repaired_bytes_to_write = 1 + processed - message_start_byte + fileBytes = fi.read(repaired_bytes_to_write) + fo.write(fileBytes) + repaired_file_bytes = repaired_file_bytes + repaired_bytes_to_write + rewind_repair_file_to = repaired_file_bytes + else: + if (fo): + rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost + + + # Check if the end of the file has been reached + if (processed >= filesize - 1): + keepGoing = False + + # Check if we should attempt to rewind + # Don't rewind if we have not yet seen a valid message + # Don't rewind if a rewind is already in progress + # Don't rewind if at the end of the file + if (ubx_nmea_state == sync_lost) and (len(messages) > 0) and (rewind_in_progress == False) and (rewind_to >= 0) and (keepGoing): + rewind_attempts += 1 # Increment the number of rewind attempts + if (rewind_attempts > self.max_rewinds): # Only rewind up to max_rewind times + print("Panic! Maximum rewind attempts reached! Aborting...") + keepGoing = False + else: + print("Sync has been lost. Currently processing byte "+str(processed)+". Rewinding to byte "+str(rewind_to)) + print() + fi.seek(rewind_to) # Rewind the file + processed = rewind_to - 1 # Rewind processed too! (-1 is needed as processed is incremented at the start of the loop) + rewind_in_progress = True # Flag that a rewind is in progress + + + finally: + fi.close() # Close the file + + if (fo): + fo.seek(rewind_repair_file_to) # Discard any partial message at the very end of the repair file + fo.truncate() + fo.close() + + # Print the file statistics + print() + processed += 1 + print('Processed',processed,'bytes') + print('File size was',filesize) + if (processed != filesize): + print('FILE SIZE MISMATCH!!') + print('Longest valid UBX message was %i bytes'%longest_UBX) + if (containsNMEA == True): + print('Longest valid NMEA message was %i characters'%longest_NMEA) + if (containsRTCM == True): + print('Longest valid RTCM message was %i bytes'%longest_rtcm) + if len(messages) > 0: + print('Message types and totals were:') + for key in messages.keys(): + spaces = ' ' * (9 - len(key)) + print('Message type:',key,spaces,'Total:',messages[key]) + if (resyncs > 0): + print('Number of successful resyncs:',resyncs) + print() + if (fo): + print('Repaired data written to:', self.repairFilename) + print() + +if __name__ == '__main__': + + import argparse + + parser = argparse.ArgumentParser(description='SparkFun UBX Integrity Checker') + parser.add_argument('ubxFile', metavar='ubxFile', type=str, help='The path to the UBX file') + parser.add_argument('-r', '--repairFile', required=False, type=str, default=None, help='The path to the repair file') + parser.add_argument('--GNTXT', default=False, action='store_true', help='Display any GNTXT messages found') + parser.add_argument('-rw', '--rewinds', required=False, type=int, default=100, help='The maximum number of file rewinds') + args = parser.parse_args() + + checker = UBX_Integrity_Checker(args.ubxFile, args.repairFile, args.GNTXT, args.rewinds) + + checker.checkIntegrity() diff --git a/keys/u-blox-20-HPG-2.00_InterfaceDescription_UBXDOC-304424225-19888_keys_sorted.txt b/keys/u-blox-20-HPG-2.00_InterfaceDescription_UBXDOC-304424225-19888_keys_sorted.txt new file mode 100644 index 0000000..d63f892 --- /dev/null +++ b/keys/u-blox-20-HPG-2.00_InterfaceDescription_UBXDOC-304424225-19888_keys_sorted.txt @@ -0,0 +1,555 @@ +0x10050012 +0x10050013 +0x10050014 +0x10050015 +0x10050016 +0x10110013 +0x10110025 +0x10110061 +0x101100dd +0x10220001 +0x10220002 +0x10220003 +0x10220004 +0x10240012 +0x10240020 +0x10240030 +0x10240040 +0x10240050 +0x10310001 +0x10310003 +0x10310004 +0x10310005 +0x10310007 +0x10310009 +0x1031000a +0x1031000b +0x1031000d +0x1031000e +0x1031000f +0x10310010 +0x10310012 +0x10310014 +0x10310015 +0x10310017 +0x10310018 +0x1031001a +0x1031001d +0x1031001f +0x10310020 +0x10310021 +0x10310022 +0x10310024 +0x10310025 +0x10310026 +0x10310028 +0x10350005 +0x10350009 +0x10360002 +0x10360003 +0x10360004 +0x10360005 +0x10360007 +0x10370005 +0x10370006 +0x10370007 +0x10510002 +0x10510003 +0x10520005 +0x10530005 +0x10640002 +0x10640003 +0x10640005 +0x10640006 +0x10710001 +0x10710002 +0x10710004 +0x10710005 +0x10720001 +0x10720002 +0x10720004 +0x10730001 +0x10730002 +0x10730004 +0x10730005 +0x10740001 +0x10740002 +0x10740004 +0x10750001 +0x10750002 +0x10750004 +0x10750005 +0x10760001 +0x10760002 +0x10760004 +0x10790001 +0x10790002 +0x10790004 +0x10790005 +0x107a0001 +0x107a0002 +0x107a0004 +0x10930003 +0x10930004 +0x10930005 +0x10930006 +0x10930011 +0x10930012 +0x10930013 +0x10930015 +0x10930016 +0x10930017 +0x10930018 +0x10930021 +0x10930022 +0x10930023 +0x10930024 +0x10930025 +0x10930026 +0x10a20001 +0x10a20002 +0x10a3002e +0x10a3002f +0x10a30030 +0x10a30031 +0x10a30032 +0x10a30033 +0x10a30034 +0x10a30035 +0x10a3006a +0x10a3006b +0x10a3006c +0x10c70001 +0x10c70002 +0x10de0002 +0x10de0004 +0x10f60009 +0x10f60051 +0x10f6005d +0x20030001 +0x20030002 +0x20030006 +0x20030007 +0x20030008 +0x2003000c +0x2003000d +0x2003000e +0x20050017 +0x20050023 +0x20050030 +0x20050036 +0x20090009 +0x20110011 +0x2011001c +0x20110021 +0x201100a1 +0x201100a2 +0x201100a3 +0x201100a4 +0x201100aa +0x201100ab +0x201100c4 +0x201100c5 +0x20140011 +0x20210003 +0x20220005 +0x20220021 +0x20220022 +0x20220031 +0x20220032 +0x20240011 +0x20240013 +0x20240014 +0x20250038 +0x2031003a +0x20340009 +0x20350007 +0x20510001 +0x20520002 +0x20520003 +0x20520004 +0x20530002 +0x20530003 +0x20530004 +0x20640001 +0x20910006 +0x20910007 +0x20910008 +0x2091000a +0x20910010 +0x20910011 +0x20910012 +0x20910014 +0x20910015 +0x20910016 +0x20910017 +0x20910019 +0x2091001a +0x2091001b +0x2091001c +0x2091001e +0x20910024 +0x20910025 +0x20910026 +0x20910028 +0x20910029 +0x2091002a +0x2091002b +0x2091002d +0x2091002e +0x2091002f +0x20910030 +0x20910032 +0x20910033 +0x20910034 +0x20910035 +0x20910037 +0x20910038 +0x20910039 +0x2091003a +0x2091003c +0x2091003d +0x2091003e +0x2091003f +0x20910041 +0x20910042 +0x20910043 +0x20910044 +0x20910046 +0x20910047 +0x20910048 +0x20910049 +0x2091004b +0x2091004c +0x2091004d +0x2091004e +0x20910050 +0x20910051 +0x20910052 +0x20910053 +0x20910055 +0x20910056 +0x20910057 +0x20910058 +0x2091005a +0x2091005b +0x2091005c +0x2091005d +0x2091005f +0x20910060 +0x20910061 +0x20910062 +0x20910064 +0x20910065 +0x20910066 +0x20910067 +0x20910069 +0x2091006a +0x2091006b +0x2091006c +0x2091006e +0x2091007e +0x2091007f +0x20910080 +0x20910082 +0x20910083 +0x20910084 +0x20910085 +0x20910087 +0x20910088 +0x20910089 +0x2091008a +0x2091008c +0x2091008d +0x2091008e +0x2091008f +0x20910091 +0x20910092 +0x20910093 +0x20910094 +0x20910096 +0x209100a1 +0x209100a2 +0x209100a3 +0x209100a5 +0x209100a6 +0x209100a7 +0x209100a8 +0x209100aa +0x209100ab +0x209100ac +0x209100ad +0x209100af +0x209100b0 +0x209100b1 +0x209100b2 +0x209100b4 +0x209100b5 +0x209100b6 +0x209100b7 +0x209100b9 +0x209100ba +0x209100bb +0x209100bc +0x209100be +0x209100bf +0x209100c0 +0x209100c1 +0x209100c3 +0x209100c4 +0x209100c5 +0x209100c6 +0x209100c8 +0x209100c9 +0x209100ca +0x209100cb +0x209100cd +0x209100ce +0x209100cf +0x209100d0 +0x209100d2 +0x209100d3 +0x209100d4 +0x209100d5 +0x209100d7 +0x209100d8 +0x209100d9 +0x209100da +0x209100dc +0x209100dd +0x209100de +0x209100df +0x209100e1 +0x209100e7 +0x209100e8 +0x209100e9 +0x209100eb +0x209100ec +0x209100ed +0x209100ee +0x209100f0 +0x209100f1 +0x209100f2 +0x209100f3 +0x209100f5 +0x209100f6 +0x209100f7 +0x209100f8 +0x209100fa +0x2091015f +0x20910160 +0x20910161 +0x20910163 +0x20910178 +0x20910179 +0x2091017a +0x2091017c +0x2091017d +0x2091017e +0x2091017f +0x20910181 +0x20910187 +0x20910188 +0x20910189 +0x2091018b +0x20910204 +0x20910205 +0x20910206 +0x20910208 +0x20910231 +0x20910232 +0x20910233 +0x20910235 +0x20910259 +0x2091025a +0x2091025b +0x2091025d +0x2091025e +0x2091025f +0x20910260 +0x20910262 +0x209102a4 +0x209102a5 +0x209102a6 +0x209102a8 +0x209102bd +0x209102be +0x209102bf +0x209102c1 +0x209102cc +0x209102cd +0x209102ce +0x209102d0 +0x209102d1 +0x209102d2 +0x209102d3 +0x209102d5 +0x209102d6 +0x209102d7 +0x209102d8 +0x209102da +0x20910303 +0x20910304 +0x20910305 +0x20910307 +0x20910318 +0x20910319 +0x2091031a +0x2091031c +0x20910345 +0x20910346 +0x20910347 +0x20910349 +0x2091034f +0x20910350 +0x20910351 +0x20910353 +0x20910354 +0x20910355 +0x20910356 +0x20910358 +0x20910359 +0x2091035a +0x2091035b +0x2091035d +0x2091035e +0x2091035f +0x20910360 +0x20910362 +0x20910363 +0x20910364 +0x20910365 +0x20910367 +0x20910368 +0x20910369 +0x2091036a +0x2091036c +0x2091036d +0x2091036e +0x2091036f +0x20910371 +0x20910386 +0x20910387 +0x20910388 +0x2091038a +0x2091038b +0x2091038c +0x2091038d +0x2091038f +0x209103a8 +0x209103a9 +0x209103aa +0x209103ac +0x20910400 +0x20910401 +0x20910402 +0x20910404 +0x20910634 +0x20910635 +0x20910636 +0x20910638 +0x20910689 +0x2091068a +0x2091068b +0x2091068d +0x2091069d +0x2091069e +0x2091069f +0x209106a1 +0x209106b6 +0x209106b7 +0x209106b8 +0x209106ba +0x209106ca +0x209106cb +0x209106cc +0x209106ce +0x20920001 +0x20920002 +0x20920003 +0x20920005 +0x20920006 +0x20920007 +0x20920008 +0x2092000a +0x20930001 +0x20930002 +0x20930007 +0x20930031 +0x20930032 +0x20a20003 +0x20a20005 +0x20a70001 +0x20c70003 +0x30050001 +0x30090001 +0x30090008 +0x30110017 +0x301100b1 +0x301100b2 +0x301100b3 +0x301100b4 +0x301100b5 +0x301100de +0x30210001 +0x30210002 +0x3025003b +0x30360008 +0x30370008 +0x30930033 +0x30a20004 +0x30a3003c +0x30de0005 +0x30de0006 +0x30de0007 +0x30f6000a +0x30f6000b +0x40030003 +0x40030004 +0x40030005 +0x40030009 +0x4003000a +0x4003000b +0x4003000f +0x40030010 +0x40030011 +0x4005000d +0x4005000e +0x4005000f +0x40050010 +0x40050011 +0x40050026 +0x40050027 +0x40110064 +0x40110065 +0x40110066 +0x40110067 +0x40110068 +0x40110069 +0x4011006a +0x401100c1 +0x401100c2 +0x40240021 +0x40240022 +0x40240023 +0x40240031 +0x40240032 +0x40240033 +0x40240041 +0x40240042 +0x40240043 +0x40240051 +0x40240052 +0x40240053 +0x40520001 +0x40530001 +0x40de0008 +0x5005002c +0x5005002d +0x50110062 +0x50110063 +0x50360006 +0x50c70004 +0x50c70005 +0x50c70006 +0x50c70007 diff --git a/keys/u-blox_config_keys_sorted.txt b/keys/u-blox_config_keys_sorted.txt index ed3f00b..cf18c8d 100644 --- a/keys/u-blox_config_keys_sorted.txt +++ b/keys/u-blox_config_keys_sorted.txt @@ -47,9 +47,11 @@ 0x10310007 0x10310009 0x1031000a +0x1031000b 0x1031000d 0x1031000e 0x1031000f +0x10310010 0x10310012 0x10310014 0x10310015 @@ -159,6 +161,9 @@ 0x10a30034 0x10a30035 0x10a30047 +0x10a3006a +0x10a3006b +0x10a3006c 0x10b10014 0x10b10016 0x10b10019 @@ -206,6 +211,7 @@ 0x201100aa 0x201100ab 0x201100c4 +0x201100c5 0x201100d6 0x20140011 0x20210003 @@ -219,6 +225,8 @@ 0x20240013 0x20240014 0x20250038 +0x2031003a +0x20340009 0x20350007 0x20370020 0x20370030 diff --git a/keywords.txt b/keywords.txt index b956b00..10b4bdc 100644 --- a/keywords.txt +++ b/keywords.txt @@ -84,6 +84,7 @@ NMEA_GGA_data_t KEYWORD1 NMEA_VTG_data_t KEYWORD1 NMEA_RMC_data_t KEYWORD1 NMEA_ZDA_data_t KEYWORD1 +NMEA_GST_data_t KEYWORD1 RTCM_1005_data_t KEYWORD1 RTCM_1006_data_t KEYWORD1 @@ -796,6 +797,11 @@ setNMEAGPZDAcallbackPtr KEYWORD2 getLatestNMEAGNZDA KEYWORD2 setNMEAGNZDAcallbackPtr KEYWORD2 +getLatestNMEAGPGST KEYWORD2 +setNMEAGPGSTcallbackPtr KEYWORD2 +getLatestNMEAGGST KEYWORD2 +setNMEAGNGSTcallbackPtr KEYWORD2 + extractLongLong KEYWORD2 extractSignedLongLong KEYWORD2 extractLong KEYWORD2 diff --git a/library.properties b/library.properties index e25ec14..19f5f3b 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=SparkFun u-blox GNSS v3 -version=3.1.9 +version=3.1.10 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_GNSS.cpp b/src/u-blox_GNSS.cpp index 815f1ef..e2225d1 100644 --- a/src/u-blox_GNSS.cpp +++ b/src/u-blox_GNSS.cpp @@ -640,6 +640,26 @@ void DevUBLOXGNSS::end(void) delete storageNMEAGNZDA; storageNMEAGNZDA = nullptr; } + + if (storageNMEAGPGST != nullptr) + { + if (storageNMEAGPGST->callbackCopy != nullptr) + { + delete storageNMEAGPGST->callbackCopy; + } + delete storageNMEAGPGST; + storageNMEAGPGST = nullptr; + } + + if (storageNMEAGNGST != nullptr) + { + if (storageNMEAGNGST->callbackCopy != nullptr) + { + delete storageNMEAGNGST->callbackCopy; + } + delete storageNMEAGNGST; + storageNMEAGNGST = nullptr; + } #endif if (_storageNMEA != nullptr) @@ -2522,6 +2542,20 @@ bool DevUBLOXGNSS::isThisNMEAauto() return true; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + if (storageNMEAGPGST != nullptr) + return true; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + if (storageNMEAGNGST != nullptr) + return true; + } + return false; } @@ -2600,6 +2634,24 @@ bool DevUBLOXGNSS::doesThisNMEAHaveCallback() return true; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + if (storageNMEAGPGST != nullptr) + if (storageNMEAGPGST->callbackCopy != nullptr) + if (storageNMEAGPGST->callbackPointerPtr != nullptr) + return true; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + if (storageNMEAGNGST != nullptr) + if (storageNMEAGNGST->callbackCopy != nullptr) + if (storageNMEAGNGST->callbackPointerPtr != nullptr) + return true; + } + return false; } @@ -2654,6 +2706,18 @@ uint8_t *DevUBLOXGNSS::getNMEAWorkingLengthPtr() return &storageNMEAGNZDA->workingCopy.length; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->workingCopy.length; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->workingCopy.length; + } + return nullptr; } @@ -2708,6 +2772,18 @@ uint8_t *DevUBLOXGNSS::getNMEAWorkingNMEAPtr() return &storageNMEAGNZDA->workingCopy.nmea[0]; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->workingCopy.nmea[0]; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->workingCopy.nmea[0]; + } + return nullptr; } @@ -2762,6 +2838,18 @@ uint8_t *DevUBLOXGNSS::getNMEACompleteLengthPtr() return &storageNMEAGNZDA->completeCopy.length; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->completeCopy.length; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->completeCopy.length; + } + return nullptr; } @@ -2816,6 +2904,18 @@ uint8_t *DevUBLOXGNSS::getNMEACompleteNMEAPtr() return &storageNMEAGNZDA->completeCopy.nmea[0]; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->completeCopy.nmea[0]; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->completeCopy.nmea[0]; + } + return nullptr; } @@ -2870,6 +2970,18 @@ uint8_t *DevUBLOXGNSS::getNMEACallbackLengthPtr() return &storageNMEAGNZDA->callbackCopy->length; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->callbackCopy->length; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->callbackCopy->length; + } + return nullptr; } @@ -2924,6 +3036,18 @@ uint8_t *DevUBLOXGNSS::getNMEACallbackNMEAPtr() return &storageNMEAGNZDA->callbackCopy->nmea[0]; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->callbackCopy->nmea[0]; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->callbackCopy->nmea[0]; + } + return nullptr; } @@ -2978,6 +3102,18 @@ uint8_t DevUBLOXGNSS::getNMEAMaxLength() return NMEA_ZDA_MAX_LENGTH; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return NMEA_GST_MAX_LENGTH; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return NMEA_GST_MAX_LENGTH; + } + return 0; } @@ -3032,6 +3168,18 @@ nmeaAutomaticFlags *DevUBLOXGNSS::getNMEAFlagsPtr() return &storageNMEAGNZDA->automaticFlags; } + strcpy(thisNMEA, "GPGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGPGST->automaticFlags; + } + + strcpy(thisNMEA, "GNGST"); + if (memcmp(thisNMEA, &nmeaAddressField[1], 5) == 0) + { + return &storageNMEAGNGST->automaticFlags; + } + return nullptr; } #endif @@ -6337,6 +6485,30 @@ void DevUBLOXGNSS::checkCallbacks(void) } storageNMEAGNZDA->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale } + + if (storageNMEAGPGST != nullptr) // If RAM has been allocated for message storage + if (storageNMEAGPGST->callbackCopy != nullptr) // If RAM has been allocated for the copy of the data + if (storageNMEAGPGST->automaticFlags.flags.bits.callbackCopyValid == 1) // If the copy of the data is valid + { + if (storageNMEAGPGST->callbackPointerPtr != nullptr) // If the pointer to the callback has been defined + { + storageNMEAGPGST->callbackPointerPtr(storageNMEAGPGST->callbackCopy); // Call the callback + } + storageNMEAGPGST->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale + } + + if (storageNMEAGNGST != nullptr) // If RAM has been allocated for message storage + if (storageNMEAGNGST->callbackCopy != nullptr) // If RAM has been allocated for the copy of the data + if (storageNMEAGNGST->automaticFlags.flags.bits.callbackCopyValid == 1) // If the copy of the data is valid + { + if (storageNMEAGNGST->callbackPointerPtr != nullptr) // If the pointer to the callback has been defined + { + // if (_printDebug == true) + // _debugSerial.println(F("checkCallbacks: calling callbackPtr for GNGST")); + storageNMEAGNGST->callbackPointerPtr(storageNMEAGNGST->callbackCopy); // Call the callback + } + storageNMEAGNGST->automaticFlags.flags.bits.callbackCopyValid = 0; // Mark the data as stale + } #endif if (storageRTCM1005 != nullptr) // If RAM has been allocated for message storage @@ -17240,6 +17412,165 @@ bool DevUBLOXGNSS::initStorageNMEAGNZDA() return (true); } + +// Initiate automatic storage of NMEA GPGST messages + +// Get the most recent GPGST 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 DevUBLOXGNSS::getLatestNMEAGPGST(NMEA_GST_data_t *data) +{ + if (storageNMEAGPGST == nullptr) + initStorageNMEAGPGST(); // Check that RAM has been allocated for the message + if (storageNMEAGPGST == nullptr) // Bail if the RAM allocation failed + return (false); + + checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Don't overwrite the requested Class and ID + + memcpy(data, &storageNMEAGPGST->completeCopy, sizeof(NMEA_GST_data_t)); // Copy the complete copy + + uint8_t result = 0; + if (storageNMEAGPGST->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? + { + result = 1; + if (storageNMEAGPGST->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? + { + result = 2; + storageNMEAGPGST->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read + } + } + + return (result); +} + +// Enable a callback on the arrival of a GPGST message +bool DevUBLOXGNSS::setNMEAGPGSTcallbackPtr(void (*callbackPointerPtr)(NMEA_GST_data_t *)) +{ + if (storageNMEAGPGST == nullptr) + initStorageNMEAGPGST(); // Check that RAM has been allocated for the message + if (storageNMEAGPGST == nullptr) // Bail if the RAM allocation failed + return (false); + + if (storageNMEAGPGST->callbackCopy == nullptr) // Check if RAM has been allocated for the callback copy + { + storageNMEAGPGST->callbackCopy = new NMEA_GST_data_t; + } + + if (storageNMEAGPGST->callbackCopy == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("setNMEAGPGSTcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + storageNMEAGPGST->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// Private: allocate RAM for incoming NMEA GPGST messages and initialize it +bool DevUBLOXGNSS::initStorageNMEAGPGST() +{ + storageNMEAGPGST = new NMEA_GPGST_t; // Allocate RAM for the main struct + if (storageNMEAGPGST == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("initStorageNMEAGPGST: RAM alloc failed!")); +#endif + return (false); + } + + storageNMEAGPGST->workingCopy.length = 0; // Clear the data length + memset(storageNMEAGPGST->workingCopy.nmea, 0, NMEA_GST_MAX_LENGTH); // Clear the nmea storage + storageNMEAGPGST->completeCopy.length = 0; // Clear the data length + memset(storageNMEAGPGST->completeCopy.nmea, 0, NMEA_GST_MAX_LENGTH); // Clear the nmea storage + + storageNMEAGPGST->callbackPointerPtr = nullptr; // Clear the callback pointers + storageNMEAGPGST->callbackCopy = nullptr; + + storageNMEAGPGST->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread + + return (true); +} + +uint8_t DevUBLOXGNSS::getLatestNMEAGNGST(NMEA_GST_data_t *data) +{ + if (storageNMEAGNGST == nullptr) + initStorageNMEAGNGST(); // Check that RAM has been allocated for the message + if (storageNMEAGNGST == nullptr) // Bail if the RAM allocation failed + return (false); + + checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Don't overwrite the requested Class and ID + + memcpy(data, &storageNMEAGNGST->completeCopy, sizeof(NMEA_GST_data_t)); // Copy the complete copy + + uint8_t result = 0; + if (storageNMEAGNGST->automaticFlags.flags.bits.completeCopyValid == 1) // Is the complete copy valid? + { + result = 1; + if (storageNMEAGNGST->automaticFlags.flags.bits.completeCopyRead == 0) // Has the data already been read? + { + result = 2; + storageNMEAGNGST->automaticFlags.flags.bits.completeCopyRead = 1; // Mark the data as read + } + } + + return (result); +} + +bool DevUBLOXGNSS::setNMEAGNGSTcallbackPtr(void (*callbackPointerPtr)(NMEA_GST_data_t *)) +{ + if (storageNMEAGNGST == nullptr) + initStorageNMEAGNGST(); // Check that RAM has been allocated for the message + if (storageNMEAGNGST == nullptr) // Bail if the RAM allocation failed + return (false); + + if (storageNMEAGNGST->callbackCopy == nullptr) // Check if RAM has been allocated for the callback copy + { + storageNMEAGNGST->callbackCopy = new NMEA_GST_data_t; + } + + if (storageNMEAGNGST->callbackCopy == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("setNMEAGNGSTcallbackPtr: RAM alloc failed!")); +#endif + return (false); + } + + storageNMEAGNGST->callbackPointerPtr = callbackPointerPtr; + return (true); +} + +// Private: allocate RAM for incoming NMEA GNGST messages and initialize it +bool DevUBLOXGNSS::initStorageNMEAGNGST() +{ + storageNMEAGNGST = new NMEA_GNGST_t; // Allocate RAM for the main struct + if (storageNMEAGNGST == nullptr) + { +#ifndef SFE_UBLOX_REDUCED_PROG_MEM + if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging + _debugSerial.println(F("initStorageNMEAGNGST: RAM alloc failed!")); +#endif + return (false); + } + + storageNMEAGNGST->workingCopy.length = 0; // Clear the data length + memset(storageNMEAGNGST->workingCopy.nmea, 0, NMEA_GST_MAX_LENGTH); // Clear the nmea storage + storageNMEAGNGST->completeCopy.length = 0; // Clear the data length + memset(storageNMEAGNGST->completeCopy.nmea, 0, NMEA_GST_MAX_LENGTH); // Clear the nmea storage + + storageNMEAGNGST->callbackPointerPtr = nullptr; // Clear the callback pointers + storageNMEAGNGST->callbackCopy = nullptr; + + storageNMEAGNGST->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread + + return (true); +} #endif // Private: allocate RAM for incoming non-Auto NMEA messages and initialize it diff --git a/src/u-blox_GNSS.h b/src/u-blox_GNSS.h index 149d44a..c198687 100644 --- a/src/u-blox_GNSS.h +++ b/src/u-blox_GNSS.h @@ -1335,6 +1335,10 @@ void logSECSIG(bool enabled = true); bool setNMEAGPZDAcallbackPtr(void (*callbackPointerPtr)(NMEA_ZDA_data_t *)); // Enable a callback on the arrival of a GPZDA message uint8_t getLatestNMEAGNZDA(NMEA_ZDA_data_t *data); // Return the most recent GNZDA: 0 = no data, 1 = stale data, 2 = fresh data bool setNMEAGNZDAcallbackPtr(void (*callbackPointerPtr)(NMEA_ZDA_data_t *)); // Enable a callback on the arrival of a GNZDA message + uint8_t getLatestNMEAGPGST(NMEA_GST_data_t *data); // Return the most recent GPGST: 0 = no data, 1 = stale data, 2 = fresh data + bool setNMEAGPGSTcallbackPtr(void (*callbackPointerPtr)(NMEA_GST_data_t *)); // Enable a callback on the arrival of a GPGST message + uint8_t getLatestNMEAGNGST(NMEA_GST_data_t *data); // Return the most recent GNGST: 0 = no data, 1 = stale data, 2 = fresh data + bool setNMEAGNGSTcallbackPtr(void (*callbackPointerPtr)(NMEA_GST_data_t *)); // Enable a callback on the arrival of a GNGST message #endif // RTCM @@ -1444,6 +1448,8 @@ void logSECSIG(bool enabled = true); NMEA_GNRMC_t *storageNMEAGNRMC = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary NMEA_GPZDA_t *storageNMEAGPZDA = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary NMEA_GNZDA_t *storageNMEAGNZDA = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary + NMEA_GPGST_t *storageNMEAGPGST = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary + NMEA_GNGST_t *storageNMEAGNGST = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary #endif RTCM_1005_t *storageRTCM1005 = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary @@ -1551,6 +1557,8 @@ void logSECSIG(bool enabled = true); bool initStorageNMEAGNRMC(); // Allocate RAM for incoming NMEA GNRMC messages and initialize it bool initStorageNMEAGPZDA(); // Allocate RAM for incoming NMEA GPZDA messages and initialize it bool initStorageNMEAGNZDA(); // Allocate RAM for incoming NMEA GNZDA messages and initialize it + bool initStorageNMEAGPGST(); // Allocate RAM for incoming NMEA GPGST messages and initialize it + bool initStorageNMEAGNGST(); // Allocate RAM for incoming NMEA GNGST messages and initialize it bool initStorageRTCM(); // Allocate RAM for incoming RTCM messages and initialize it bool initStorageNMEA(); // Allocate RAM for incoming non-Auto NMEA messages and initialize it diff --git a/src/u-blox_config_keys.h b/src/u-blox_config_keys.h index 97fcb06..4402c22 100644 --- a/src/u-blox_config_keys.h +++ b/src/u-blox_config_keys.h @@ -127,6 +127,7 @@ const uint32_t UBLOX_CFG_ANA_ORBMAXERR = UBX_CFG_U2 | 0x30230002; // Maximum acc // CFG-BDS: BeiDou system configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- const uint32_t UBLOX_CFG_BDS_USE_PRN_1_TO_5 = UBX_CFG_L | 0x10340014; // Use BeiDou geostationary satellites (PRN 1-5) +const uint32_t UBLOX_CFG_BDS_D1D2_NAVDATA = UBX_CFG_E1 | 0x20340009; // Enable only the given BDS D1/D2 navigation data streams // CFG-CLOCK: System clock configuration //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -190,6 +191,9 @@ const uint32_t UBLOX_CFG_HW_ANT_SUP_OPEN_THR = UBX_CFG_U1 | 0x20a30056; // const uint32_t UBLOX_CFG_HW_SENS_WOM_MODE = UBX_CFG_E1 | 0x20a30063; // Select Wake-On-Motion mode const uint32_t UBLOX_CFG_HW_SENS_WOM_THLD = UBX_CFG_U1 | 0x20a30064; // Wake-On-Motion threshold const uint32_t UBLOX_CFG_HW_RF_LNA_MODE = UBX_CFG_E1 | 0x20a30057; // Mode for internal LNA (NEO-F10) +const uint32_t UBLOX_CFG_HW_RF1_LNA_MODE_LOWGAIN = UBX_CFG_E1 | 0x10a3006a; // Low Gain Mode for internal LNA RF1 +const uint32_t UBLOX_CFG_HW_RF2_LNA_MODE_LOWGAIN = UBX_CFG_E1 | 0x10a3006b; // Low Gain Mode for internal LNA RF2 +const uint32_t UBLOX_CFG_HW_RF3_LNA_MODE_LOWGAIN = UBX_CFG_E1 | 0x10a3006c; // Low Gain Mode for internal LNA RF3 // CFG-I2C: Configuration of the I2C interface //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- @@ -1021,6 +1025,7 @@ const uint32_t UBLOX_CFG_NAVSPG_OUTFIL_FACC = UBX_CFG_U2 | 0x301100b5; // Out const uint32_t UBLOX_CFG_NAVSPG_CONSTR_ALT = UBX_CFG_I4 | 0x401100c1; // Fixed altitude (mean sea level) for 2D fix mode const uint32_t UBLOX_CFG_NAVSPG_CONSTR_ALTVAR = UBX_CFG_U4 | 0x401100c2; // Fixed altitude variance for 2D mode const uint32_t UBLOX_CFG_NAVSPG_CONSTR_DGNSSTO = UBX_CFG_U1 | 0x201100c4; // DGNSS timeout +const uint32_t UBLOX_CFG_NAVSPG_CONSTR_DGNSSTO_SCALE = UBX_CFG_U1 | 0x201100c5; // DGNSS timeout value scale for CFG-NAVSPGCONSTR_DGNSSTO const uint32_t UBLOX_CFG_NAVSPG_SIGATTCOMP = UBX_CFG_E1 | 0x201100d6; // Permanently attenuated signal compensation mode const uint32_t UBLOX_CFG_NAVSPG_PL_ENA = UBX_CFG_L | 0x101100d7; // Enable Protection level. If enabled, protection level computing will be on. const uint32_t UBLOX_CFG_NAVSPG_ONLY_AUTHDATA = UBX_CFG_L | 0x101100dd; // Enable using only signals with authenticated navigation data @@ -1215,11 +1220,13 @@ const uint32_t UBLOX_CFG_SIGNAL_GAL_ENA = UBX_CFG_L | 0x10310021; // Galil const uint32_t UBLOX_CFG_SIGNAL_GAL_E1_ENA = UBX_CFG_L | 0x10310007; // Galileo E1 const uint32_t UBLOX_CFG_SIGNAL_GAL_E5A_ENA = UBX_CFG_L | 0x10310009; // Galileo E5a const uint32_t UBLOX_CFG_SIGNAL_GAL_E5B_ENA = UBX_CFG_L | 0x1031000a; // Galileo E5b (only on u-blox F9 platform products) +const uint32_t UBLOX_CFG_SIGNAL_GAL_E6_ENA = UBX_CFG_L | 0x1031000b; // Galileo E6 (Default is true) const uint32_t UBLOX_CFG_SIGNAL_BDS_ENA = UBX_CFG_L | 0x10310022; // BeiDou Enable const uint32_t UBLOX_CFG_SIGNAL_BDS_B1_ENA = UBX_CFG_L | 0x1031000d; // BeiDou B1I const uint32_t UBLOX_CFG_SIGNAL_BDS_B1C_ENA = UBX_CFG_L | 0x1031000f; // BeiDou B1C const uint32_t UBLOX_CFG_SIGNAL_BDS_B2A_ENA = UBX_CFG_L | 0x10310028; // BeiDou B2a const uint32_t UBLOX_CFG_SIGNAL_BDS_B2_ENA = UBX_CFG_L | 0x1031000e; // BeiDou B2I (only on u-blox F9 platform products) +const uint32_t UBLOX_CFG_SIGNAL_BDS_B3_ENA = UBX_CFG_L | 0x10310010; // BeiDou B3I (Default is true) const uint32_t UBLOX_CFG_SIGNAL_QZSS_ENA = UBX_CFG_L | 0x10310024; // QZSS enable const uint32_t UBLOX_CFG_SIGNAL_QZSS_L1CA_ENA = UBX_CFG_L | 0x10310012; // QZSS L1C/A const uint32_t UBLOX_CFG_SIGNAL_QZSS_L5_ENA = UBX_CFG_L | 0x10310017; // QZSS L5 @@ -1230,6 +1237,7 @@ const uint32_t UBLOX_CFG_SIGNAL_GLO_L1_ENA = UBX_CFG_L | 0x10310018; // GLONA const uint32_t UBLOX_CFG_SIGNAL_GLO_L2_ENA = UBX_CFG_L | 0x1031001a; // GLONASS L2 (only on u-blox F9 platform products) const uint32_t UBLOX_CFG_SIGNAL_NAVIC_ENA = UBX_CFG_L | 0x10310026; // NavIC const uint32_t UBLOX_CFG_SIGNAL_NAVIC_L5_ENA = UBX_CFG_L | 0x1031001d; // NavIC L5 +const uint32_t UBLOX_CFG_SIGNAL_PLAN = UBX_CFG_E1 | 0x2031003a; // Active signal plan // CFG-SPARTN: Configuration of the SPARTN interface //-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- diff --git a/src/u-blox_structs.h b/src/u-blox_structs.h index 3a183ed..885928e 100644 --- a/src/u-blox_structs.h +++ b/src/u-blox_structs.h @@ -3056,6 +3056,32 @@ typedef struct NMEA_ZDA_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy } NMEA_GNZDA_t; +const uint8_t NMEA_GST_MAX_LENGTH = 100; + +typedef struct +{ + uint8_t length; // The number of bytes in nmea + uint8_t nmea[NMEA_GST_MAX_LENGTH]; +} NMEA_GST_data_t; + +typedef struct +{ + nmeaAutomaticFlags automaticFlags; + NMEA_GST_data_t workingCopy; // Incoming data is added to the working copy + NMEA_GST_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid + void (*callbackPointerPtr)(NMEA_GST_data_t *); + NMEA_GST_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy +} NMEA_GPGST_t; + +typedef struct +{ + nmeaAutomaticFlags automaticFlags; + NMEA_GST_data_t workingCopy; // Incoming data is added to the working copy + NMEA_GST_data_t completeCopy; // The working copy is copied into the complete copy when all data has been received and the checksum is valid + void (*callbackPointerPtr)(NMEA_GST_data_t *); + NMEA_GST_data_t *callbackCopy; // The callback gets its own preserved copy of the complete copy +} NMEA_GNGST_t; + typedef struct { uint8_t length;