@@ -0,0 +1,515 @@
|
||||
# Aligns the rcvTow in RAWX messages in u-blox UBX binary files to the nearest decimalPlaces seconds
|
||||
|
||||
# Written by: Paul Clark
|
||||
# Last update: August 17th 2022
|
||||
|
||||
# SparkFun code, firmware, and software is released under the MIT License (http://opensource.org/licenses/MIT)
|
||||
#
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2022 SparkFun Electronics
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
import sys
|
||||
import os
|
||||
import struct
|
||||
|
||||
# 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
|
||||
|
||||
print('UBX RAWX Aligner')
|
||||
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
|
||||
|
||||
repairFile = True
|
||||
if (filename[-4] == '.'):
|
||||
repairFilename = filename[:-4] + '.aligned' + filename[-4:]
|
||||
else:
|
||||
repairFilename = filename + '.aligned'
|
||||
|
||||
decimalPlaces = 0 # Default to whole seconds
|
||||
if len(sys.argv) > 2: decimalPlaces = sys.argv[2]
|
||||
|
||||
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 write and read
|
||||
if (repairFile):
|
||||
try:
|
||||
fo = open(repairFilename,"w+b")
|
||||
except:
|
||||
raise Exception('Could not open aligned file!')
|
||||
|
||||
processed = -1 # The number of bytes processed
|
||||
messages = {} # The collected message types
|
||||
keepGoing = True
|
||||
|
||||
# Sync 'state machine'
|
||||
looking_for_B5_dollar = 0 # Looking for either a UBX 0xB5 or an NMEA '$'
|
||||
looking_for_62 = 1 # Looking for a 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_payload = 6 # Processing the 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
|
||||
|
||||
ubx_nmea_state = sync_lost # Initialize the state machine
|
||||
|
||||
# Storage for UBX messages
|
||||
ubx_length = 0
|
||||
ubx_length_LSB = 0
|
||||
ubx_length_MSB = 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
|
||||
|
||||
max_nmea_len = 128 # Maximum length for an NMEA message: use this to detect if we have lost sync while receiving an NMEA 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
|
||||
|
||||
repair_file_rawx_payload_start = 0 # Keep a note of where the RAWX payload starts (i.e. where the rcvTow R8 starts)
|
||||
largest_rawx_alignment = 0.0 # Keep note of the largest alignment change
|
||||
|
||||
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"
|
||||
# Or when a data error is detected (sync_lost)
|
||||
|
||||
# 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) 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)+". Checking for Sync Char 2")
|
||||
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)+". Attempting to process the message")
|
||||
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
|
||||
else:
|
||||
#print("Was expecting Sync Char 0xB5 or an NMEA $ 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?")
|
||||
sync_lost_at = processed
|
||||
ubx_nmea_state = sync_lost
|
||||
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")
|
||||
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_length_LSB = 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_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_length_MSB = 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
|
||||
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_payload # Now look for payload bytes (length: ubx_length)
|
||||
|
||||
if (message_type == '0x02 0x15'): # Is this RAWX? If so, record the start of the payload
|
||||
repair_file_rawx_payload_start = repaired_file_bytes
|
||||
|
||||
elif (ubx_nmea_state == processing_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 # 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.")
|
||||
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
|
||||
|
||||
# Note: alignment is skipped if resyncing is in progress
|
||||
# You may need to run the code twice to align any skipped alignments
|
||||
|
||||
else:
|
||||
if (repairFile):
|
||||
rewind_repair_file_to = repaired_file_bytes # Rewind repair file to here if sync is lost
|
||||
|
||||
if (message_type == '0x02 0x15'): # Is this RAWX? If so, do the alignment
|
||||
|
||||
ubx_expected_checksum_A = 0 # Reuse the expected checksum
|
||||
ubx_expected_checksum_B = 0
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_class
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_ID
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_length_LSB
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + ubx_length_MSB
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
|
||||
fo.seek(repair_file_rawx_payload_start) # Rewind the repair file
|
||||
|
||||
fileBytes = fo.read(8) # Read the rcvTow R8
|
||||
rcvTow = struct.unpack('<d', fileBytes)[0] # Unpack the R8 (Little-endian)
|
||||
rcvTowRounded = round(rcvTow, decimalPlaces) # Round to decimalPlaces
|
||||
if (abs(rcvTow - rcvTowRounded) > largest_rawx_alignment):
|
||||
largest_rawx_alignment = abs(rcvTow - rcvTowRounded) # Record the largest alignment change
|
||||
fileBytes = struct.pack('<d', rcvTowRounded)
|
||||
fo.seek(repair_file_rawx_payload_start) # Rewind the repair file
|
||||
fo.write(fileBytes) # Write the rounded TOW to the repair file
|
||||
for i in range(8): # Update the checksum
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + fileBytes[i]
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
|
||||
bytesToRead = (ubx_length_MSB * 256) + ubx_length_LSB - 8
|
||||
fileBytes = fo.read(bytesToRead)
|
||||
for i in range(bytesToRead): # Update the checksum
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A + fileBytes[i]
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B + ubx_expected_checksum_A
|
||||
ubx_expected_checksum_A = ubx_expected_checksum_A & 0xff # Limit checksums to 8-bits
|
||||
ubx_expected_checksum_B = ubx_expected_checksum_B & 0xff
|
||||
fileBytes = struct.pack('BB', ubx_expected_checksum_A, ubx_expected_checksum_B)
|
||||
fo.write(fileBytes) # Write the updated checksum
|
||||
|
||||
# NMEA messages
|
||||
elif (ubx_nmea_state == looking_for_asterix):
|
||||
nmea_length = nmea_length + 1 # Increase the message length count
|
||||
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")
|
||||
sync_lost_at = processed
|
||||
resync_in_progress = True
|
||||
ubx_nmea_state = sync_lost
|
||||
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
|
||||
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"
|
||||
# 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
|
||||
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")
|
||||
sync_lost_at = processed
|
||||
resync_in_progress = True
|
||||
ubx_nmea_state = sync_lost
|
||||
else:
|
||||
# Checksum was valid so wait for the terminators
|
||||
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")
|
||||
sync_lost_at = processed
|
||||
resync_in_progress = True
|
||||
ubx_nmea_state = sync_lost
|
||||
else:
|
||||
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")
|
||||
sync_lost_at = processed
|
||||
resync_in_progress = True
|
||||
ubx_nmea_state = sync_lost
|
||||
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
|
||||
# LF was received so go back to looking for B5 or a $
|
||||
ubx_nmea_state = looking_for_B5_dollar
|
||||
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
|
||||
|
||||
# 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))
|
||||
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 len(messages) > 0:
|
||||
print('Message types and totals were:')
|
||||
for key in messages.keys():
|
||||
print('Message type:',key,' Total:',messages[key])
|
||||
if (resyncs > 0):
|
||||
print('Number of successful resyncs:',resyncs)
|
||||
print()
|
||||
if (repairFile):
|
||||
print('Aligned data written to:', repairFilename)
|
||||
print('Largest alignment change:', largest_rawx_alignment)
|
||||
if (resyncs > 0):
|
||||
print('Note: alignment is skipped during resyncing')
|
||||
print('You may need to run the code twice to align any skipped alignments')
|
||||
print()
|
||||
print('Bye!')
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
Callback Example: ESF RAW (100Hz!)
|
||||
By: Paul Clark
|
||||
SparkFun Electronics
|
||||
Date: September 8th, 2022
|
||||
License: MIT. See license file for more information but you can
|
||||
basically do whatever you want with this code.
|
||||
|
||||
This example configures the External Sensor Fusion RAW IMU sensor messages on the NEO-M8U / ZED-F9R and
|
||||
uses callbacks to process and display the ESF data automatically.
|
||||
|
||||
Notes:
|
||||
On the ZED-F9R, each ESF RAW message contains _one_ set of IMU sensor data: seven readings in total (3 x Accel, 3 x Gyro, 1 x Temperature).
|
||||
However, on the NEO-M8U, each message contains _ten_ sets of IMU sensor data, seventy readings in total.
|
||||
The NEO-M8U data is all timestamped and it is possible to reconstruct the full data stream, you just need to do it
|
||||
ten samples at a time...
|
||||
Also, note that the sensor data is 24-bit signed (two's complement). You need to be careful when converting to int32_t.
|
||||
Data will arrive at 100Hz! (10Hz x 10 on the NEO-M8U)
|
||||
400kHz I2C is essential...
|
||||
Serial printing needs to be kept short and the baud rate needs to be at least 230400.
|
||||
|
||||
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
|
||||
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
|
||||
|
||||
Feel like supporting open source hardware?
|
||||
Buy a board from SparkFun!
|
||||
NEO-M8U: https://www.sparkfun.com/products/16329
|
||||
|
||||
Hardware Connections:
|
||||
Plug a Qwiic cable into the GPS and a Redboard Qwiic
|
||||
If you don't have a platform with a Qwiic connection use the
|
||||
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
|
||||
Open the serial monitor at 115200 baud to see the output
|
||||
|
||||
*/
|
||||
|
||||
#include <Wire.h> //Needed for I2C to GPS
|
||||
|
||||
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
|
||||
SFE_UBLOX_GNSS myGNSS;
|
||||
|
||||
// Callback: printESFRAWdata will be called when new ESF RAW data arrives
|
||||
// See u-blox_structs.h for the full definition of UBX_ESF_RAW_data_t
|
||||
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFRAWcallback
|
||||
// / _____ This _must_ be UBX_ESF_RAW_data_t
|
||||
// | / _____ You can use any name you like for the struct
|
||||
// | | /
|
||||
// | | |
|
||||
void printESFRAWdata(UBX_ESF_RAW_data_t *ubxDataStruct)
|
||||
{
|
||||
// ubxDataStruct->numEsfRawBlocks indicates how many sensor readings the UBX_ESF_RAW_data_t contains.
|
||||
// On the ZED-F9R, numEsfRawBlocks will be 7: 3 x Accel, 3 x Gyro, 1 x Temperature.
|
||||
// On the NEO-M8U, numEsfRawBlocks will be 70: 10 sets of sensor data. The sensor time tag (sTag)
|
||||
// indicates the timing of each sample.
|
||||
// Serial output will be approx. 110 bytes depending on how many digits are in the sensor readings.
|
||||
// To keep up, Serial needs to be running at 100k baud minimum. 230400 is recommended.
|
||||
|
||||
uint32_t sTag = 0xFFFFFFFF; // Sensor time tag
|
||||
|
||||
// Only print the first seven sensor readings (on the NEO-M8U)
|
||||
for (uint8_t i = 0; (i < ubxDataStruct->numEsfRawBlocks) && (i < 7); i++)
|
||||
// For fun, and to prove it works, uncomment use this line instead to get the full 100Hz data on the NEO-M8U
|
||||
//for (uint8_t i = 0; i < ubxDataStruct->numEsfRawBlocks; i++)
|
||||
{
|
||||
// Print sTag the first time - and also if it changes
|
||||
if (sTag != ubxDataStruct->data[i].sTag)
|
||||
{
|
||||
sTag = ubxDataStruct->data[i].sTag;
|
||||
Serial.print(F("Time:"));
|
||||
Serial.println(sTag);
|
||||
}
|
||||
|
||||
// Print the sensor data type
|
||||
// From the M8 interface description:
|
||||
// 0: None
|
||||
// 1-4: Reserved
|
||||
// 5: z-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 11: speed m/s * 1e-3 signed
|
||||
// 12: gyroscope temperature deg Celsius * 1e-2 signed
|
||||
// 13: y-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 14: x-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
// 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
// 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
switch (ubxDataStruct->data[i].data.bits.dataType)
|
||||
{
|
||||
case 5:
|
||||
Serial.print(F("Zgyr:"));
|
||||
break;
|
||||
case 12:
|
||||
Serial.print(F("Temp:"));
|
||||
break;
|
||||
case 13:
|
||||
Serial.print(F("Ygyr:"));
|
||||
break;
|
||||
case 14:
|
||||
Serial.print(F("Xgyr:"));
|
||||
break;
|
||||
case 16:
|
||||
Serial.print(F("Xacc:"));
|
||||
break;
|
||||
case 17:
|
||||
Serial.print(F("Yacc:"));
|
||||
break;
|
||||
case 18:
|
||||
Serial.print(F("Zacc:"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Gyro data
|
||||
if ((ubxDataStruct->data[i].data.bits.dataType == 5) || (ubxDataStruct->data[i].data.bits.dataType == 13) || (ubxDataStruct->data[i].data.bits.dataType == 14))
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
rate /= 256.0; // Divide by 256 to undo the shift
|
||||
rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s
|
||||
Serial.println(rate);
|
||||
}
|
||||
// Accelerometer data
|
||||
else if ((ubxDataStruct->data[i].data.bits.dataType == 16) || (ubxDataStruct->data[i].data.bits.dataType == 17) || (ubxDataStruct->data[i].data.bits.dataType == 18))
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float force = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
force /= 256.0; // Divide by 256 to undo the shift
|
||||
force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2
|
||||
Serial.println(force);
|
||||
}
|
||||
// Gyro Temperature
|
||||
else if (ubxDataStruct->data[i].data.bits.dataType == 12)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = ubxDataStruct->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
temperature /= 256.0; // Divide by 256 to undo the shift
|
||||
temperature *= 0.01; // Convert from C * 1e-2 to C
|
||||
Serial.println(temperature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(230400); // <--- Use >> 100k baud (see notes above)
|
||||
|
||||
while (!Serial); //Wait for user to open terminal
|
||||
Serial.println(F("SparkFun u-blox Example"));
|
||||
|
||||
Wire.begin();
|
||||
Wire.setClock(400000); // <-- Use 400kHz I2C (ESSENTIAL)
|
||||
|
||||
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
|
||||
|
||||
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
|
||||
{
|
||||
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
|
||||
while (1);
|
||||
}
|
||||
|
||||
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
|
||||
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
|
||||
|
||||
myGNSS.setI2CpollingWait(5); //Allow checkUblox to poll I2C data every 5ms to keep up with the ESF RAW messages
|
||||
|
||||
if (myGNSS.setAutoESFRAWcallbackPtr(&printESFRAWdata) == true) // Enable automatic ESF RAW messages with callback to printESFRAWdata
|
||||
Serial.println(F("setAutoESFRAWcallback successful"));
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
|
||||
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed.
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
/*
|
||||
u-blox Example: ESF RAW (100Hz!)
|
||||
By: Paul Clark
|
||||
SparkFun Electronics
|
||||
Date: September 8th, 2022
|
||||
License: MIT. See license file for more information but you can
|
||||
basically do whatever you want with this code.
|
||||
|
||||
This example configures the External Sensor Fusion RAW IMU sensor messages on the NEO-M8U / ZED-F9R and
|
||||
shows how to access the ESF data in the loop - without using the callback.
|
||||
|
||||
Notes:
|
||||
On the ZED-F9R, each ESF RAW message contains _one_ set of IMU sensor data: seven readings in total (3 x Accel, 3 x Gyro, 1 x Temperature).
|
||||
However, on the NEO-M8U, each message contains _ten_ sets of IMU sensor data, seventy readings in total.
|
||||
The NEO-M8U data is all timestamped and it is possible to reconstruct the full data stream, you just need to do it
|
||||
ten samples at a time...
|
||||
Also, note that the sensor data is 24-bit signed (two's complement). You need to be careful when converting to int32_t.
|
||||
Data will arrive at 100Hz! (10Hz x 10 on the NEO-M8U)
|
||||
400kHz I2C is essential...
|
||||
Serial printing needs to be kept short and the baud rate needs to be at least 230400.
|
||||
|
||||
Please make sure your NEO-M8U is running UDR firmware >= 1.31. Please update using u-center if necessary:
|
||||
https://www.u-blox.com/en/product/neo-m8u-module#tab-documentation-resources
|
||||
|
||||
Feel like supporting open source hardware?
|
||||
Buy a board from SparkFun!
|
||||
NEO-M8U: https://www.sparkfun.com/products/16329
|
||||
|
||||
Hardware Connections:
|
||||
Plug a Qwiic cable into the GPS and a Redboard Qwiic
|
||||
If you don't have a platform with a Qwiic connection use the
|
||||
SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
|
||||
Open the serial monitor at 115200 baud to see the output
|
||||
|
||||
*/
|
||||
|
||||
#include <Wire.h> //Needed for I2C to GPS
|
||||
|
||||
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
|
||||
SFE_UBLOX_GNSS myGNSS;
|
||||
|
||||
// Callback: printESFRAWdata will be called when new ESF RAW data arrives
|
||||
// See u-blox_structs.h for the full definition of UBX_ESF_RAW_data_t
|
||||
// _____ You can use any name you like for the callback. Use the same name when you call setAutoESFRAWcallback
|
||||
// / _____ This _must_ be UBX_ESF_RAW_data_t
|
||||
// | / _____ You can use any name you like for the struct
|
||||
// | | /
|
||||
// | | |
|
||||
void printESFRAWdata(UBX_ESF_RAW_data_t *ubxDataStruct)
|
||||
{
|
||||
Serial.println(F("Hey! The ESF RAW callback has been called!"));
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(230400); // <--- Use >> 100k baud (see notes above)
|
||||
|
||||
while (!Serial); //Wait for user to open terminal
|
||||
Serial.println(F("SparkFun u-blox Example"));
|
||||
|
||||
Wire.begin();
|
||||
Wire.setClock(400000); // <-- Use 400kHz I2C (ESSENTIAL)
|
||||
|
||||
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
|
||||
|
||||
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
|
||||
{
|
||||
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
|
||||
while (1);
|
||||
}
|
||||
|
||||
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
|
||||
myGNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); //Save (only) the communications port settings to flash and BBR
|
||||
|
||||
myGNSS.setI2CpollingWait(5); //Allow checkUblox to poll I2C data every 5ms to keep up with the ESF RAW messages
|
||||
|
||||
if (myGNSS.setAutoESFRAWcallbackPtr(&printESFRAWdata) == true) // Enable automatic ESF RAW messages with callback to printESFRAWdata
|
||||
Serial.println(F("setAutoESFRAWcallback successful"));
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
myGNSS.checkUblox(); // Check for the arrival of new data and process it.
|
||||
|
||||
// Check if new ESF RAW data has arrived:
|
||||
// If myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid is true, it indicates new ESF RAW data has been received and has been copied.
|
||||
// automaticFlags.flags.bits.callbackCopyValid will be cleared automatically when the callback is called.
|
||||
|
||||
if (myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == true)
|
||||
{
|
||||
// But, we can manually clear the callback flag too. This will prevent the callback from being called!
|
||||
myGNSS.packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid = false; // Comment this line if you still want the callback to be called
|
||||
|
||||
// myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks indicates how many sensor readings the UBX_ESF_RAW_data_t contains.
|
||||
// On the ZED-F9R, numEsfRawBlocks will be 7: 3 x Accel, 3 x Gyro, 1 x Temperature.
|
||||
// On the NEO-M8U, numEsfRawBlocks will be 70: 10 sets of sensor data. The sensor time tag (sTag)
|
||||
// indicates the timing of each sample.
|
||||
// Serial output will be approx. 110 bytes depending on how many digits are in the sensor readings.
|
||||
// To keep up, Serial needs to be running at 100k baud minimum. 230400 is recommended.
|
||||
|
||||
uint32_t sTag = 0xFFFFFFFF; // Sensor time tag
|
||||
|
||||
// Only print the first seven sensor readings (on the NEO-M8U)
|
||||
for (uint8_t i = 0; (i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks) && (i < 7); i++)
|
||||
// For fun, and to prove it works, uncomment use this line instead to get the full 100Hz data on the NEO-M8U
|
||||
//for (uint8_t i = 0; i < myGNSS.packetUBXESFRAW->callbackData->numEsfRawBlocks; i++)
|
||||
{
|
||||
// Print sTag the first time - and also if it changes
|
||||
if (sTag != myGNSS.packetUBXESFRAW->callbackData->data[i].sTag)
|
||||
{
|
||||
sTag = myGNSS.packetUBXESFRAW->callbackData->data[i].sTag;
|
||||
Serial.print(F("Time:"));
|
||||
Serial.println(sTag);
|
||||
}
|
||||
|
||||
// Print the sensor data type
|
||||
// From the M8 interface description:
|
||||
// 0: None
|
||||
// 1-4: Reserved
|
||||
// 5: z-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 6: front-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 7: front-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 8: rear-left wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 9: rear-right wheel ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 10: speed ticks: Bits 0-22: unsigned tick value. Bit 23: direction indicator (0=forward, 1=backward)
|
||||
// 11: speed m/s * 1e-3 signed
|
||||
// 12: gyroscope temperature deg Celsius * 1e-2 signed
|
||||
// 13: y-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 14: x-axis gyroscope angular rate deg/s * 2^-12 signed
|
||||
// 16: x-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
// 17: y-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
// 18: z-axis accelerometer specific force m/s^2 * 2^-10 signed
|
||||
switch (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType)
|
||||
{
|
||||
case 5:
|
||||
Serial.print(F("Zgyr:"));
|
||||
break;
|
||||
case 12:
|
||||
Serial.print(F("Temp:"));
|
||||
break;
|
||||
case 13:
|
||||
Serial.print(F("Ygyr:"));
|
||||
break;
|
||||
case 14:
|
||||
Serial.print(F("Xgyr:"));
|
||||
break;
|
||||
case 16:
|
||||
Serial.print(F("Xacc:"));
|
||||
break;
|
||||
case 17:
|
||||
Serial.print(F("Yacc:"));
|
||||
break;
|
||||
case 18:
|
||||
Serial.print(F("Zacc:"));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Gyro data
|
||||
if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 5) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 13) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 14))
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float rate = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
rate /= 256.0; // Divide by 256 to undo the shift
|
||||
rate *= 0.000244140625; // Convert from deg/s * 2^-12 to deg/s
|
||||
Serial.println(rate);
|
||||
}
|
||||
// Accelerometer data
|
||||
else if ((myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 16) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 17) || (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 18))
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float force = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
force /= 256.0; // Divide by 256 to undo the shift
|
||||
force *= 0.0009765625; // Convert from m/s^2 * 2^-10 to m/s^2
|
||||
Serial.println(force);
|
||||
}
|
||||
// Gyro Temperature
|
||||
else if (myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataType == 12)
|
||||
{
|
||||
union
|
||||
{
|
||||
int32_t signed32;
|
||||
uint32_t unsigned32;
|
||||
} signedUnsigned; // Avoid any ambiguity casting uint32_t to int32_t
|
||||
// The dataField is 24-bit signed, stored in the 24 LSBs of a uint32_t
|
||||
signedUnsigned.unsigned32 = myGNSS.packetUBXESFRAW->callbackData->data[i].data.bits.dataField << 8; // Shift left by 8 bits to correctly align the data
|
||||
float temperature = signedUnsigned.signed32; // Extract the signed data. Convert to float
|
||||
temperature /= 256.0; // Divide by 256 to undo the shift
|
||||
temperature *= 0.01; // Convert from C * 1e-2 to C
|
||||
Serial.println(temperature);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
myGNSS.checkCallbacks(); // Check if any callbacks are waiting to be processed. There will not be any in this example, unless you commented the line above
|
||||
}
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
Get the high precision ECEF coordinates using double
|
||||
By: Paul Clark
|
||||
SparkFun Electronics
|
||||
Date: September 8th, 2022
|
||||
License: MIT. See license file for more information but you can
|
||||
basically do whatever you want with this code.
|
||||
|
||||
This example shows how to read the high-precision ECEF
|
||||
positional solution. Please see below for information about the units.
|
||||
|
||||
** This example will only work correctly on platforms which support 64-bit double **
|
||||
|
||||
Feel like supporting open source hardware?
|
||||
Buy a board from SparkFun!
|
||||
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
|
||||
NEO-M8P RTK: https://www.sparkfun.com/products/15005
|
||||
|
||||
Hardware Connections:
|
||||
Plug a Qwiic cable into the GNSS and (e.g.) a Redboard Artemis https://www.sparkfun.com/products/15444
|
||||
or an Artemis Thing Plus https://www.sparkfun.com/products/15574
|
||||
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
|
||||
Open the serial monitor at 115200 baud to see the output
|
||||
*/
|
||||
|
||||
#include <Wire.h> // Needed for I2C to GNSS
|
||||
|
||||
#define myWire Wire // This will work on the Redboard Artemis and the Artemis Thing Plus using Qwiic
|
||||
//#define myWire Wire1 // Uncomment this line if you are using the extra SCL1/SDA1 pins (D17 and D16) on the Thing Plus
|
||||
|
||||
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
|
||||
SFE_UBLOX_GNSS myGNSS;
|
||||
|
||||
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
while (!Serial); //Wait for user to open terminal
|
||||
|
||||
myWire.begin();
|
||||
|
||||
//myGNSS.enableDebugging(Serial); // Uncomment this line to enable debug messages
|
||||
|
||||
if (myGNSS.begin(myWire) == false) //Connect to the u-blox module using Wire port
|
||||
{
|
||||
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
// Check that this platform supports 64-bit (8 byte) double
|
||||
if (sizeof(double) < 8)
|
||||
{
|
||||
Serial.println(F("Warning! Your platform does not support 64-bit double."));
|
||||
Serial.println(F("The ECEF coordinates will be inaccurate."));
|
||||
}
|
||||
|
||||
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
|
||||
//myGNSS.saveConfiguration(); //Save the current settings to flash and BBR
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
//Query module only every second.
|
||||
//The module only responds when a new position is available.
|
||||
if (millis() - lastTime > 1000)
|
||||
{
|
||||
lastTime = millis(); //Update the timer
|
||||
|
||||
// getHighResECEFX: returns the X coordinate from HPPOSECEF as an int32_t in cm
|
||||
// getHighResECEFXHp: returns the high resolution component of the X coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
|
||||
// getHighResECEFY: returns the Y coordinate from HPPOSECEF as an int32_t in cm
|
||||
// getHighResECEFYHp: returns the high resolution component of the Y coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
|
||||
// getHighResECEFZ: returns the Z coordinate from HPPOSECEF as an int32_t in cm
|
||||
// getHighResECEFZHp: returns the high resolution component of the Z coordinate from HPPOSECEF as an int8_t in mm*10^-1 (0.1mm)
|
||||
// getPositionAccuracy: returns the position accuracy estimate from HPPOSLLH as an uint32_t in mm (note: not 0.1mm)
|
||||
|
||||
// First, let's collect the position data
|
||||
int32_t ECEFX = myGNSS.getHighResECEFX();
|
||||
int8_t ECEFXHp = myGNSS.getHighResECEFXHp();
|
||||
int32_t ECEFY = myGNSS.getHighResECEFY();
|
||||
int8_t ECEFYHp = myGNSS.getHighResECEFYHp();
|
||||
int32_t ECEFZ = myGNSS.getHighResECEFZ();
|
||||
int8_t ECEFZHp = myGNSS.getHighResECEFZHp();
|
||||
uint32_t accuracy = myGNSS.getPositionAccuracy();
|
||||
|
||||
// Defines storage for the ECEF coordinates as double
|
||||
double d_ECEFX;
|
||||
double d_ECEFY;
|
||||
double d_ECEFZ;
|
||||
|
||||
// Assemble the high precision coordinates
|
||||
d_ECEFX = ((double)ECEFX) / 100.0; // Convert from cm to m
|
||||
d_ECEFX += ((double)ECEFXHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
|
||||
d_ECEFY = ((double)ECEFY) / 100.0; // Convert from cm to m
|
||||
d_ECEFY += ((double)ECEFYHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
|
||||
d_ECEFZ = ((double)ECEFZ) / 100.0; // Convert from cm to m
|
||||
d_ECEFZ += ((double)ECEFZHp) / 10000.0; // Now add the high resolution component ( mm * 10^-1 = m * 10^-4 )
|
||||
|
||||
// Print the coordinates with 4 decimal places (0.1mm)
|
||||
Serial.print("X (m): ");
|
||||
Serial.print(d_ECEFX, 4);
|
||||
Serial.print(", Y (m): ");
|
||||
Serial.print(d_ECEFY, 4);
|
||||
Serial.print(", Z (m): ");
|
||||
Serial.print(d_ECEFZ, 4);
|
||||
|
||||
// Now define float storage for the accuracy
|
||||
float f_accuracy;
|
||||
|
||||
// Convert the horizontal accuracy (mm) to a float
|
||||
f_accuracy = accuracy;
|
||||
// Now convert to m
|
||||
f_accuracy = f_accuracy / 1000.0; // Convert from mm to m
|
||||
|
||||
// Finally, do the printing
|
||||
Serial.print(", Accuracy (m): ");
|
||||
Serial.println(f_accuracy, 3); // Print the accuracy with 3 decimal places
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -484,8 +484,6 @@ assumeAutoESFMEAS KEYWORD2
|
||||
flushESFMEAS KEYWORD2
|
||||
logESFMEAS KEYWORD2
|
||||
|
||||
getEsfRawDataInfo KEYWORD2
|
||||
getESFRAW KEYWORD2
|
||||
setAutoESFRAW KEYWORD2
|
||||
setAutoESFRAWrate KEYWORD2
|
||||
setAutoESFRAWcallback KEYWORD2
|
||||
@@ -585,6 +583,12 @@ getMagAcc KEYWORD2
|
||||
getHeadVehValid KEYWORD2
|
||||
|
||||
getPositionAccuracy KEYWORD2
|
||||
getHighResECEFX KEYWORD2
|
||||
getHighResECEFY KEYWORD2
|
||||
getHighResECEFZ KEYWORD2
|
||||
getHighResECEFXHp KEYWORD2
|
||||
getHighResECEFYHp KEYWORD2
|
||||
getHighResECEFZHp KEYWORD2
|
||||
|
||||
getTimeOfWeekFromHPPOSLLH KEYWORD2
|
||||
getHighResLongitude KEYWORD2
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
name=SparkFun u-blox GNSS Arduino Library
|
||||
version=2.2.13
|
||||
version=2.2.14
|
||||
author=SparkFun Electronics <techsupport@sparkfun.com>
|
||||
maintainer=SparkFun Electronics <sparkfun.com>
|
||||
sentence=Library for I2C, Serial and SPI Communication with u-blox GNSS modules<br/><br/>
|
||||
|
||||
@@ -4246,15 +4246,13 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
|
||||
// Parse various byte fields into storage - but only if we have memory allocated for it
|
||||
if (packetUBXESFRAW != NULL)
|
||||
{
|
||||
for (uint16_t i = 0; (i < DEF_NUM_SENS) && ((i * 8) < (msg->len - 4)); i++)
|
||||
packetUBXESFRAW->data.numEsfRawBlocks = (msg->len - 4) / 8; // Record how many blocks were received. Could be 7 or 70 (ZED-F9R vs. NEO-M8U)
|
||||
for (uint16_t i = 0; (i < (DEF_NUM_SENS * DEF_MAX_NUM_ESF_RAW_REPEATS)) && ((i * 8) < (msg->len - 4)); i++)
|
||||
{
|
||||
packetUBXESFRAW->data.data[i].data.all = extractLong(msg, 4 + (i * 8));
|
||||
packetUBXESFRAW->data.data[i].sTag = extractLong(msg, 8 + (i * 8));
|
||||
}
|
||||
|
||||
// Mark all datums as fresh (not read before)
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.all = 0xFFFFFFFF;
|
||||
|
||||
// Check if we need to copy the data for the callback
|
||||
if ((packetUBXESFRAW->callbackData != NULL) // If RAM has been allocated for the copy of the data
|
||||
&& (packetUBXESFRAW->automaticFlags.flags.bits.callbackCopyValid == false)) // AND the data is stale
|
||||
@@ -10233,7 +10231,7 @@ bool SFE_UBLOX_GNSS::getVehAtt(uint16_t maxWait)
|
||||
bool SFE_UBLOX_GNSS::getNAVATT(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVATT == NULL)
|
||||
initPacketUBXNAVATT(); // Check that RAM has been allocated for the ESF RAW data
|
||||
initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data
|
||||
if (packetUBXNAVATT == NULL) // Only attempt this if RAM allocation was successful
|
||||
return false;
|
||||
|
||||
@@ -10373,7 +10371,7 @@ bool SFE_UBLOX_GNSS::setAutoNAVATTcallbackPtr(void (*callbackPointerPtr)(UBX_NAV
|
||||
bool SFE_UBLOX_GNSS::assumeAutoNAVATT(bool enabled, bool implicitUpdate)
|
||||
{
|
||||
if (packetUBXNAVATT == NULL)
|
||||
initPacketUBXNAVATT(); // Check that RAM has been allocated for the ESF RAW data
|
||||
initPacketUBXNAVATT(); // Check that RAM has been allocated for the NAV ATT data
|
||||
if (packetUBXNAVATT == NULL) // Only attempt this if RAM allocation was successful
|
||||
return false;
|
||||
|
||||
@@ -14738,92 +14736,23 @@ void SFE_UBLOX_GNSS::logESFMEAS(bool enabled)
|
||||
|
||||
// ***** ESF RAW automatic support
|
||||
|
||||
bool SFE_UBLOX_GNSS::getEsfRawDataInfo(uint16_t maxWait)
|
||||
{
|
||||
return (getESFRAW(maxWait));
|
||||
}
|
||||
// ESF RAW messages are output only. They cannot be polled.
|
||||
|
||||
bool SFE_UBLOX_GNSS::getESFRAW(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXESFRAW == NULL)
|
||||
initPacketUBXESFRAW(); // Check that RAM has been allocated for the ESF RAW data
|
||||
if (packetUBXESFRAW == NULL) // Only attempt this if RAM allocation was successful
|
||||
return false;
|
||||
|
||||
if (packetUBXESFRAW->automaticFlags.flags.bits.automatic && packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate)
|
||||
{
|
||||
// The GPS is automatically reporting, we just check whether we got unread data
|
||||
// if (_printDebug == true)
|
||||
// {
|
||||
// _debugSerial->println(F("getEsfRawDataInfo: Autoreporting"));
|
||||
// }
|
||||
checkUbloxInternal(&packetCfg, UBX_CLASS_ESF, UBX_ESF_RAW);
|
||||
return packetUBXESFRAW->moduleQueried.moduleQueried.bits.all;
|
||||
}
|
||||
else if (packetUBXESFRAW->automaticFlags.flags.bits.automatic && !packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate)
|
||||
{
|
||||
// Someone else has to call checkUblox for us...
|
||||
// if (_printDebug == true)
|
||||
// {
|
||||
// _debugSerial->println(F("getEsfRawDataInfo: Exit immediately"));
|
||||
// }
|
||||
return (false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// if (_printDebug == true)
|
||||
// {
|
||||
// _debugSerial->println(F("getEsfRawDataInfo: Polling"));
|
||||
// }
|
||||
|
||||
// The GPS is not automatically reporting HNR PVT so we have to poll explicitly
|
||||
packetCfg.cls = UBX_CLASS_ESF;
|
||||
packetCfg.id = UBX_ESF_RAW;
|
||||
packetCfg.len = 0;
|
||||
packetCfg.startingSpot = 0;
|
||||
|
||||
// The data is parsed as part of processing the response
|
||||
sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait);
|
||||
|
||||
if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED)
|
||||
return (true);
|
||||
|
||||
if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN)
|
||||
{
|
||||
// if (_printDebug == true)
|
||||
// {
|
||||
// _debugSerial->println(F("getEsfRawDataInfo: data in packetCfg was OVERWRITTEN by another message (but that's OK)"));
|
||||
// }
|
||||
return (true);
|
||||
}
|
||||
|
||||
// if (_printDebug == true)
|
||||
// {
|
||||
// _debugSerial->print(F("getEsfRawDataInfo retVal: "));
|
||||
// _debugSerial->println(statusString(retVal));
|
||||
// }
|
||||
return (false);
|
||||
}
|
||||
|
||||
return (false); // Trap. We should never get here...
|
||||
}
|
||||
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS. This changes the way getESFRawDataInfo
|
||||
// works.
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS.
|
||||
bool SFE_UBLOX_GNSS::setAutoESFRAW(bool enable, uint16_t maxWait)
|
||||
{
|
||||
return setAutoESFRAWrate(enable ? 1 : 0, true, maxWait);
|
||||
}
|
||||
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS. This changes the way getESFRawDataInfo
|
||||
// works.
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS.
|
||||
bool SFE_UBLOX_GNSS::setAutoESFRAW(bool enable, bool implicitUpdate, uint16_t maxWait)
|
||||
{
|
||||
return setAutoESFRAWrate(enable ? 1 : 0, implicitUpdate, maxWait);
|
||||
}
|
||||
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS. This changes the way getESFRawDataInfo
|
||||
// works.
|
||||
// Enable or disable automatic ESF RAW message generation by the GNSS.
|
||||
// Note: this function can only be used to enable or disable the messages. A rate of zero disables the messages.
|
||||
// A rate of 1 or more causes the messages to be generated at the full 100Hz.
|
||||
bool SFE_UBLOX_GNSS::setAutoESFRAWrate(uint8_t rate, bool implicitUpdate, uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXESFRAW == NULL)
|
||||
@@ -14848,11 +14777,10 @@ bool SFE_UBLOX_GNSS::setAutoESFRAWrate(uint8_t rate, bool implicitUpdate, uint16
|
||||
packetUBXESFRAW->automaticFlags.flags.bits.automatic = (rate > 0);
|
||||
packetUBXESFRAW->automaticFlags.flags.bits.implicitUpdate = implicitUpdate;
|
||||
}
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.bits.all = false; // Mark data as stale
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Enable automatic navigation message generation by the GNSS.
|
||||
// Enable automatic message generation by the GNSS.
|
||||
bool SFE_UBLOX_GNSS::setAutoESFRAWcallback(void (*callbackPointer)(UBX_ESF_RAW_data_t), uint16_t maxWait)
|
||||
{
|
||||
// Enable auto messages. Set implicitUpdate to false as we expect the user to call checkUblox manually.
|
||||
@@ -14937,7 +14865,6 @@ bool SFE_UBLOX_GNSS::initPacketUBXESFRAW()
|
||||
packetUBXESFRAW->callbackPointer = NULL;
|
||||
packetUBXESFRAW->callbackPointerPtr = NULL;
|
||||
packetUBXESFRAW->callbackData = NULL;
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.all = 0;
|
||||
return (true);
|
||||
}
|
||||
|
||||
@@ -14946,7 +14873,6 @@ void SFE_UBLOX_GNSS::flushESFRAW()
|
||||
{
|
||||
if (packetUBXESFRAW == NULL)
|
||||
return; // Bail if RAM has not been allocated (otherwise we could be writing anywhere!)
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.all = 0; // Mark all datums as stale (read before)
|
||||
}
|
||||
|
||||
// Log this data in file buffer
|
||||
@@ -17527,6 +17453,108 @@ uint32_t SFE_UBLOX_GNSS::getPositionAccuracy(uint16_t maxWait)
|
||||
return (tempAccuracy);
|
||||
}
|
||||
|
||||
// Get the current 3D high precision X coordinate
|
||||
// Returns a long representing the coordinate in cm
|
||||
int32_t SFE_UBLOX_GNSS::getHighResECEFX(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefX == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefX = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefX);
|
||||
}
|
||||
|
||||
// Get the current 3D high precision Y coordinate
|
||||
// Returns a long representing the coordinate in cm
|
||||
int32_t SFE_UBLOX_GNSS::getHighResECEFY(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefY == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefY = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefY);
|
||||
}
|
||||
|
||||
// Get the current 3D high precision Z coordinate
|
||||
// Returns a long representing the coordinate in cm
|
||||
int32_t SFE_UBLOX_GNSS::getHighResECEFZ(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZ == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZ = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefZ);
|
||||
}
|
||||
|
||||
// Get the high precision component of the ECEF X coordinate
|
||||
// Returns a signed byte representing the component as 0.1*mm
|
||||
int8_t SFE_UBLOX_GNSS::getHighResECEFXHp(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefXHp == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefXHp = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefXHp);
|
||||
}
|
||||
|
||||
// Get the high precision component of the ECEF Y coordinate
|
||||
// Returns a signed byte representing the component as 0.1*mm
|
||||
int8_t SFE_UBLOX_GNSS::getHighResECEFYHp(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefYHp == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefYHp = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefYHp);
|
||||
}
|
||||
|
||||
// Get the high precision component of the ECEF Z coordinate
|
||||
// Returns a signed byte representing the component as 0.1*mm
|
||||
int8_t SFE_UBLOX_GNSS::getHighResECEFZHp(uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXNAVHPPOSECEF == NULL)
|
||||
initPacketUBXNAVHPPOSECEF(); // Check that RAM has been allocated for the HPPOSECEF data
|
||||
if (packetUBXNAVHPPOSECEF == NULL) // Bail if the RAM allocation failed
|
||||
return 0;
|
||||
|
||||
if (packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZHp == false)
|
||||
getNAVHPPOSECEF(maxWait);
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.ecefZHp = false; // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXNAVHPPOSECEF->moduleQueried.moduleQueried.bits.all = false;
|
||||
|
||||
return (packetUBXNAVHPPOSECEF->data.ecefZHp);
|
||||
}
|
||||
|
||||
// ***** HPPOSLLH Helper Functions
|
||||
|
||||
uint32_t SFE_UBLOX_GNSS::getTimeOfWeekFromHPPOSLLH(uint16_t maxWait)
|
||||
@@ -18039,22 +18067,6 @@ bool SFE_UBLOX_GNSS::getSensorFusionMeasurement(UBX_ESF_MEAS_sensorData_t *senso
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool SFE_UBLOX_GNSS::getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorData, uint8_t sensor, uint16_t maxWait)
|
||||
{
|
||||
if (packetUBXESFRAW == NULL)
|
||||
initPacketUBXESFRAW(); // Check that RAM has been allocated for the ESF RAW data
|
||||
if (packetUBXESFRAW == NULL) // Bail if the RAM allocation failed
|
||||
return (false);
|
||||
|
||||
if ((packetUBXESFRAW->moduleQueried.moduleQueried.bits.data & (1 << sensor)) == 0)
|
||||
getESFRAW(maxWait);
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.bits.data &= ~(1 << sensor); // Since we are about to give this to user, mark this data as stale
|
||||
packetUBXESFRAW->moduleQueried.moduleQueried.bits.all = false;
|
||||
sensorData->data.all = packetUBXESFRAW->data.data[sensor].data.all;
|
||||
sensorData->sTag = packetUBXESFRAW->data.data[sensor].sTag;
|
||||
return (true);
|
||||
}
|
||||
|
||||
bool SFE_UBLOX_GNSS::getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorData, UBX_ESF_RAW_data_t ubxDataStruct, uint8_t sensor)
|
||||
{
|
||||
sensorData->data.all = ubxDataStruct.data[sensor].data.all;
|
||||
|
||||
@@ -1283,8 +1283,6 @@ public:
|
||||
void flushESFMEAS(); // Mark all the data as read/stale
|
||||
void logESFMEAS(bool enabled = true); // Log data to file buffer
|
||||
|
||||
bool getEsfRawDataInfo(uint16_t maxWait = defaultMaxWait); // ESF RAW Helper
|
||||
bool getESFRAW(uint16_t maxWait = defaultMaxWait); // ESF RAW
|
||||
bool setAutoESFRAW(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic ESF RAW reports
|
||||
bool setAutoESFRAW(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic ESF RAW reports, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
|
||||
bool setAutoESFRAWrate(uint8_t rate, bool implicitUpdate = true, uint16_t maxWait = defaultMaxWait); // Set the rate for automatic RAW reports
|
||||
@@ -1408,6 +1406,12 @@ public:
|
||||
// Helper functions for HPPOSECEF
|
||||
|
||||
uint32_t getPositionAccuracy(uint16_t maxWait = defaultMaxWait); // Returns the 3D accuracy of the current high-precision fix, in mm. Supported on NEO-M8P, ZED-F9P,
|
||||
int32_t getHighResECEFX(uint16_t maxWait = defaultMaxWait); // Returns the ECEF X coordinate (cm)
|
||||
int32_t getHighResECEFY(uint16_t maxWait = defaultMaxWait); // Returns the ECEF Y coordinate (cm)
|
||||
int32_t getHighResECEFZ(uint16_t maxWait = defaultMaxWait); // Returns the ECEF Z coordinate (cm)
|
||||
int8_t getHighResECEFXHp(uint16_t maxWait = defaultMaxWait); // Returns the ECEF X coordinate High Precision Component (0.1 mm)
|
||||
int8_t getHighResECEFYHp(uint16_t maxWait = defaultMaxWait); // Returns the ECEF Y coordinate High Precision Component (0.1 mm)
|
||||
int8_t getHighResECEFZHp(uint16_t maxWait = defaultMaxWait); // Returns the ECEF Z coordinate High Precision Component (0.1 mm)
|
||||
|
||||
// Helper functions for HPPOSLLH
|
||||
|
||||
@@ -1464,7 +1468,6 @@ public:
|
||||
float getESFyaw(uint16_t maxWait = defaultMaxWait); // Returned as degrees
|
||||
bool getSensorFusionMeasurement(UBX_ESF_MEAS_sensorData_t *sensorData, uint8_t sensor, uint16_t maxWait = defaultMaxWait);
|
||||
bool getSensorFusionMeasurement(UBX_ESF_MEAS_sensorData_t *sensorData, UBX_ESF_MEAS_data_t ubxDataStruct, uint8_t sensor);
|
||||
bool getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorData, uint8_t sensor, uint16_t maxWait = defaultMaxWait);
|
||||
bool getRawSensorMeasurement(UBX_ESF_RAW_sensorData_t *sensorData, UBX_ESF_RAW_data_t ubxDataStruct, uint8_t sensor);
|
||||
bool getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensorStatus, uint8_t sensor, uint16_t maxWait = defaultMaxWait);
|
||||
bool getSensorFusionStatus(UBX_ESF_STATUS_sensorStatus_t *sensorStatus, UBX_ESF_STATUS_data_t ubxDataStruct, uint8_t sensor);
|
||||
|
||||
+10
-17
@@ -49,6 +49,10 @@
|
||||
#define DEF_NUM_SENS 7 // The maximum number of ESF sensors
|
||||
#endif
|
||||
|
||||
#ifndef DEF_MAX_NUM_ESF_RAW_REPEATS
|
||||
#define DEF_MAX_NUM_ESF_RAW_REPEATS 10 // The NEO-M8U sends ESF RAW data in blocks / sets of ten readings. (The ZED-F9R sends them one at a time.)
|
||||
#endif
|
||||
|
||||
// Additional flags and pointers that need to be stored with each message type
|
||||
struct ubxAutomaticFlags
|
||||
{
|
||||
@@ -2257,7 +2261,10 @@ typedef struct
|
||||
|
||||
// UBX-ESF-RAW (0x10 0x03): Raw sensor measurements
|
||||
// Note: length is variable
|
||||
const uint16_t UBX_ESF_RAW_MAX_LEN = 4 + (8 * DEF_NUM_SENS);
|
||||
// Note: The ZED-F9R sends sets of seven sensor readings one at a time
|
||||
// But the NEO-M8U sends them in sets of ten (i.e. seventy readings per message)
|
||||
// Note: ESF RAW data cannot be polled. It is "Output" only
|
||||
const uint16_t UBX_ESF_RAW_MAX_LEN = 4 + (8 * DEF_NUM_SENS * DEF_MAX_NUM_ESF_RAW_REPEATS);
|
||||
|
||||
typedef struct
|
||||
{
|
||||
@@ -2276,28 +2283,14 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reserved1[4];
|
||||
UBX_ESF_RAW_sensorData_t data[DEF_NUM_SENS];
|
||||
UBX_ESF_RAW_sensorData_t data[DEF_NUM_SENS * DEF_MAX_NUM_ESF_RAW_REPEATS];
|
||||
uint8_t numEsfRawBlocks; // Note: this is not contained in the ESF RAW message. It is calculated from the message length.
|
||||
} UBX_ESF_RAW_data_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
union
|
||||
{
|
||||
uint32_t all;
|
||||
struct
|
||||
{
|
||||
uint32_t all : 1;
|
||||
|
||||
uint32_t data : DEF_NUM_SENS;
|
||||
} bits;
|
||||
} moduleQueried;
|
||||
} UBX_ESF_RAW_moduleQueried_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
ubxAutomaticFlags automaticFlags;
|
||||
UBX_ESF_RAW_data_t data;
|
||||
UBX_ESF_RAW_moduleQueried_t moduleQueried;
|
||||
void (*callbackPointer)(UBX_ESF_RAW_data_t);
|
||||
void (*callbackPointerPtr)(UBX_ESF_RAW_data_t *);
|
||||
UBX_ESF_RAW_data_t *callbackData;
|
||||
|
||||
Reference in New Issue
Block a user