Skip to content

Commit f1bebf3

Browse files
committed
Add support for RTCM 1005 - work in progress
1 parent 957f1ac commit f1bebf3

File tree

4 files changed

+374
-2
lines changed

4 files changed

+374
-2
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
Get the RTCM 1005 sentence using getLatestRTCM1005
3+
By: Paul Clark
4+
SparkFun Electronics
5+
Date: May 4th, 2023
6+
License: MIT. See license file for more information.
7+
8+
This example shows how to turn on/off the RTCM sentences being output over I2C.
9+
It then demonstrates how to use the new getLatestRTCM1005 function to retrieve the latest RTCM 1005 message.
10+
getLatestRTCM1005 returns immediately - it is not blocking.
11+
It returns:
12+
0 if no data is available
13+
1 if the data is valid but is stale (you have read it before)
14+
2 if the data is valid and fresh
15+
16+
Feel like supporting open source hardware?
17+
Buy a board from SparkFun!
18+
SparkFun GPS-RTK2 - ZED-F9P (GPS-15136) https://www.sparkfun.com/products/15136
19+
SparkFun GPS-RTK-SMA - ZED-F9P (GPS-16481) https://www.sparkfun.com/products/16481
20+
21+
Hardware Connections:
22+
Plug a Qwiic cable into the GNSS and a RedBoard
23+
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
24+
Open the serial monitor at 115200 baud to see the output
25+
*/
26+
27+
#include <Wire.h> //Needed for I2C to GNSS
28+
29+
#include <SparkFun_u-blox_GNSS_v3.h> //Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS_v3
30+
SFE_UBLOX_GNSS myGNSS;
31+
32+
void setup()
33+
{
34+
35+
Serial.begin(115200);
36+
Serial.println(F("SparkFun u-blox GNSS Example"));
37+
38+
Wire.begin();
39+
40+
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
41+
42+
while (myGNSS.begin() == false)
43+
{
44+
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring."));
45+
delay(1000);
46+
}
47+
48+
//Disable or enable various RTCM sentences over the I2C interface
49+
myGNSS.setI2COutput(COM_TYPE_NMEA | COM_TYPE_UBX | COM_TYPE_RTCM3); // Turn on UBX, NMEA and RTCM sentences on I2C
50+
myGNSS.newCfgValset(VAL_LAYER_RAM_BBR); // Use cfgValset to disable / enable individual RTCM messages
51+
myGNSS.addCfgValset(UBLOX_CFG_MSGOUT_RTCM_3X_TYPE1005_I2C, 1); // Enable RTCM 1005 at current navigation rate
52+
if (myGNSS.sendCfgValset()) // Send the configuration VALSET
53+
Serial.println(F("RTCM messages were configured successfully"));
54+
else
55+
Serial.println(F("RTCM message configuration failed!"));
56+
57+
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save only the ioPort and message settings to NVM
58+
59+
//myGNSS.setRTCMOutputPort(Serial); // Uncomment this line to echo all RTCM data to Serial for debugging
60+
61+
if (sizeof(double) != 8) // Check double is 64-bit
62+
Serial.println(F("double is not 64-bit. ECEF resolution may be limited!"));
63+
}
64+
65+
void loop()
66+
{
67+
// getLatestRTCM1005 calls checkUblox for us. We don't need to do it here
68+
69+
RTCM_1005_data_t data; // Storage for the RTCM 1005 data
70+
uint8_t result = myGNSS.getLatestRTCM1005(&data); // Get the latest RTCM 1005 data (if any)
71+
72+
if (result == 0)
73+
{
74+
Serial.println(F("No RTCM 1005 data available"));
75+
}
76+
else if (result == 1)
77+
{
78+
Serial.println(F("RTCM 1005 data is available but is stale"));
79+
}
80+
else // if (result == 2)
81+
{
82+
double x = data.AntennaReferencePointECEFX;
83+
x /= 10000.0; // Convert to m
84+
double y = data.AntennaReferencePointECEFY;
85+
y /= 10000.0; // Convert to m
86+
double z = data.AntennaReferencePointECEFZ;
87+
z /= 10000.0; // Convert to m
88+
89+
Serial.print(F("Latest RTCM 1005: ARP ECEF-X: "));
90+
Serial.print(x, 4); // 4 decimal places
91+
Serial.print(F(" Y: "));
92+
Serial.print(y, 4); // 4 decimal places
93+
Serial.print(F(" Z: "));
94+
Serial.println(z, 4); // 4 decimal places
95+
}
96+
97+
delay(250);
98+
}

src/u-blox_GNSS.cpp

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -622,6 +622,16 @@ void DevUBLOXGNSS::end(void)
622622
}
623623
#endif
624624

625+
if (storageRTCM1005 != nullptr)
626+
{
627+
if (storageRTCM1005->callbackData != nullptr)
628+
{
629+
delete storageRTCM1005->callbackData;
630+
}
631+
delete storageRTCM1005;
632+
storageRTCM1005 = nullptr;
633+
}
634+
625635
if (sfe_ublox_ubx_logging_list_head != nullptr)
626636
{
627637
while (sfe_ublox_ubx_logging_list_head->next != nullptr)
@@ -2168,6 +2178,35 @@ void DevUBLOXGNSS::process(uint8_t incoming, ubxPacket *incomingUBX, uint8_t req
21682178
// If there is space in the RTCM buffer, store the data there too
21692179
if (rtcmBufferSpaceAvailable() >= _storageRTCM->messageLength + 6)
21702180
storeRTCMBytes(_storageRTCM->dataMessage, _storageRTCM->messageLength + 6);
2181+
2182+
// Check "Auto" RTCM
2183+
if ((messageType == 1005) && (_storageRTCM->messageLength == RTCM_1005_MSG_LEN_BYTES) && (storageRTCM1005 != nullptr))
2184+
{
2185+
storageRTCM1005->data.MessageNumber = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 0, 12);
2186+
storageRTCM1005->data.ReferenceStationID = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 12, 12);
2187+
storageRTCM1005->data.ITRFRealizationYear = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 24, 6);
2188+
storageRTCM1005->data.GPSIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 30, 1);
2189+
storageRTCM1005->data.GLONASSIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 31, 1);
2190+
storageRTCM1005->data.GalileoIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 32, 1);
2191+
storageRTCM1005->data.ReferenceStationIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 33, 1);
2192+
storageRTCM1005->data.AntennaReferencePointECEFX = extractSignedBits64(&_storageRTCM->dataMessage[3], 34, 38);
2193+
storageRTCM1005->data.SingleReceiverOscillatorIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 72, 1);
2194+
storageRTCM1005->data.Reserved = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 73, 1);
2195+
storageRTCM1005->data.AntennaReferencePointECEFY = extractSignedBits64(&_storageRTCM->dataMessage[3], 74, 38);
2196+
storageRTCM1005->data.QuarterCycleIndicator = extractUnsignedBits16(&_storageRTCM->dataMessage[3], 112, 2);
2197+
storageRTCM1005->data.AntennaReferencePointECEFY = extractSignedBits64(&_storageRTCM->dataMessage[3], 114, 38);
2198+
2199+
storageRTCM1005->automaticFlags.flags.bits.dataValid = 1; // Mark the data as valid and unread
2200+
storageRTCM1005->automaticFlags.flags.bits.dataRead = 0;
2201+
2202+
if (storageRTCM1005->callbackData != nullptr) // Should we copy the data for the callback?
2203+
if (storageRTCM1005->callbackPointerPtr != nullptr) // Has the callback been defined?
2204+
if (storageRTCM1005->automaticFlags.flags.bits.callbackDataValid == 0) // Only overwrite the callback copy if it has been read
2205+
{
2206+
memcpy(storageRTCM1005->callbackData, &_storageRTCM->dataMessage[3], RTCM_1005_MSG_LEN_BYTES);
2207+
storageRTCM1005->automaticFlags.flags.bits.callbackDataValid = 1;
2208+
}
2209+
}
21712210
}
21722211
else
21732212
{
@@ -15706,6 +15745,83 @@ void DevUBLOXGNSS::crc24q(uint8_t incoming, uint32_t *checksum)
1570615745

1570715746
#endif
1570815747

15748+
// Return the most recent RTCM 1005: 0 = no data, 1 = stale data, 2 = fresh data
15749+
uint8_t DevUBLOXGNSS::getLatestRTCM1005(RTCM_1005_data_t *data)
15750+
{
15751+
if (!initStorageRTCM())
15752+
return 0;
15753+
if (storageRTCM1005 == nullptr)
15754+
initStorageRTCM1005(); // Check that RAM has been allocated for the message
15755+
if (storageRTCM1005 == nullptr) // Bail if the RAM allocation failed
15756+
return 0;
15757+
15758+
checkUbloxInternal(&packetCfg, 0, 0); // Call checkUbloxInternal to parse any incoming data. Don't overwrite the requested Class and ID
15759+
15760+
uint8_t result = 0;
15761+
if (storageRTCM1005->automaticFlags.flags.bits.dataValid == 1) // Is the copy valid?
15762+
{
15763+
result = 1;
15764+
if (storageRTCM1005->automaticFlags.flags.bits.dataRead == 0) // Has the data already been read?
15765+
{
15766+
result = 2;
15767+
storageRTCM1005->automaticFlags.flags.bits.dataRead = 1; // Mark the data as read
15768+
}
15769+
}
15770+
15771+
return (result);
15772+
}
15773+
15774+
bool DevUBLOXGNSS::setRTCM1005callbackPtr(void (*callbackPointerPtr)(RTCM_1005_data_t *))
15775+
{
15776+
if (!initStorageRTCM())
15777+
return false;
15778+
if (storageRTCM1005 == nullptr)
15779+
initStorageRTCM1005(); // Check that RAM has been allocated for the message
15780+
if (storageRTCM1005 == nullptr) // Bail if the RAM allocation failed
15781+
return (false);
15782+
15783+
if (storageRTCM1005->callbackData == nullptr) // Check if RAM has been allocated for the callback copy
15784+
{
15785+
storageRTCM1005->callbackData = new RTCM_1005_data_t;
15786+
}
15787+
15788+
if (storageRTCM1005->callbackData == nullptr)
15789+
{
15790+
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
15791+
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
15792+
_debugSerial.println(F("setRTCM1005callbackPtr: RAM alloc failed!"));
15793+
#endif
15794+
return (false);
15795+
}
15796+
15797+
storageRTCM1005->callbackPointerPtr = callbackPointerPtr;
15798+
return (true);
15799+
}
15800+
15801+
// Private: allocate RAM for incoming RTCM 1005 messages and initialize it
15802+
bool DevUBLOXGNSS::initStorageRTCM1005()
15803+
{
15804+
if (storageRTCM1005 != nullptr) // Check if storage already exists
15805+
return true;
15806+
15807+
storageRTCM1005 = new RTCM_1005_t; // Allocate RAM for the main struct
15808+
if (storageRTCM1005 == nullptr)
15809+
{
15810+
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
15811+
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
15812+
_debugSerial.println(F("initStorageRTCM1005: RAM alloc failed!"));
15813+
#endif
15814+
return (false);
15815+
}
15816+
15817+
storageRTCM1005->callbackPointerPtr = nullptr; // Clear the callback pointers
15818+
storageRTCM1005->callbackData = nullptr;
15819+
15820+
storageRTCM1005->automaticFlags.flags.all = 0; // Mark the data as invalid/stale and unread
15821+
15822+
return (true);
15823+
}
15824+
1570915825
// ***** CFG RATE Helper Functions
1571015826

1571115827
// Set the rate at which the module will give us an updated navigation solution
@@ -17560,3 +17676,104 @@ double DevUBLOXGNSS::extractDouble(ubxPacket *msg, uint16_t spotToStart)
1756017676
converter.unsigned64 = extractLongLong(msg, spotToStart);
1756117677
return (converter.dbl);
1756217678
}
17679+
17680+
// Given a pointer, extract an unsigned integer, starting at bit start with width
17681+
uint16_t DevUBLOXGNSS::extractUnsignedBits16(uint8_t *ptr, uint16_t start, uint16_t width)
17682+
{
17683+
uint16_t result = 0;
17684+
uint16_t count = 0;
17685+
uint8_t bitMask = 0x80;
17686+
17687+
// Loop until we reach the start bit
17688+
while (count < start)
17689+
{
17690+
bitMask >> 1; // Shift the bit mask
17691+
count++; // Increment the count
17692+
if (bitMask == 0) // Have we counted 8 bits?
17693+
{
17694+
ptr++; // Point to the next byte
17695+
bitMask = 0x80; // Reset the bit mask
17696+
}
17697+
}
17698+
17699+
// We have reached the start bit and ptr is pointing at the correct byte
17700+
// Now extract width bytes, incrementing ptr and shifting bitMask as we go
17701+
while (count < (start + width))
17702+
{
17703+
if (*ptr & bitMask) // Is the bit set?
17704+
result |= 1; // Set the corresponding bit in result
17705+
17706+
bitMask >> 1; // Shift the bit mask
17707+
count++; // Increment the count
17708+
17709+
if (bitMask == 0) // Have we counted 8 bits?
17710+
{
17711+
ptr++; // Point to the next byte
17712+
bitMask = 0x80; // Reset the bit mask
17713+
}
17714+
17715+
if (count < (start + width)) // Do we need to shift result?
17716+
result <<= 1; // Shift the result
17717+
}
17718+
17719+
return result;
17720+
}
17721+
17722+
// Given a pointer, extract a signed integer, starting at bit start with width
17723+
int64_t DevUBLOXGNSS::extractSignedBits64(uint8_t *ptr, uint16_t start, uint16_t width)
17724+
{
17725+
17726+
unsignedSigned64 result;
17727+
result.unsigned64 = 0;
17728+
17729+
unsignedSigned64 twosComplement;
17730+
twosComplement.unsigned64 = 0xFFFFFFFFFFFFFFFF;
17731+
17732+
bool isNegative;
17733+
17734+
uint16_t count = 0;
17735+
uint8_t bitMask = 0x80;
17736+
17737+
// Loop until we reach the start bit
17738+
while (count < start)
17739+
{
17740+
bitMask >> 1; // Shift the bit mask
17741+
count++; // Increment the count
17742+
if (bitMask == 0) // Have we counted 8 bits?
17743+
{
17744+
ptr++; // Point to the next byte
17745+
bitMask = 0x80; // Reset the bit mask
17746+
}
17747+
}
17748+
17749+
isNegative = *ptr & bitMask; // Record the first bit - indicates in the number is negative
17750+
17751+
// We have reached the start bit and ptr is pointing at the correct byte
17752+
// Now extract width bytes, incrementing ptr and shifting bitMask as we go
17753+
while (count < (start + width))
17754+
{
17755+
if (*ptr & bitMask) // Is the bit set?
17756+
result.unsigned64 |= 1; // Set the corresponding bit in result
17757+
17758+
bitMask >> 1; // Shift the bit mask
17759+
count++; // Increment the count
17760+
twosComplement.unsigned64 <<= 1; // Shift the two's complement mask
17761+
17762+
if (bitMask == 0) // Have we counted 8 bits?
17763+
{
17764+
ptr++; // Point to the next byte
17765+
bitMask = 0x80; // Reset the bit mask
17766+
}
17767+
17768+
if (count < (start + width)) // Do we need to shift result?
17769+
result.unsigned64 <<= 1; // Shift the result
17770+
}
17771+
17772+
// Handle negative number
17773+
if (isNegative)
17774+
result.unsigned64 |= twosComplement.unsigned64; // OR in the two's complement mask
17775+
17776+
return result.signed64;
17777+
}
17778+
17779+

src/u-blox_GNSS.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1254,8 +1254,13 @@ class DevUBLOXGNSS
12541254
bool setNMEAGNZDAcallbackPtr(void (*callbackPointerPtr)(NMEA_ZDA_data_t *)); // Enable a callback on the arrival of a GNZDA message
12551255
#endif
12561256

1257-
// Helper functions for RTCM logging
1257+
// RTCM
1258+
12581259
#ifndef SFE_UBLOX_DISABLE_RTCM_LOGGING
1260+
uint8_t getLatestRTCM1005(RTCM_1005_data_t *data); // Return the most recent RTCM 1005: 0 = no data, 1 = stale data, 2 = fresh data
1261+
bool setRTCM1005callbackPtr(void (*callbackPointerPtr)(RTCM_1005_data_t *)); // Configure a callback for the RTCM 1005 Message
1262+
1263+
// Helper functions for RTCM logging
12591264
bool setRTCMLoggingMask(uint32_t messages = SFE_UBLOX_FILTER_RTCM_ALL); // Add selected RTCM messages to file buffer - if enabled. Default to adding ALL messages to the file buffer
12601265
uint32_t getRTCMLoggingMask(); // Return which RTCM messages are selected for logging to the file buffer - if enabled
12611266
#endif
@@ -1276,6 +1281,10 @@ class DevUBLOXGNSS
12761281
float extractFloat(ubxPacket *msg, uint16_t spotToStart); // Get signed 32-bit float (R4) from payload
12771282
double extractDouble(ubxPacket *msg, uint16_t spotToStart); // Get signed 64-bit double (R8) from payload
12781283

1284+
// Functions to help extract RTCM bit fields
1285+
uint16_t extractUnsignedBits16(uint8_t *ptr, uint16_t start, uint16_t width);
1286+
int64_t extractSignedBits64(uint8_t *ptr, uint16_t start, uint16_t width);
1287+
12791288
// Pointers to storage for the "automatic" messages
12801289
// RAM is allocated for these if/when required.
12811290

@@ -1342,6 +1351,8 @@ class DevUBLOXGNSS
13421351
NMEA_GNZDA_t *storageNMEAGNZDA = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary
13431352
#endif
13441353

1354+
RTCM_1005_t *storageRTCM1005 = nullptr; // Pointer to struct. RAM will be allocated for this if/when necessary
1355+
13451356
uint16_t rtcmFrameCounter = 0; // Tracks the type of incoming byte inside RTCM frame
13461357

13471358
protected:
@@ -1419,6 +1430,8 @@ class DevUBLOXGNSS
14191430
bool initStorageRTCM(); // Allocate RAM for incoming RTCM messages and initialize it
14201431
bool initStorageNMEA(); // Allocate RAM for incoming non-Auto NMEA messages and initialize it
14211432

1433+
bool initStorageRTCM1005(); // Allocate RAM for incoming RTCM 1005 messages and initialize it
1434+
14221435
// Variables
14231436
SparkFun_UBLOX_GNSS::GNSSDeviceBus *_sfeBus;
14241437

0 commit comments

Comments
 (0)