Merge pull request #130 from sparkfun/release_candidate

v2.2.7
This commit is contained in:
Paul
2022-03-18 17:21:10 +00:00
committed by GitHub
13 changed files with 1040 additions and 132 deletions
@@ -93,27 +93,43 @@ void loop()
WiFiClientSecure wifiClient = WiFiClientSecure();
MqttClient mqttClient(wifiClient);
void mqttMessageHandler(int messageSize) {
uint8_t mgaData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
int mgaCount = 0;
Serial.print(F("Pushed data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
void mqttMessageHandler(int messageSize)
{
const uint16_t mqttLimit = 512;
uint8_t *mqttData = new uint8_t[mqttLimit]; // Allocate memory to hold the MQTT data
if (mqttData == NULL)
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
mgaData[mgaCount++] = ch;
if (mgaCount == sizeof(mgaData))
break;
Serial.println(F("Memory allocation for mqttData failed!"));
return;
}
if (mgaCount > 0)
Serial.print(F("Pushing data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
{
//Push MGA data to GNSS module over I2C
myGNSS.pushRawData(mgaData, mgaCount, false);
lastReceived_ms = millis();
uint16_t mqttCount = 0;
while (mqttClient.available())
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
mqttData[mqttCount++] = ch;
if (mqttCount == mqttLimit)
break;
}
if (mqttCount > 0)
{
//Push KEYS or SPARTN data to GNSS module over I2C
myGNSS.pushRawData(mqttData, mqttCount, false);
lastReceived_ms = millis();
}
}
delete[] mqttData;
}
//Connect to MQTT broker, receive MGA, and push to ZED module over I2C
@@ -10,7 +10,7 @@
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S: Coming soon!
NEO-D9S Correction Data Receiver: https://www.sparkfun.com/products/19390
Hardware Connections:
Use a Qwiic cable to connect the NEO-D9S L-Band corection data receiver to your board
@@ -36,6 +36,7 @@
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 <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoMqttClient.h> // Click here to get the library: http://librarymanager/All#ArduinoMqttClient
@@ -44,10 +45,137 @@
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;
//Global variables
#define OK(ok) (ok ? F(" -> OK") : F(" -> ERROR!")) // Convert uint8_t into OK/ERROR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Global variables
long lastReceived_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallbackPtr
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print(ubxDataStruct->ebno);
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
@@ -58,17 +186,32 @@ void setup()
Wire.begin(); //Start I2C
if (myGNSS.begin() == false) //Connect to the Ublox module using Wire port
//myGNSS.enableDebugging(); // Uncomment this line to enable debug messages on Serial
while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GPS not detected at default I2C address. Please check wiring. Freezing."));
while (1);
Serial.println(F("u-blox GNSS module not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox module connected"));
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled.
myGNSS.setNavigationFrequency(1); //Set output in Hz.
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); // Be sure SPARTN input is enabled.
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 0); // Use IP source (default). Change this to 1 for L-Band (PMP)
if (ok) ok = myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
if (ok) ok = myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the SPARTN data is being decrypted successfully
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
@@ -81,6 +224,9 @@ void setup()
Serial.println(WiFi.localIP());
while (Serial.available()) Serial.read();
Serial.println(F("Press any key to start MQTT/SPARTN Client."));
}
void loop()
@@ -88,38 +234,56 @@ void loop()
if (Serial.available())
{
beginClient();
while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
Serial.println(F("Press any key to start MQTT/SPARTN Client."));
}
Serial.println(F("Press any key to start MQTT/SPARTN Client."));
delay(1000);
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
}
WiFiClientSecure wifiClient = WiFiClientSecure();
MqttClient mqttClient(wifiClient);
void mqttMessageHandler(int messageSize) {
uint8_t spartnData[512 * 4]; //Most incoming data is around 500 bytes but may be larger
int spartnCount = 0;
Serial.print(F("Pushed data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
void mqttMessageHandler(int messageSize)
{
const uint16_t mqttLimit = 512;
uint8_t *mqttData = new uint8_t[mqttLimit]; // Allocate memory to hold the MQTT data
if (mqttData == NULL)
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
spartnData[spartnCount++] = ch;
if (spartnCount == sizeof(spartnData))
break;
Serial.println(F("Memory allocation for mqttData failed!"));
return;
}
if (spartnCount > 0)
Serial.print(F("Pushing data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
{
//Push KEYS or SPARTN data to GNSS module over I2C
myGNSS.pushRawData(spartnData, spartnCount, false);
lastReceived_ms = millis();
uint16_t mqttCount = 0;
while (mqttClient.available())
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
mqttData[mqttCount++] = ch;
if (mqttCount == mqttLimit)
break;
}
if (mqttCount > 0)
{
//Push KEYS or SPARTN data to GNSS module over I2C
myGNSS.pushRawData(mqttData, mqttCount, false);
lastReceived_ms = millis();
}
}
delete[] mqttData;
}
//Connect to STARTN MQTT broker, receive RTCM, and push to ZED module over I2C
@@ -153,12 +317,14 @@ void beginClient()
mqttClient.onMessage(mqttMessageHandler);
mqttClient.subscribe(MQTT_TOPIC_KEY);
mqttClient.subscribe(MQTT_TOPIC_SPARTN);
mqttClient.subscribe(MQTT_TOPIC_ASSISTNOW);
lastReceived_ms = millis();
} //End attempt to connect
} //End connected == false
else {
mqttClient.poll();
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceived_ms > maxTimeBeforeHangup_ms)
{
@@ -168,6 +334,9 @@ void beginClient()
return;
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
delay(10);
}
@@ -5,15 +5,19 @@ const char password[] = "<YOUR PASSWORD>";
// Below infomation you can set after signing up with u-blox Thingstream portal
// and after add a new New PointPerfect Thing
// https://portal.thingstream.io/app/location-services/things
// in the new PointPerfect Thing you go to the credentials page and copy past the values and certificate into this.
// in the new PointPerfect Thing you go to the credentials page and copy paste the values and certificate into this.
// <Your PointPerfect Thing> -> Credentials -> Hostname
const char AWS_IOT_ENDPOINT[] = "pp.services.u-blox.com";
const unsigned short AWS_IOT_PORT = 8883;
// <Your PointPerfect Thing> -> Credentials -> IP key distribution topic
const char MQTT_TOPIC_KEY[] = "/pp/key/ip";
const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/ip"; // This topic provides the IP only dynamic keys in UBX format
//const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/Lb"; // This topic provides the L-Band + IP dynamic keys in UBX format
// <Your PointPerfect Thing> -> Credentials -> IP correction topic for EU/US region
const char MQTT_TOPIC_SPARTN[] = "/pp/ip/us"; // choice of {eu, us}
const char MQTT_TOPIC_SPARTN[] = "/pp/ip/us"; // This topic provides the SPARTN corrections for IP only: choice of {eu, us}
//const char MQTT_TOPIC_SPARTN[] = "/pp/Lb/us"; // This topic provides the SPARTN corrections for L-Band and L-Band + IP: choice of {eu, us}
// <Your PointPerfect Thing> -> Credentials -> AssistNow (MGA) topic
const char MQTT_TOPIC_ASSISTNOW[] = "/pp/ubx/mga";
// <Your PointPerfect Thing> -> Credentials -> Client Id
static const char MQTT_CLIENT_ID[] = "<ADD YOUR CLIENT ID HERE>";
@@ -10,7 +10,7 @@
This is a proof of concept to show how the UBX-RXM-PMP corrections control the accuracy.
You will need a Thingstream PointPerfect account to be able to access the SPARTN Credentials (IP Dynamic Keys).
You will need a Thingstream PointPerfect account to be able to access the SPARTN Credentials (L-Band or L-Band + IP Dynamic Keys).
Copy and paste the Current Key and Next Key into secrets.h.
Feel like supporting open source hardware?
@@ -121,6 +121,67 @@ void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print(ubxDataStruct->ebno);
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
@@ -148,6 +209,8 @@ void setup()
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 1); // use LBAND PMP message
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
//Configure the SPARTN IP Dynamic Keys
//"When the receiver boots, the host should send 'current' and 'next' keys in one message." - Use setDynamicSPARTNKeys for this.
@@ -164,6 +227,8 @@ void setup()
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the PMP data is being decrypted successfully
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the NEO-D9S L-Band receiver
@@ -1,24 +1,27 @@
// You can set the information below after signing up with the u-blox Thingstream portal
// and adding a new New PointPerfect Thing
// and adding a new New PointPerfect Thing (L-Band or L-Band + IP)
// https://portal.thingstream.io/app/location-services/things
// In the new PointPerfect Thing, you go to the credentials tab and copy and paste the IP Dynamic Keys here.
//
// The keys are valid from a particular GPS Week Number and Time of Week.
// Looking at the credentials tab, the current key expires 23:59 Feb 11th 2022.
// This means the next key is valid _from_ Midnight Feb 12th 2022.
// That is GPS Week 2196. The GPS Time of Week in seconds is 518418.
// That is GPS Week 2196. The GPS Time of Week in seconds is 518400.
// Working backwards, the current key became valid exactly 4 weeks earlier (Midnight Jan 15th 2022).
//
// See: https://www.labsat.co.uk/index.php/en/gps-time-calculator
//
// The keys are given as: 32 hexadecimal digits = 128 bits = 16 Bytes
//
// The next example shows how to retrieve the keys using ESP32 WiFi and MQTT.
// You can cut and paste the keys and GPS week/time-of-week from that example into here.
const uint8_t currentKeyLengthBytes = 16;
const char currentDynamicKey[] = "f742bd6b7248043177dd649141d8fb0b";
const uint16_t currentKeyGPSWeek = 2192;
const uint32_t currentKeyGPSToW = 518418;
const char currentDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
const uint16_t currentKeyGPSWeek = 2192; // Update this when you add new keys
const uint32_t currentKeyGPSToW = 518400;
const uint8_t nextKeyLengthBytes = 16;
const char nextDynamicKey[] = "8206........................29f4";
const uint16_t nextKeyGPSWeek = 2196;
const uint32_t nextKeyGPSToW = 518418;
const char nextDynamicKey[] = "<ADD YOUR L-Band or L-Band + IP DYNAMIC KEY HERE>";
const uint16_t nextKeyGPSWeek = 2196; // Update this when you add new keys
const uint32_t nextKeyGPSToW = 518400;
@@ -0,0 +1,459 @@
/*
Use ESP32 WiFi to get the L-Band dynamic keys from PointPerfect, allowing a ZED-F9x to use
the PMP data from a NEO-D9S correction data receiver.
By: SparkFun / Paul Clark
Based on original code by: u-blox AG / Michael Ammann
Date: March 17th, 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 obtain the L-Band dynamic keys from PointPerfect over ESP32 WiFi
and push them over I2C to a ZED-F9x. The ZED will then be able to decrypt the PMP correction data
from a NEO-D9S correction data receiver.
You can copy the keys directly from the Thingstream portal and paste them into your code - the
previous example shows how to do this - but calculating the "valid from" week and time is a chore.
This example requests the keys for you (using your client key and certificates) via MQTT.
It prints them too, so you can copy and paste them into the previous example if you wish.
You will need to have a valid u-blox Thingstream account and have a PointPerfect L-Band or L-Band + IP
Location Thing and payed plan.
Thingstream offers SSR corrections to SPARTN capable RTK receivers such as the u-blox ZED-F9 series
in continental Europe and US. Their Network is planned to be expanded to other regions over the next years.
To sign up, go to: https://portal.thingstream.io/app/location-services/things
For more information about MQTT, SPARTN and PointPerfect Correction Services
please see: https://www.u-blox.com/en/product/pointperfect
Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
NEO-D9S Correction Data Receiver: https://www.sparkfun.com/products/19390
RTK Surveyor: https://www.sparkfun.com/products/18443
RTK Express: https://www.sparkfun.com/products/18442
Recommended Hardware:
MicroMod GNSS Carrier Board: https://www.sparkfun.com/products/17722
ESP32 Micromod https://www.sparkfun.com/products/16781
Hardware Connections:
Plug a Qwiic cable into the GNSS and a ESP32 Thing Plus
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 <WiFi.h>
#include <WiFiClientSecure.h>
#include <ArduinoMqttClient.h> // Click here to get the library: http://librarymanager/All#ArduinoMqttClient
#include "secrets.h"
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> // Click here to get the library: http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS; // ZED-F9x
SFE_UBLOX_GNSS myLBand; // NEO-D9S
const uint32_t myLBandFreq = 1556290000; // Uncomment this line to use the US SPARTN 1.8 service
//const uint32_t myLBandFreq = 1545260000; // Uncomment this line to use the EU SPARTN 1.8 service
#define OK(ok) (ok ? F(" -> OK") : F(" -> ERROR!")) // Convert uint8_t into OK/ERROR
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//Global variables
long lastReceived_ms = 0; //5 RTCM messages take approximately ~300ms to arrive at 115200bps
int maxTimeBeforeHangup_ms = 10000; //If we fail to get a complete RTCM frame after 10s, then disconnect from caster
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: pushRXMPMP will be called when new PMP data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_PMP_message_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMPMPmessageCallbackPtr
// / _____ This _must_ be UBX_RXM_PMP_message_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void pushRXMPMP(UBX_RXM_PMP_message_data_t *pmpData)
{
//Extract the raw message payload length
uint16_t payloadLen = ((uint16_t)pmpData->lengthMSB << 8) | (uint16_t)pmpData->lengthLSB;
Serial.print(F("New RXM-PMP data received. Message payload length is "));
Serial.print(payloadLen);
Serial.println(F(" Bytes. Pushing it to the GNSS..."));
//Push the PMP data to the GNSS
//The payload length could be variable, so we need to push the header and payload, then checksum
myGNSS.pushRawData(&pmpData->sync1, (size_t)payloadLen + 6); // Push the sync chars, class, ID, length and payload
myGNSS.pushRawData(&pmpData->checksumA, (size_t)2); // Push the checksum bytes
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printPVTdata will be called when new NAV PVT data arrives
// See u-blox_structs.h for the full definition of UBX_NAV_PVT_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setAutoPVTcallbackPtr
// / _____ This _must_ be UBX_NAV_PVT_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printPVTdata(UBX_NAV_PVT_data_t *ubxDataStruct)
{
double latitude = ubxDataStruct->lat; // Print the latitude
Serial.print(F("Lat: "));
Serial.print(latitude / 10000000.0, 7);
double longitude = ubxDataStruct->lon; // Print the longitude
Serial.print(F(" Long: "));
Serial.print(longitude / 10000000.0, 7);
double altitude = ubxDataStruct->hMSL; // Print the height above mean sea level
Serial.print(F(" Height: "));
Serial.print(altitude / 1000.0, 3);
uint8_t fixType = ubxDataStruct->fixType; // Print the fix type
Serial.print(F(" Fix: "));
Serial.print(fixType);
if (fixType == 0)
Serial.print(F(" (None)"));
else if (fixType == 1)
Serial.print(F(" (Dead Reckoning)"));
else if (fixType == 2)
Serial.print(F(" (2D)"));
else if (fixType == 3)
Serial.print(F(" (3D)"));
else if (fixType == 3)
Serial.print(F(" (GNSS + Dead Reckoning)"));
else if (fixType == 5)
Serial.print(F(" (Time Only)"));
else
Serial.print(F(" (UNKNOWN)"));
uint8_t carrSoln = ubxDataStruct->flags.bits.carrSoln; // Print the carrier solution
Serial.print(F(" Carrier Solution: "));
Serial.print(carrSoln);
if (carrSoln == 0)
Serial.print(F(" (None)"));
else if (carrSoln == 1)
Serial.print(F(" (Floating)"));
else if (carrSoln == 2)
Serial.print(F(" (Fixed)"));
else
Serial.print(F(" (UNKNOWN)"));
uint32_t hAcc = ubxDataStruct->hAcc; // Print the horizontal accuracy estimate
Serial.print(F(" Horizontal Accuracy Estimate: "));
Serial.print(hAcc);
Serial.print(F(" (mm)"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Callback: printRXMCOR will be called when new RXM COR data arrives
// See u-blox_structs.h for the full definition of UBX_RXM_COR_data_t
// _____ You can use any name you like for the callback. Use the same name when you call setRXMCORcallbackPtr
// / _____ This _must_ be UBX_RXM_COR_data_t
// | / _____ You can use any name you like for the struct
// | | /
// | | |
void printRXMCOR(UBX_RXM_COR_data_t *ubxDataStruct)
{
Serial.print(F("UBX-RXM-COR: ebno: "));
Serial.print(ubxDataStruct->ebno);
Serial.print(F(" protocol: "));
if (ubxDataStruct->statusInfo.bits.protocol == 1)
Serial.print(F("RTCM3"));
else if (ubxDataStruct->statusInfo.bits.protocol == 2)
Serial.print(F("SPARTN"));
else if (ubxDataStruct->statusInfo.bits.protocol == 29)
Serial.print(F("PMP (SPARTN)"));
else if (ubxDataStruct->statusInfo.bits.protocol == 30)
Serial.print(F("QZSSL6"));
else
Serial.print(F("Unknown"));
Serial.print(F(" errStatus: "));
if (ubxDataStruct->statusInfo.bits.errStatus == 1)
Serial.print(F("Error-free"));
else if (ubxDataStruct->statusInfo.bits.errStatus == 2)
Serial.print(F("Erroneous"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgUsed: "));
if (ubxDataStruct->statusInfo.bits.msgUsed == 1)
Serial.print(F("Not used"));
else if (ubxDataStruct->statusInfo.bits.msgUsed == 2)
Serial.print(F("Used"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgEncrypted: "));
if (ubxDataStruct->statusInfo.bits.msgEncrypted == 1)
Serial.print(F("Not encrypted"));
else if (ubxDataStruct->statusInfo.bits.msgEncrypted == 2)
Serial.print(F("Encrypted"));
else
Serial.print(F("Unknown"));
Serial.print(F(" msgDecrypted: "));
if (ubxDataStruct->statusInfo.bits.msgDecrypted == 1)
Serial.print(F("Not decrypted"));
else if (ubxDataStruct->statusInfo.bits.msgDecrypted == 2)
Serial.print(F("Successfully decrypted"));
else
Serial.print(F("Unknown"));
Serial.println();
}
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(F("NEO-D9S SPARTN Corrections"));
Wire.begin(); //Start I2C
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the ZED-F9x
//myGNSS.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
while (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS module not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox GNSS module connected"));
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_SPARTN_USE_SOURCE, 1); // use LBAND PMP message
if (ok) ok = myGNSS.setVal8(UBLOX_CFG_MSGOUT_UBX_RXM_COR_I2C, 1); // Enable UBX-RXM-COR messages on I2C
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
Serial.print(F("GNSS: configuration "));
Serial.println(OK(ok));
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
myGNSS.setRXMCORcallbackPtr(&printRXMCOR); // Print the contents of UBX-RXM-COR messages so we can check if the PMP data is being decrypted successfully
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Begin and configure the NEO-D9S L-Band receiver
//myLBand.enableDebugging(); // Uncomment this line to enable helpful debug messages on Serial
while (myLBand.begin(Wire, 0x43) == false) //Connect to the u-blox NEO-D9S using Wire port. The D9S default I2C address is 0x43 (not 0x42)
{
Serial.println(F("u-blox NEO-D9S not detected at default I2C address. Please check wiring."));
delay(2000);
}
Serial.println(F("u-blox NEO-D9S connected"));
ok = myLBand.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, myLBandFreq); // Default 1539812500 Hz
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SEARCH_WINDOW, 2200); // Default 2200 Hz
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_SERVICE_ID, 0); // Default 1
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SERVICE_ID, 21845); // Default 50821
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_DATA_RATE, 2400); // Default 2400 bps
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_DESCRAMBLER, 1); // Default 1
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_DESCRAMBLER_INIT, 26969); // Default 23560
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_PRESCRAMBLING, 0); // Default 0
if (ok) ok = myLBand.setVal64(UBLOX_CFG_PMP_UNIQUE_WORD, 16238547128276412563ull);
if (ok) ok = myLBand.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_I2C, 1); // Ensure UBX-RXM-PMP is enabled on the I2C port
if (ok) ok = myLBand.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART1, 1); // Output UBX-RXM-PMP on UART1
if (ok) ok = myLBand.setVal(UBLOX_CFG_UART2OUTPROT_UBX, 1); // Enable UBX output on UART2
if (ok) ok = myLBand.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 1); // Output UBX-RXM-PMP on UART2
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART1_BAUDRATE, 38400); // match baudrate with ZED default
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART2_BAUDRATE, 38400); // match baudrate with ZED default
Serial.print(F("L-Band: configuration "));
Serial.println(OK(ok));
myLBand.softwareResetGNSSOnly(); // Do a restart
myLBand.setRXMPMPmessageCallbackPtr(&pushRXMPMP); // Call pushRXMPMP when new PMP data arrives. Push it to the GNSS
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Connect to WiFi so we can request the dynamic keys via MQTT
Serial.print(F("Connecting to local WiFi"));
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(F("."));
}
Serial.println();
Serial.print(F("WiFi connected with IP: "));
Serial.println(WiFi.localIP());
while (Serial.available()) Serial.read();
Serial.println(F("Press any key to start MQTT Client."));
}
void loop()
{
if (Serial.available())
{
beginClient();
while (Serial.available()) Serial.read(); //Empty buffer of any newline chars
Serial.println(F("Press any key to start MQTT Client."));
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
myLBand.checkUblox(); // Check for the arrival of new PMP data and process it.
myLBand.checkCallbacks(); // Check if any LBand callbacks are waiting to be processed.
}
WiFiClientSecure wifiClient = WiFiClientSecure();
MqttClient mqttClient(wifiClient);
void mqttMessageHandler(int messageSize)
{
const uint16_t mqttLimit = 512;
uint8_t *mqttData = new uint8_t[mqttLimit]; // Allocate memory to hold the MQTT data
if (mqttData == NULL)
{
Serial.println(F("Memory allocation for mqttData failed!"));
return;
}
Serial.print(F("Pushing data from "));
Serial.print(mqttClient.messageTopic());
Serial.println(F(" topic to ZED"));
while (mqttClient.available())
{
uint16_t mqttCount = 0;
while (mqttClient.available())
{
char ch = mqttClient.read();
//Serial.write(ch); //Pipe to serial port is fine but beware, it's a lot of binary data
mqttData[mqttCount++] = ch;
if (mqttCount == mqttLimit)
break;
}
if (mqttCount > 0)
{
//Push KEYS or SPARTN data to GNSS module over I2C
myGNSS.pushRawData(mqttData, mqttCount, false);
lastReceived_ms = millis();
if ((mqttData[0] == 0xB5) // Check if this is UBX-RXM-SPARTNKEY
&& (mqttData[1] == 0x62)
&& (mqttData[2] == 0x02) // Class: RXM
&& (mqttData[3] == 0x36)) // ID: SPARTNKEY
{
uint8_t numKeys = mqttData[7]; // Get the number of keys
uint8_t keyStart = 10 + (numKeys * 8); // Point to the start of the first key
for (uint8_t key = 0; key < numKeys; key++)
{
Serial.print(F("SPARTNKEY: "));
Serial.println(key);
Serial.print(F("Valid from GPS week number: "));
uint16_t validFromWno = ((uint16_t)mqttData[12 + (key * 8)]) | ((uint16_t)mqttData[13 + (key * 8)] << 8); // Little endian
Serial.println(validFromWno);
Serial.print(F("Valid from GPS time of week: "));
uint32_t validFromTow = ((uint32_t)mqttData[14 + (key * 8)]) | ((uint32_t)mqttData[15 + (key * 8)] << 8) | ((uint32_t)mqttData[16 + (key * 8)] << 16) | ((uint32_t)mqttData[17 + (key * 8)] << 24);
Serial.println(validFromTow);
uint8_t keyLengthBytes = mqttData[11 + (key * 8)];
Serial.print(F("Key length (bytes): "));
Serial.println(keyLengthBytes);
Serial.print(F("Key: \""));
for (uint8_t digit = 0; digit < keyLengthBytes; digit++)
{
Serial.print(mqttData[keyStart + digit] >> 4, HEX); // Print the key as ASCII Hex
Serial.print(mqttData[keyStart + digit] & 0x0F, HEX); // Print the key as ASCII Hex
}
Serial.println(F("\""));
keyStart += keyLengthBytes; // Update keyStart for the next key
}
}
}
}
delete[] mqttData;
}
//Connect to MQTT broker, receive dynamic keys and push to ZED module over I2C
void beginClient()
{
Serial.println(F("Subscribing to Broker. Press key to stop"));
delay(10); //Wait for any serial to arrive
while (Serial.available()) Serial.read(); //Flush
while (Serial.available() == 0)
{
//Connect if we are not already
if (wifiClient.connected() == false)
{
// Connect to AWS IoT
wifiClient.setCACert(AWS_CERT_CA);
wifiClient.setCertificate(AWS_CERT_CRT);
wifiClient.setPrivateKey(AWS_CERT_PRIVATE);
mqttClient.setId(MQTT_CLIENT_ID);
mqttClient.setKeepAliveInterval(60*1000);
mqttClient.setConnectionTimeout( 5*1000);
if (!mqttClient.connect(AWS_IOT_ENDPOINT, AWS_IOT_PORT)) {
Serial.print(F("MQTT connection failed! Error code = "));
Serial.println(mqttClient.connectError());
return;
} else {
Serial.println(F("You're connected to the PointPerfect MQTT broker: "));
Serial.println(AWS_IOT_ENDPOINT);
// Subscribe to MQTT and register a callback
Serial.println(F("Subscribe to Topics"));
mqttClient.onMessage(mqttMessageHandler);
mqttClient.subscribe(MQTT_TOPIC_KEY);
lastReceived_ms = millis();
} //End attempt to connect
} //End connected == false
else {
mqttClient.poll();
}
//Close socket if we don't have new data for 10s
if (millis() - lastReceived_ms > maxTimeBeforeHangup_ms)
{
Serial.println(F("MQTT timeout. Disconnecting..."));
if (mqttClient.connected() == true)
mqttClient.stop();
return;
}
myGNSS.checkUblox(); // Check for the arrival of new GNSS data and process it.
myGNSS.checkCallbacks(); // Check if any GNSS callbacks are waiting to be processed.
myLBand.checkUblox(); // Check for the arrival of new PMP data and process it.
myLBand.checkCallbacks(); // Check if any LBand callbacks are waiting to be processed.
delay(10);
}
Serial.println(F("User pressed a key"));
Serial.println(F("Disconnecting..."));
wifiClient.stop();
}
@@ -0,0 +1,40 @@
//Your WiFi credentials
const char ssid[] = "<YOUR SSID>";
const char password[] = "<YOUR PASSWORD>";
// Below infomation you can set after signing up with u-blox Thingstream portal
// and after add a new New PointPerfect Thing (L-Band or L-Band + IP)
// https://portal.thingstream.io/app/location-services/things
// in the new PointPerfect Thing you go to the credentials page and copy paste the values and certificate into this.
// <Your PointPerfect Thing> -> Credentials -> Hostname
const char AWS_IOT_ENDPOINT[] = "pp.services.u-blox.com";
const unsigned short AWS_IOT_PORT = 8883;
// <Your PointPerfect Thing> -> Credentials -> IP key distribution topic
//const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/ip"; // This topic provides the IP only dynamic keys in UBX format
const char MQTT_TOPIC_KEY[] = "/pp/ubx/0236/Lb"; // This topic provides the L-Band + IP dynamic keys in UBX format
// <Your PointPerfect Thing> -> Credentials -> Client Id
static const char MQTT_CLIENT_ID[] = "<ADD YOUR CLIENT ID HERE>";
// <Your PointPerfect Thing> -> Credentials -> Amazon Root Certificate
static const char AWS_CERT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
<ADD YOUR CERTICICATE HERE>
-----END CERTIFICATE-----
)EOF";
// <Your PointPerfect Thing> -> Credentials -> Client Certificate
static const char AWS_CERT_CRT[] PROGMEM = R"KEY(
-----BEGIN CERTIFICATE-----
<ADD YOUR CERTICICATE HERE>
-----END CERTIFICATE-----
)KEY";
// Get this from Thingstream Portal
// <Your PointPerfect Thing> -> Credentials -> Client Key
static const char AWS_CERT_PRIVATE[] PROGMEM = R"KEY(
-----BEGIN RSA PRIVATE KEY-----
<ADD YOUR KEY HERE>
-----END RSA PRIVATE KEY-----
)KEY";
+5
View File
@@ -40,6 +40,7 @@ UBX_NAV_AOPSTATUS_data_t KEYWORD1
UBX_RXM_PMP_data_t KEYWORD1
UBX_RXM_PMP_message_data_t KEYWORD1
UBX_RXM_COR_data_t KEYWORD1
UBX_RXM_SFRBX_data_t KEYWORD1
UBX_RXM_RAWX_data_t KEYWORD1
@@ -393,6 +394,8 @@ logAOPSTATUS KEYWORD2
setRXMPMPcallbackPtr KEYWORD2
setRXMPMPmessageCallbackPtr KEYWORD2
setRXMCORcallbackPtr KEYWORD2
getRXMSFRBX KEYWORD2
setAutoRXMSFRBX KEYWORD2
setAutoRXMSFRBXrate KEYWORD2
@@ -772,6 +775,8 @@ UBX_NAV_TIMELS LITERAL1
UBX_NAV_VELECEF LITERAL1
UBX_NAV_VELNED LITERAL1
UBX_RXM_PMP LITERAL1
UBX_RXM_COR LITERAL1
UBX_RXM_RAWX LITERAL1
UBX_RXM_SFRBX LITERAL1
UBX_RXM_SPARTN LITERAL1
+1 -1
View File
@@ -1,5 +1,5 @@
name=SparkFun u-blox GNSS Arduino Library
version=2.2.6
version=2.2.7
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/>
@@ -294,6 +294,16 @@ void SFE_UBLOX_GNSS::end(void)
packetUBXRXMPMPmessage = NULL; // Redundant?
}
if (packetUBXRXMCOR != NULL)
{
if (packetUBXRXMCOR->callbackData != NULL)
{
delete packetUBXRXMCOR->callbackData;
}
delete packetUBXRXMCOR;
packetUBXRXMCOR = NULL; // Redundant?
}
if (packetUBXRXMSFRBX != NULL)
{
if (packetUBXRXMSFRBX->callbackData != NULL)
@@ -1333,6 +1343,10 @@ bool SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
if ((packetUBXRXMPMP != NULL) || (packetUBXRXMPMPmessage != NULL))
result = true;
break;
case UBX_RXM_COR:
if (packetUBXRXMCOR != NULL)
result = true;
break;
}
}
break;
@@ -1504,6 +1518,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID)
case UBX_RXM_PMP:
maxSize = UBX_RXM_PMP_MAX_LEN;
break;
case UBX_RXM_COR:
maxSize = UBX_RXM_COR_LEN;
break;
}
}
break;
@@ -3772,6 +3789,22 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid
}
}
else if (msg->id == UBX_RXM_COR)
{
// Parse various byte fields into storage - but only if we have memory allocated for it
if ((packetUBXRXMCOR != NULL) && (packetUBXRXMCOR->callbackData != NULL)
//&& (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == false) // <=== Uncomment this line to prevent new data from overwriting 'old'
)
{
packetUBXRXMCOR->callbackData->version = extractByte(msg, 0);
packetUBXRXMCOR->callbackData->ebno = extractByte(msg, 1);
packetUBXRXMCOR->callbackData->statusInfo.all = extractLong(msg, 4);
packetUBXRXMCOR->callbackData->msgType = extractInt(msg, 8);
packetUBXRXMCOR->callbackData->msgSubType = extractInt(msg, 10);
packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = true; // Mark the data as valid
}
}
else if (msg->id == UBX_RXM_SFRBX)
// Note: length is variable
// Note: on protocol version 17: numWords is (0..16)
@@ -5359,6 +5392,17 @@ void SFE_UBLOX_GNSS::checkCallbacks(void)
packetUBXRXMPMPmessage->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
}
if ((packetUBXRXMCOR != NULL) // If RAM has been allocated for message storage
&& (packetUBXRXMCOR->callbackData != NULL) // If RAM has been allocated for the copy of the data
&& (packetUBXRXMCOR->callbackPointerPtr != NULL) // If the pointer to the callback has been defined
&& (packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
{
// if (_printDebug == true)
// _debugSerial->println(F("checkCallbacks: calling callbackPtr for RXM COR"));
packetUBXRXMCOR->callbackPointerPtr(packetUBXRXMCOR->callbackData); // Call the callback
packetUBXRXMCOR->automaticFlags.flags.bits.callbackCopyValid = false; // Mark the data as stale
}
if ((packetUBXRXMSFRBX != NULL) // If RAM has been allocated for message storage
&& (packetUBXRXMSFRBX->callbackData != NULL) // If RAM has been allocated for the copy of the data
&& (packetUBXRXMSFRBX->automaticFlags.flags.bits.callbackCopyValid == true)) // If the copy of the data is valid
@@ -12316,6 +12360,49 @@ bool SFE_UBLOX_GNSS::initPacketUBXRXMPMPmessage()
return (true);
}
bool SFE_UBLOX_GNSS::setRXMCORcallbackPtr(void (*callbackPointer)(UBX_RXM_COR_data_t *))
{
if (packetUBXRXMCOR == NULL)
initPacketUBXRXMCOR(); // Check that RAM has been allocated for the data
if (packetUBXRXMCOR == NULL) // Only attempt this if RAM allocation was successful
return false;
if (packetUBXRXMCOR->callbackData == NULL) // Check if RAM has been allocated for the callback copy
{
packetUBXRXMCOR->callbackData = new UBX_RXM_COR_data_t; // Allocate RAM for the main struct
}
if (packetUBXRXMCOR->callbackData == NULL)
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
_debugSerial->println(F("setAutoRXMCORcallbackPtr: RAM alloc failed!"));
#endif
return (false);
}
packetUBXRXMCOR->callbackPointerPtr = callbackPointer;
return (true);
}
// PRIVATE: Allocate RAM for packetUBXRXMCOR and initialize it
bool SFE_UBLOX_GNSS::initPacketUBXRXMCOR()
{
packetUBXRXMCOR = new UBX_RXM_COR_t; // Allocate RAM for the main struct
if (packetUBXRXMCOR == NULL)
{
#ifndef SFE_UBLOX_REDUCED_PROG_MEM
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
_debugSerial->println(F("initPacketUBXRXMCOR: RAM alloc failed!"));
#endif
return (false);
}
packetUBXRXMCOR->automaticFlags.flags.all = 0;
packetUBXRXMCOR->callbackPointerPtr = NULL;
packetUBXRXMCOR->callbackData = NULL;
return (true);
}
// ***** RXM SFRBX automatic support
bool SFE_UBLOX_GNSS::getRXMSFRBX(uint16_t maxWait)
+51 -51
View File
@@ -624,20 +624,19 @@ typedef struct
const uint32_t SFE_UBLOX_DAYS_FROM_1970_TO_2020 = 18262; // Jan 1st 2020 Epoch = 1577836800 seconds
const uint16_t SFE_UBLOX_DAYS_SINCE_2020[80] =
{
0, 366, 731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862, 10227, 10593,
10958, 11323, 11688, 12054, 12419, 12784, 13149, 13515, 13880, 14245,
14610, 14976, 15341, 15706, 16071, 16437, 16802, 17167, 17532, 17898,
18263, 18628, 18993, 19359, 19724, 20089, 20454, 20820, 21185, 21550,
21915, 22281, 22646, 23011, 23376, 23742, 24107, 24472, 24837, 25203,
25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855
};
{
0, 366, 731, 1096, 1461, 1827, 2192, 2557, 2922, 3288,
3653, 4018, 4383, 4749, 5114, 5479, 5844, 6210, 6575, 6940,
7305, 7671, 8036, 8401, 8766, 9132, 9497, 9862, 10227, 10593,
10958, 11323, 11688, 12054, 12419, 12784, 13149, 13515, 13880, 14245,
14610, 14976, 15341, 15706, 16071, 16437, 16802, 17167, 17532, 17898,
18263, 18628, 18993, 19359, 19724, 20089, 20454, 20820, 21185, 21550,
21915, 22281, 22646, 23011, 23376, 23742, 24107, 24472, 24837, 25203,
25568, 25933, 26298, 26664, 27029, 27394, 27759, 28125, 28490, 28855};
const uint16_t SFE_UBLOX_DAYS_SINCE_MONTH[2][12] =
{
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, // Leap Year (Year % 4 == 0)
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} // Normal Year
{
{0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335}, // Leap Year (Year % 4 == 0)
{0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334} // Normal Year
};
class SFE_UBLOX_GNSS
@@ -860,18 +859,17 @@ public:
bool disableRTCMmessage(uint8_t messageNumber, uint8_t portID, uint16_t maxWait = defaultMaxWait); // Turn off given RTCM message from a given port
// Functions used for RTK and base station setup
// It is probably safe to assume that users of the RTK will be using I2C / Qwiic. So let's leave maxWait set to 250ms.
bool getSurveyMode(uint16_t maxWait = 250); // Get the current TimeMode3 settings
bool setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Control survey in mode
bool setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Control survey in mode
bool enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Begin Survey-In for NEO-M8P / ZED-F9x
bool enableSurveyModeFull(uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = 250); // Begin Survey-In for NEO-M8P / ZED-F9x
bool disableSurveyMode(uint16_t maxWait = 250); // Stop Survey-In mode
bool getSurveyMode(uint16_t maxWait = defaultMaxWait); // Get the current TimeMode3 settings
bool setSurveyMode(uint8_t mode, uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode
bool setSurveyModeFull(uint8_t mode, uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Control survey in mode
bool enableSurveyMode(uint16_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Begin Survey-In for NEO-M8P / ZED-F9x
bool enableSurveyModeFull(uint32_t observationTime, float requiredAccuracy, uint16_t maxWait = defaultMaxWait); // Begin Survey-In for NEO-M8P / ZED-F9x
bool disableSurveyMode(uint16_t maxWait = defaultMaxWait); // Stop Survey-In mode
// Given coordinates, put receiver into static position. Set latlong to true to pass in lat/long values instead of ecef.
// For ECEF the units are: cm, 0.1mm, cm, 0.1mm, cm, 0.1mm
// For Lat/Lon/Alt the units are: degrees^-7, degrees^-9, degrees^-7, degrees^-9, cm, 0.1mm
bool setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong = false, uint16_t maxWait = 250);
bool setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latLong = false, uint16_t maxWait = 250);
bool setStaticPosition(int32_t ecefXOrLat, int8_t ecefXOrLatHP, int32_t ecefYOrLon, int8_t ecefYOrLonHP, int32_t ecefZOrAlt, int8_t ecefZOrAltHP, bool latLong = false, uint16_t maxWait = defaultMaxWait);
bool setStaticPosition(int32_t ecefXOrLat, int32_t ecefYOrLon, int32_t ecefZOrAlt, bool latLong = false, uint16_t maxWait = defaultMaxWait);
bool setDGNSSConfiguration(sfe_ublox_dgnss_mode_e dgnssMode = SFE_UBLOX_DGNSS_MODE_FIXED, uint16_t maxWait = defaultMaxWait); // Set the DGNSS differential mode
// Read the module's protocol version
@@ -954,35 +952,33 @@ public:
// General configuration (used only on protocol v27 and higher - ie, ZED-F9P)
// It is probably safe to assume that users of the ZED-F9P will be using I2C / Qwiic.
// If they are using Serial then the higher baud rate will also help. So let's leave maxWait set to 250ms.
uint32_t createKey(uint16_t group, uint16_t id, uint8_t size); // Form 32-bit key from group/id/size
sfe_ublox_status_e getVal(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Load payload with response
uint8_t getVal8(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
uint16_t getVal16(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
uint32_t getVal32(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
uint64_t getVal64(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given key location
uint8_t getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
uint16_t getVal16(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
uint32_t getVal32(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
uint64_t getVal64(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = 250); // Returns the value at a given group/id/size location
uint8_t setVal(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 16-bit value at a given group/id/size location
uint8_t setVal8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 8-bit value at a given group/id/size location
uint8_t setVal16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 16-bit value at a given group/id/size location
uint8_t setVal32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 32-bit value at a given group/id/size location
uint8_t setVal64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = 250); // Sets the 64-bit value at a given group/id/size location
uint8_t newCfgValset8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 8-bit value
uint8_t newCfgValset16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 16-bit value
uint8_t newCfgValset32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 32-bit value
uint8_t newCfgValset64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 64-bit value
uint8_t addCfgValset8(uint32_t keyID, uint8_t value); // Add a new KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset16(uint32_t keyID, uint16_t value); // Add a new KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset32(uint32_t keyID, uint32_t value); // Add a new KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset64(uint32_t keyID, uint64_t value); // Add a new KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t sendCfgValset8(uint32_t keyID, uint8_t value, uint16_t maxWait = 250); // Add the final KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = 250); // Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = 250); // Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset64(uint32_t keyID, uint64_t value, uint16_t maxWait = 250); // Add the final KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint32_t createKey(uint16_t group, uint16_t id, uint8_t size); // Form 32-bit key from group/id/size
sfe_ublox_status_e getVal(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Load payload with response
uint8_t getVal8(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
uint16_t getVal16(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
uint32_t getVal32(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
uint64_t getVal64(uint32_t keyID, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given key location
uint8_t getVal8(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
uint16_t getVal16(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
uint32_t getVal32(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
uint64_t getVal64(uint16_t group, uint16_t id, uint8_t size, uint8_t layer = VAL_LAYER_RAM, uint16_t maxWait = defaultMaxWait); // Returns the value at a given group/id/size location
uint8_t setVal(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 16-bit value at a given group/id/size location
uint8_t setVal8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 8-bit value at a given group/id/size location
uint8_t setVal16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 16-bit value at a given group/id/size location
uint8_t setVal32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 32-bit value at a given group/id/size location
uint8_t setVal64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL, uint16_t maxWait = defaultMaxWait); // Sets the 64-bit value at a given group/id/size location
uint8_t newCfgValset8(uint32_t keyID, uint8_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 8-bit value
uint8_t newCfgValset16(uint32_t keyID, uint16_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 16-bit value
uint8_t newCfgValset32(uint32_t keyID, uint32_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 32-bit value
uint8_t newCfgValset64(uint32_t keyID, uint64_t value, uint8_t layer = VAL_LAYER_ALL); // Define a new UBX-CFG-VALSET with the given KeyID and 64-bit value
uint8_t addCfgValset8(uint32_t keyID, uint8_t value); // Add a new KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset16(uint32_t keyID, uint16_t value); // Add a new KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset32(uint32_t keyID, uint32_t value); // Add a new KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t addCfgValset64(uint32_t keyID, uint64_t value); // Add a new KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket
uint8_t sendCfgValset8(uint32_t keyID, uint8_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 8-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset16(uint32_t keyID, uint16_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 16-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset32(uint32_t keyID, uint32_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 32-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
uint8_t sendCfgValset64(uint32_t keyID, uint64_t value, uint16_t maxWait = defaultMaxWait); // Add the final KeyID and 64-bit value to an existing UBX-CFG-VALSET ubxPacket and send it
// get and set functions for all of the "automatic" message processing
@@ -1159,6 +1155,8 @@ public:
bool setRXMPMPcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_data_t *)); // Callback receives a pointer to the data, instead of _all_ the data. Much kinder on the stack!
bool setRXMPMPmessageCallbackPtr(void (*callbackPointerPtr)(UBX_RXM_PMP_message_data_t *)); // Use this if you want all of the PMP message (including sync chars, checksum, etc.) to push to a GNSS
bool setRXMCORcallbackPtr(void (*callbackPointerPtr)(UBX_RXM_COR_data_t *)); // RXM COR
bool getRXMSFRBX(uint16_t maxWait = defaultMaxWait); // RXM SFRBX
bool setAutoRXMSFRBX(bool enabled, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic RXM SFRBX reports at the navigation frequency
bool setAutoRXMSFRBX(bool enabled, bool implicitUpdate, uint16_t maxWait = defaultMaxWait); // Enable/disable automatic RXM SFRBX reports at the navigation frequency, with implicitUpdate == false accessing stale data will not issue parsing of data in the rxbuffer of your interface, instead you have to call checkUblox when you want to perform an update
@@ -1512,6 +1510,7 @@ public:
UBX_RXM_PMP_t *packetUBXRXMPMP = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_RXM_PMP_message_t *packetUBXRXMPMPmessage = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_RXM_COR_t *packetUBXRXMCOR = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_RXM_SFRBX_t *packetUBXRXMSFRBX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
UBX_RXM_RAWX_t *packetUBXRXMRAWX = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
@@ -1610,6 +1609,7 @@ private:
bool initPacketUBXNAVAOPSTATUS(); // Allocate RAM for packetUBXNAVAOPSTATUS and initialize it
bool initPacketUBXRXMPMP(); // Allocate RAM for packetUBXRXMPMP and initialize it
bool initPacketUBXRXMPMPmessage(); // Allocate RAM for packetUBXRXMPMPRaw and initialize it
bool initPacketUBXRXMCOR(); // Allocate RAM for packetUBXRXMCOR and initialize it
bool initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it
bool initPacketUBXRXMRAWX(); // Allocate RAM for packetUBXRXMRAWX and initialize it
bool initPacketUBXCFGPRT(); // Allocate RAM for packetUBXCFGPRT and initialize it
+82 -22
View File
@@ -1484,6 +1484,66 @@ typedef struct
UBX_RXM_RAWX_data_t *callbackData;
} UBX_RXM_RAWX_t;
// UBX-RXM-COR (0x02 0x34): Differential correction input status
const uint16_t UBX_RXM_COR_LEN = 12;
typedef struct
{
uint8_t version; // Message version (0x01 for this version)
uint8_t ebno; // Energy per bit to noise power spectral density ratio (Eb/N0): 2^-3 dB
// 0: unknown. Reported only for protocol UBX-RXM-PMP (SPARTN) to monitor signal quality.
uint8_t reserved0[2]; // Reserved
union
{
uint32_t all;
struct
{
uint32_t protocol : 5; // Input correction data protocol:
// 0: Unknown
// 1: RTCM3
// 2: SPARTN (Secure Position Augmentation for Real Time Navigation)
// 29: UBX-RXM-PMP (SPARTN)
// 30: UBX-RXM-QZSSL6
uint32_t errStatus : 2; // Error status of the received correction message content based on possibly available error codes or checksums:
// 0: Unknown
// 1: Error-free
// 2: Erroneous
uint32_t msgUsed : 2; // Status of receiver using the input message:
// 0: Unknown
// 1: Not used
// 2: Used
uint32_t correctionId : 16; // Identifier for the correction stream:
// For RTCM 3: Reference station ID (DF003) of the received RTCM input message.
// Valid range 0-4095.
// For all other messages, reports 0xFFFF.
// For other correction protocols 0xFFFF.
uint32_t msgTypeValid : 1; // Validity of the msgType field. Set to False e.g. if the protocol does not define msgType.
uint32_t msgSubTypeValid : 1; // Validity of the msgSubType field. Set to False e.g. if the protocol does not define subtype for the msgType.
uint32_t msgInputHandle : 1; // Input handling support of the input message:
// 0: Receiver does not have input handling support for this message
// 1: Receiver has input handling support for this message
uint32_t msgEncrypted : 2; // Encryption status of the input message:
// 0: Unknown
// 1: Not encrypted
// 2: Encrypted
uint32_t msgDecrypted : 2; // Decryption status of the input message:
// 0: Unknown
// 1: Not decrypted
// 2: Successfully decrypted
} bits;
} statusInfo;
uint16_t msgType; // Message type
uint16_t msgSubType; // Message subtype
} UBX_RXM_COR_data_t;
// The COR data can only be accessed via a callback. COR cannot be polled.
typedef struct
{
ubxAutomaticFlags automaticFlags;
void (*callbackPointerPtr)(UBX_RXM_COR_data_t *);
UBX_RXM_COR_data_t *callbackData;
} UBX_RXM_COR_t;
// UBX-RXM-PMP (0x02 0x72): PMP raw data (D9 modules)
// There are two versions of this message but, fortunately, both have a max len of 528
const uint16_t UBX_RXM_PMP_MAX_USER_DATA = 504;
@@ -1702,36 +1762,36 @@ const uint16_t UBX_MON_HW_LEN = 60;
typedef struct
{
uint32_t pinSel; // Mask of pins set as peripheral/PIO
uint32_t pinBank; // Mask of pins set as bank A/B
uint32_t pinDir; // Mask of pins set as input/output
uint32_t pinVal; // Mask of pins value low/high
uint32_t pinSel; // Mask of pins set as peripheral/PIO
uint32_t pinBank; // Mask of pins set as bank A/B
uint32_t pinDir; // Mask of pins set as input/output
uint32_t pinVal; // Mask of pins value low/high
uint16_t noisePerMS; // Noise level as measured by the GPS core
uint16_t agcCnt; // AGC monitor (counts SIGHI xor SIGLO, range 0 to 8191)
uint8_t aStatus; // Status of the antenna supervisor state machine (0=INIT, 1=DONTKNOW, 2=OK, 3=SHORT, 4=OPEN)
uint8_t aPower; // Current power status of antenna (0=OFF, 1=ON, 2=DONTKNOW)
uint16_t agcCnt; // AGC monitor (counts SIGHI xor SIGLO, range 0 to 8191)
uint8_t aStatus; // Status of the antenna supervisor state machine (0=INIT, 1=DONTKNOW, 2=OK, 3=SHORT, 4=OPEN)
uint8_t aPower; // Current power status of antenna (0=OFF, 1=ON, 2=DONTKNOW)
union
{
uint8_t all;
struct
{
uint8_t rtcCalib : 1; // RTC is calibrated
uint8_t safeBoot : 1; // Safeboot mode (0 = inactive, 1 = active)
uint8_t rtcCalib : 1; // RTC is calibrated
uint8_t safeBoot : 1; // Safeboot mode (0 = inactive, 1 = active)
uint8_t jammingState : 2; // Output from jamming/interference monitor (0 = unknown or feature disabled,
// 1 = ok - no significant jamming,
// 2 = warning - interference visible but fix OK,
// 3 = critical - interference visible and no fix)
uint8_t xtalAbsent : 1; // RTC xtal has been determined to be absent
uint8_t xtalAbsent : 1; // RTC xtal has been determined to be absent
} bits;
} flags;
uint8_t reserved1; // Reserved
uint32_t usedMask; // Mask of pins that are used by the virtual pin manager
uint8_t VP[17]; // Array of pin mappings for each of the 17 physical pins
uint8_t jamInd; // CW jamming indicator, scaled (0 = no CW jamming, 255 = strong CW jamming)
uint8_t reserved1; // Reserved
uint32_t usedMask; // Mask of pins that are used by the virtual pin manager
uint8_t VP[17]; // Array of pin mappings for each of the 17 physical pins
uint8_t jamInd; // CW jamming indicator, scaled (0 = no CW jamming, 255 = strong CW jamming)
uint8_t reserved2[2]; // Reserved
uint32_t pinIrq; // Mask of pins value using the PIO Irq
uint32_t pullH; // Mask of pins value using the PIO pull high resistor
uint8_t pullL; // Mask of pins value using the PIO pull low resistor
uint32_t pinIrq; // Mask of pins value using the PIO Irq
uint32_t pullH; // Mask of pins value using the PIO pull high resistor
uint8_t pullL; // Mask of pins value using the PIO pull low resistor
} UBX_MON_HW_data_t;
// UBX-MON-HW2 (0x0A 0x0B): Extended hardware status
@@ -1739,15 +1799,15 @@ const uint16_t UBX_MON_HW2_LEN = 28;
typedef struct
{
int8_t ofsI; // Imbalance of I-part of complex signal, scaled (-128 = max. negative imbalance, 127 = max. positive imbalance)
uint8_t magI; // Magnitude of I-part of complex signal, scaled (0 = no signal, 255 = max. magnitude)
int8_t ofsQ; // Imbalance of Q-part of complex signal, scaled (-128 = max. negative imbalance, 127 = max. positive imbalance)
uint8_t magQ; // Magnitude of Q-part of complex signal, scaled (0 = no signal, 255 = max. magnitude)
int8_t ofsI; // Imbalance of I-part of complex signal, scaled (-128 = max. negative imbalance, 127 = max. positive imbalance)
uint8_t magI; // Magnitude of I-part of complex signal, scaled (0 = no signal, 255 = max. magnitude)
int8_t ofsQ; // Imbalance of Q-part of complex signal, scaled (-128 = max. negative imbalance, 127 = max. positive imbalance)
uint8_t magQ; // Magnitude of Q-part of complex signal, scaled (0 = no signal, 255 = max. magnitude)
uint8_t cfgSource; // Source of low-level configuration (114 = ROM, 111 = OTP, 112 = config pins, 102 = flash image)
uint8_t reserved0[3];
uint32_t lowLevCfg; // Low-level configuration (obsolete for protocol versions greater than 15.00)
uint8_t reserved1[8];
uint32_t postStatus; // POST status word
uint32_t postStatus; // POST status word
uint8_t reserved2[4]; // Reserved
} UBX_MON_HW2_data_t;