Skip to content

Commit 213d29c

Browse files
committed
Add setDynamicSPARTNKeys. Update Example19
1 parent b70cb1c commit 213d29c

File tree

3 files changed

+220
-28
lines changed

3 files changed

+220
-28
lines changed

examples/ZED-F9P/Example19_LBand_Corrections_with_NEO-D9S/Example19_LBand_Corrections_with_NEO-D9S.ino

+24-7
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
1111
This is a proof of concept to show how the UBX-RXM-PMP corrections control the accuracy.
1212
13+
You will need a Thingstream PointPerfect account to be able to access the SPARTN Credentials (IP Dynamic Keys).
14+
Copy and paste the Current Key and Next Key into secrets.h.
15+
1316
Feel like supporting open source hardware?
1417
Buy a board from SparkFun!
1518
ZED-F9P RTK2: https://www.sparkfun.com/products/16481
@@ -21,6 +24,8 @@
2124
Open the serial monitor at 115200 baud to see the output
2225
*/
2326

27+
#include "secrets.h" // <- Copy and paste the Current Key and Next Key into secrets.h
28+
2429
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
2530
SFE_UBLOX_GNSS myGNSS; // ZED-F9x
2631
SFE_UBLOX_GNSS myLBand; // NEO-D9S
@@ -141,16 +146,27 @@ void setup()
141146
}
142147
Serial.println(F("u-blox GNSS module connected"));
143148

144-
myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
145-
myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
149+
uint8_t ok = myGNSS.setI2COutput(COM_TYPE_UBX); //Turn off NMEA noise
150+
if (ok) ok = myGNSS.setPortInput(COM_PORT_I2C, COM_TYPE_UBX | COM_TYPE_NMEA | COM_TYPE_SPARTN); //Be sure SPARTN input is enabled
146151

147-
myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
152+
if (ok) ok = myGNSS.setDGNSSConfiguration(SFE_UBLOX_DGNSS_MODE_FIXED); // Set the differential mode - ambiguities are fixed whenever possible
148153

149-
myGNSS.setNavigationFrequency(1); //Set output in Hz.
154+
if (ok) ok = myGNSS.setNavigationFrequency(1); //Set output in Hz.
150155

151-
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
156+
//Configure the SPARTN IP Dynamic Keys
157+
//"When the receiver boots, the host should send 'current' and 'next' keys in one message." - Use setDynamicSPARTNKeys for this.
158+
//"Every time the 'current' key is expired, 'next' takes its place."
159+
//"Therefore the host should then retrieve the new 'next' key and send only that." - Use setDynamicSPARTNKey for this.
160+
// The key can be provided in binary format or in ASCII Hex format, but in both cases keyLengthBytes _must_ represent the binary key length in bytes.
161+
if (ok) ok = myGNSS.setDynamicSPARTNKeys(currentKeyLengthBytes, currentKeyGPSWeek, currentKeyGPSToW, currentDynamicKey,
162+
nextKeyLengthBytes, nextKeyGPSWeek, nextKeyGPSToW, nextDynamicKey);
152163

153-
//myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
164+
//if (ok) ok = myGNSS.saveConfiguration(VAL_CFG_SUBSEC_IOPORT | VAL_CFG_SUBSEC_MSGCONF); //Optional: Save the ioPort and message settings to NVM
165+
166+
Serial.print(F("GNSS: configuration "));
167+
Serial.println(OK(ok));
168+
169+
myGNSS.setAutoPVTcallbackPtr(&printPVTdata); // Enable automatic NAV PVT messages with callback to printPVTdata so we can watch the carrier solution go to fixed
154170

155171
//=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
156172
// Begin and configure the NEO-D9S L-Band receiver
@@ -164,7 +180,7 @@ void setup()
164180
}
165181
Serial.println(F("u-blox NEO-D9S connected"));
166182

167-
uint8_t ok = myLBand.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, myLBandFreq); // Default 1539812500 Hz
183+
ok = myLBand.setVal32(UBLOX_CFG_PMP_CENTER_FREQUENCY, myLBandFreq); // Default 1539812500 Hz
168184
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SEARCH_WINDOW, 2200); // Default 2200 Hz
169185
if (ok) ok = myLBand.setVal8(UBLOX_CFG_PMP_USE_SERVICE_ID, 0); // Default 1
170186
if (ok) ok = myLBand.setVal16(UBLOX_CFG_PMP_SERVICE_ID, 21845); // Default 50821
@@ -178,6 +194,7 @@ void setup()
178194
if (ok) ok = myLBand.setVal(UBLOX_CFG_MSGOUT_UBX_RXM_PMP_UART2, 1); // Output UBX-RXM-PMP on UART2
179195
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART1_BAUDRATE, 38400); // match baudrate with ZED default
180196
if (ok) ok = myLBand.setVal32(UBLOX_CFG_UART2_BAUDRATE, 38400); // match baudrate with ZED default
197+
181198
Serial.print(F("L-Band: configuration "));
182199
Serial.println(OK(ok));
183200

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// You can set the information below after signing up with the u-blox Thingstream portal
2+
// and adding a new New PointPerfect Thing
3+
// https://portal.thingstream.io/app/location-services/things
4+
// In the new PointPerfect Thing, you go to the credentials tab and copy and paste the IP Dynamic Keys here.
5+
//
6+
// The keys are valid from a particular GPS Week Number and Time of Week.
7+
// Looking at the credentials tab, the current key expires 23:59 Feb 11th 2022.
8+
// This means the next key is valid _from_ Midnight Feb 12th 2022.
9+
// That is GPS Week 2196. The GPS Time of Week in seconds is 518418.
10+
// Working backwards, the current key became valid exactly 4 weeks earlier (Midnight Jan 15th 2022).
11+
//
12+
// See: https://www.labsat.co.uk/index.php/en/gps-time-calculator
13+
//
14+
// The keys are given as: 32 hexadecimal digits = 128 bits = 16 Bytes
15+
16+
const uint8_t currentKeyLengthBytes = 16;
17+
const uint8_t currentDynamicKey[] = "f742bd6b7248043177dd649141d8fb0b";
18+
const uint16_t currentKeyGPSWeek = 2192;
19+
const uint32_t currentKeyGPSToW = 518418;
20+
21+
const uint8_t nextKeyLengthBytes = 16;
22+
const uint8_t nextDynamicKey[] = "8206........................29f4";
23+
const uint16_t nextKeyGPSWeek = 2196;
24+
const uint32_t nextKeyGPSToW = 518418;

src/SparkFun_u-blox_GNSS_Arduino_Library.cpp

+172-21
Original file line numberDiff line numberDiff line change
@@ -7585,27 +7585,6 @@ bool SFE_UBLOX_GNSS::setAopCfg(uint8_t aopCfg, uint16_t aopOrbMaxErr, uint16_t m
75857585
// The key can be provided in binary format or in ASCII Hex format, but in both cases keyLengthBytes _must_ represent the binary key length in bytes.
75867586
bool SFE_UBLOX_GNSS::setDynamicSPARTNKey(uint8_t keyLengthBytes, uint16_t validFromWno, uint32_t validFromTow, const uint8_t *key, uint16_t maxWait)
75877587
{
7588-
// Check if all keyLengthBytes are ASCII Hex 0-9, a-f, A-F
7589-
bool isASCIIHex = true;
7590-
uint16_t i = 0;
7591-
while ((i < (uint16_t)keyLengthBytes) && (isASCIIHex == true))
7592-
{
7593-
if (((key[i] >= '0') && (key[i] <= '9')) || ((key[i] >= 'a') && (key[i] <= 'f')) || ((key[i] >= 'A') && (key[i] <= 'F')))
7594-
i++; // Keep checking if data is all ASCII Hex
7595-
else
7596-
isASCIIHex = false; // Data is binary
7597-
}
7598-
if (isASCIIHex) // Check the second half of the ASCII Hex key
7599-
{
7600-
while ((i < ((uint16_t)keyLengthBytes * 2) && (isASCIIHex == true)))
7601-
{
7602-
if (((key[i] >= '0') && (key[i] <= '9')) || ((key[i] >= 'a') && (key[i] <= 'f')) || ((key[i] >= 'A') && (key[i] <= 'F')))
7603-
i++; // Keep checking if data is all ASCII Hex
7604-
else
7605-
isASCIIHex = false; // Data is binary
7606-
}
7607-
}
7608-
76097588
// Check if there is room for the key in packetCfg. Resize the buffer if not.
76107589
size_t payloadLength = (size_t)keyLengthBytes + 12;
76117590
if (packetCfgPayloadSize < payloadLength)
@@ -7635,6 +7614,27 @@ bool SFE_UBLOX_GNSS::setDynamicSPARTNKey(uint8_t keyLengthBytes, uint16_t validF
76357614
payloadCfg[10] = (validFromTow >> 16) & 0xFF;
76367615
payloadCfg[11] = (validFromTow >> 24) & 0xFF;
76377616

7617+
// Check if all keyLengthBytes are ASCII Hex 0-9, a-f, A-F
7618+
bool isASCIIHex = true;
7619+
uint16_t i = 0;
7620+
while ((i < (uint16_t)keyLengthBytes) && (isASCIIHex == true))
7621+
{
7622+
if (((key[i] >= '0') && (key[i] <= '9')) || ((key[i] >= 'a') && (key[i] <= 'f')) || ((key[i] >= 'A') && (key[i] <= 'F')))
7623+
i++; // Keep checking if data is all ASCII Hex
7624+
else
7625+
isASCIIHex = false; // Data is binary
7626+
}
7627+
if (isASCIIHex) // Check the second half of the ASCII Hex key
7628+
{
7629+
while ((i < ((uint16_t)keyLengthBytes * 2) && (isASCIIHex == true)))
7630+
{
7631+
if (((key[i] >= '0') && (key[i] <= '9')) || ((key[i] >= 'a') && (key[i] <= 'f')) || ((key[i] >= 'A') && (key[i] <= 'F')))
7632+
i++; // Keep checking if data is all ASCII Hex
7633+
else
7634+
isASCIIHex = false; // Data is binary
7635+
}
7636+
}
7637+
76387638
if (isASCIIHex) // Convert ASCII Hex key to binary
76397639
{
76407640
for (i = 0; i < ((uint16_t)keyLengthBytes * 2); i += 2)
@@ -7677,6 +7677,157 @@ bool SFE_UBLOX_GNSS::setDynamicSPARTNKey(uint8_t keyLengthBytes, uint16_t validF
76777677
bool SFE_UBLOX_GNSS::setDynamicSPARTNKeys(uint8_t keyLengthBytes1, uint16_t validFromWno1, uint32_t validFromTow1, const uint8_t *key1,
76787678
uint8_t keyLengthBytes2, uint16_t validFromWno2, uint32_t validFromTow2, const uint8_t *key2, uint16_t maxWait)
76797679
{
7680+
// Check if there is room for the key in packetCfg. Resize the buffer if not.
7681+
size_t payloadLength = (size_t)keyLengthBytes1 + (size_t)keyLengthBytes2 + 20;
7682+
if (packetCfgPayloadSize < payloadLength)
7683+
{
7684+
if (!setPacketCfgPayloadSize(payloadLength)) // Check if the resize was successful
7685+
{
7686+
return (false);
7687+
}
7688+
}
7689+
7690+
// Copy the key etc. into packetCfg
7691+
packetCfg.cls = UBX_CLASS_RXM;
7692+
packetCfg.id = UBX_RXM_SPARTNKEY;
7693+
packetCfg.len = payloadLength;
7694+
packetCfg.startingSpot = 0;
7695+
7696+
payloadCfg[0] = 0x01; // version
7697+
payloadCfg[1] = 0x02; // numKeys
7698+
payloadCfg[2] = 0x00; // reserved0
7699+
payloadCfg[3] = 0x00; // reserved0
7700+
payloadCfg[4] = 0x00; // reserved1
7701+
payloadCfg[5] = keyLengthBytes1;
7702+
payloadCfg[6] = validFromWno1 & 0xFF; // validFromWno little-endian
7703+
payloadCfg[7] = validFromWno1 >> 8;
7704+
payloadCfg[8] = validFromTow1 & 0xFF; // validFromTow little-endian
7705+
payloadCfg[9] = (validFromTow1 >> 8) & 0xFF;
7706+
payloadCfg[10] = (validFromTow1 >> 16) & 0xFF;
7707+
payloadCfg[11] = (validFromTow1 >> 24) & 0xFF;
7708+
payloadCfg[12] = 0x00; // reserved1
7709+
payloadCfg[13] = keyLengthBytes2;
7710+
payloadCfg[14] = validFromWno2 & 0xFF; // validFromWno little-endian
7711+
payloadCfg[15] = validFromWno2 >> 8;
7712+
payloadCfg[16] = validFromTow2 & 0xFF; // validFromTow little-endian
7713+
payloadCfg[17] = (validFromTow2 >> 8) & 0xFF;
7714+
payloadCfg[18] = (validFromTow2 >> 16) & 0xFF;
7715+
payloadCfg[19] = (validFromTow2 >> 24) & 0xFF;
7716+
7717+
// Check if all keyLengthBytes are ASCII Hex 0-9, a-f, A-F
7718+
bool isASCIIHex = true;
7719+
uint16_t i = 0;
7720+
while ((i < (uint16_t)keyLengthBytes1) && (isASCIIHex == true))
7721+
{
7722+
if (((key1[i] >= '0') && (key1[i] <= '9')) || ((key1[i] >= 'a') && (key1[i] <= 'f')) || ((key1[i] >= 'A') && (key1[i] <= 'F')))
7723+
i++; // Keep checking if data is all ASCII Hex
7724+
else
7725+
isASCIIHex = false; // Data is binary
7726+
}
7727+
if (isASCIIHex) // Check the second half of the ASCII Hex key
7728+
{
7729+
while ((i < ((uint16_t)keyLengthBytes1 * 2) && (isASCIIHex == true)))
7730+
{
7731+
if (((key1[i] >= '0') && (key1[i] <= '9')) || ((key1[i] >= 'a') && (key1[i] <= 'f')) || ((key1[i] >= 'A') && (key1[i] <= 'F')))
7732+
i++; // Keep checking if data is all ASCII Hex
7733+
else
7734+
isASCIIHex = false; // Data is binary
7735+
}
7736+
}
7737+
7738+
if (isASCIIHex) // Convert ASCII Hex key to binary
7739+
{
7740+
for (i = 0; i < ((uint16_t)keyLengthBytes1 * 2); i += 2)
7741+
{
7742+
if ((key1[i] >= '0') && (key1[i] <= '9'))
7743+
{
7744+
payloadCfg[20 + (i >> 1)] = (key1[i] - '0') << 4;
7745+
}
7746+
else if ((key1[i] >= 'a') && (key1[i] <= 'f'))
7747+
{
7748+
payloadCfg[20 + (i >> 1)] = (key1[i] + 10 - 'a') << 4;
7749+
}
7750+
else // if ((key1[i] >= 'A') && (key1[i] <= 'F'))
7751+
{
7752+
payloadCfg[20 + (i >> 1)] = (key1[i] + 10 - 'A') << 4;
7753+
}
7754+
7755+
if ((key1[i + 1] >= '0') && (key1[i + 1] <= '9'))
7756+
{
7757+
payloadCfg[20 + (i >> 1)] |= key1[i + 1] - '0';
7758+
}
7759+
else if ((key1[i + 1] >= 'a') && (key1[i + 1] <= 'f'))
7760+
{
7761+
payloadCfg[20 + (i >> 1)] |= key1[i + 1] + 10 - 'a';
7762+
}
7763+
else // if ((key1[i + 1] >= 'A') && (key1[i + 1] <= 'F'))
7764+
{
7765+
payloadCfg[20 + (i >> 1)] |= key1[i + 1] + 10 - 'A';
7766+
}
7767+
}
7768+
}
7769+
else // Binary key
7770+
{
7771+
memcpy(&payloadCfg[20], key1, keyLengthBytes1);
7772+
}
7773+
7774+
// Check if all keyLengthBytes are ASCII Hex 0-9, a-f, A-F
7775+
isASCIIHex = true;
7776+
i = 0;
7777+
while ((i < (uint16_t)keyLengthBytes2) && (isASCIIHex == true))
7778+
{
7779+
if (((key2[i] >= '0') && (key2[i] <= '9')) || ((key2[i] >= 'a') && (key2[i] <= 'f')) || ((key2[i] >= 'A') && (key2[i] <= 'F')))
7780+
i++; // Keep checking if data is all ASCII Hex
7781+
else
7782+
isASCIIHex = false; // Data is binary
7783+
}
7784+
if (isASCIIHex) // Check the second half of the ASCII Hex key
7785+
{
7786+
while ((i < ((uint16_t)keyLengthBytes2 * 2) && (isASCIIHex == true)))
7787+
{
7788+
if (((key2[i] >= '0') && (key2[i] <= '9')) || ((key2[i] >= 'a') && (key2[i] <= 'f')) || ((key2[i] >= 'A') && (key2[i] <= 'F')))
7789+
i++; // Keep checking if data is all ASCII Hex
7790+
else
7791+
isASCIIHex = false; // Data is binary
7792+
}
7793+
}
7794+
7795+
if (isASCIIHex) // Convert ASCII Hex key to binary
7796+
{
7797+
for (i = 0; i < ((uint16_t)keyLengthBytes2 * 2); i += 2)
7798+
{
7799+
if ((key2[i] >= '0') && (key2[i] <= '9'))
7800+
{
7801+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] = (key2[i] - '0') << 4;
7802+
}
7803+
else if ((key2[i] >= 'a') && (key2[i] <= 'f'))
7804+
{
7805+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] = (key2[i] + 10 - 'a') << 4;
7806+
}
7807+
else // if ((key2[i] >= 'A') && (key2[i] <= 'F'))
7808+
{
7809+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] = (key2[i] + 10 - 'A') << 4;
7810+
}
7811+
7812+
if ((key2[i + 1] >= '0') && (key2[i + 1] <= '9'))
7813+
{
7814+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] |= key2[i + 1] - '0';
7815+
}
7816+
else if ((key2[i + 1] >= 'a') && (key2[i + 1] <= 'f'))
7817+
{
7818+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] |= key2[i + 1] + 10 - 'a';
7819+
}
7820+
else // if ((key2[i + 1] >= 'A') && (key2[i + 1] <= 'F'))
7821+
{
7822+
payloadCfg[20 + keyLengthBytes1 + (i >> 1)] |= key2[i + 1] + 10 - 'A';
7823+
}
7824+
}
7825+
}
7826+
else // Binary key
7827+
{
7828+
memcpy(&payloadCfg[20 + keyLengthBytes1], key2, keyLengthBytes2);
7829+
}
7830+
76807831
return (sendCommand(&packetCfg, maxWait) == SFE_UBLOX_STATUS_DATA_SENT); // We are only expecting an ACK
76817832
}
76827833

0 commit comments

Comments
 (0)