Skip to content

Commit b476796

Browse files
authored
Merge pull request #25 from UT2UH/release_candidate
UBX-NAV-TIMELS support added for leap second event
2 parents 039004e + 5c7298c commit b476796

File tree

5 files changed

+352
-0
lines changed

5 files changed

+352
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/*
2+
Getting leap second event info as SNTP Leap Indicator, time to a leap second event and the number of leap seconds since GPS epoch
3+
By: UT2UH
4+
Date: April 14th, 2021
5+
License: MIT. See license file for more information but you can
6+
basically do whatever you want with this code.
7+
8+
This example shows how to query a u-blox module for the leap second event info to cast to SNTP Leap Indicator enumeration.
9+
We also turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic dramatically.
10+
11+
Leave NMEA parsing behind. Now you can simply ask the module for the datums you want!
12+
13+
Feel like supporting open source hardware?
14+
Buy a board from SparkFun!
15+
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
16+
NEO-M8P RTK: https://www.sparkfun.com/products/15005
17+
SAM-M8Q: https://www.sparkfun.com/products/15106
18+
19+
Hardware Connections:
20+
Plug a Qwiic cable into the GNSS and a BlackBoard
21+
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
22+
Open the serial monitor at 115200 baud to see the output
23+
*/
24+
25+
#include <Wire.h> //Needed for I2C to GNSS
26+
27+
#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
28+
SFE_UBLOX_GNSS myGNSS;
29+
30+
typedef enum {
31+
LI_NO_WARNING, //Time leaping not scheduled
32+
LI_LAST_MINUTE_61_SEC, //Last minute has 61 seconds
33+
LI_LAST_MINUTE_59_SEC, //Last minute has 59 seconds
34+
LI_ALARM_CONDITION //The NTP server's clock not synchronized
35+
} ntp_LI_e;
36+
37+
38+
long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.
39+
40+
void setup()
41+
{
42+
Serial.begin(115200);
43+
while (!Serial)
44+
; //Wait for user to open terminal
45+
Serial.println("SparkFun u-blox Example");
46+
47+
Wire.begin();
48+
49+
if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
50+
{
51+
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
52+
while (1)
53+
;
54+
}
55+
56+
// Uncomment the next line if you need to completely reset your module
57+
//myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts
58+
59+
myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
60+
myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR
61+
62+
Serial.println("Compare Unix Epoch given with reference one from https://www.epochconverter.com/");
63+
64+
}
65+
66+
void loop()
67+
{
68+
//Query module only every second. Doing it more often will just cause I2C traffic.
69+
//The module only responds when a new position is available
70+
if (millis() - lastTime > 1000)
71+
{
72+
lastTime = millis(); //Update the timer
73+
74+
// getUnixEpoch marks the PVT data as stale so you will get Unix time and PVT time on alternate seconds
75+
76+
uint32_t us; //microseconds returned by getUnixEpoch()
77+
uint32_t epoch = myGNSS.getUnixEpoch();
78+
Serial.print("Unix Epoch rounded: ");
79+
Serial.print(epoch, DEC);
80+
epoch = myGNSS.getUnixEpoch(us);
81+
Serial.print(" Exact Unix Epoch: ");
82+
Serial.print(epoch, DEC);
83+
Serial.print(" micros: ");
84+
Serial.println(us, DEC);
85+
int32_t timeToLeapSecEvent;
86+
ntp_LI_e leapIndicator = (ntp_LI_e)myGNSS.getLeapIndicator(timeToLeapSecEvent);
87+
Serial.print("NTP LI: ");
88+
Serial.print(leapIndicator, DEC);
89+
switch (leapIndicator){
90+
case LI_NO_WARNING:
91+
Serial.print(" - No event scheduled");
92+
break;
93+
case LI_LAST_MINUTE_61_SEC:
94+
Serial.print(" - last minute will end at 23:60");
95+
break;
96+
case LI_LAST_MINUTE_59_SEC:
97+
Serial.print(" - last minute will end at 23:58");
98+
break;
99+
case LI_ALARM_CONDITION:
100+
default:
101+
Serial.print(" - Unknown (clock not synchronized)");
102+
break;
103+
}
104+
Serial.print(". Time to the next leap second event: ");
105+
Serial.println(timeToLeapSecEvent, DEC);
106+
107+
sfe_ublox_ls_src_e leapSecSource;
108+
Serial.print("Leap seconds since GPS Epoch (Jan 6th, 1980): ");
109+
Serial.print(myGNSS.getCurrentLeapSeconds(leapSecSource), DEC);
110+
switch (leapSecSource){
111+
case SFE_UBLOX_LS_SRC_DEFAULT:
112+
Serial.print(" - hardcoded");
113+
break;
114+
case SFE_UBLOX_LS_SRC_GLONASS:
115+
Serial.print(" - derived from GPS and GLONASS time difference");
116+
break;
117+
case SFE_UBLOX_LS_SRC_GPS:
118+
Serial.print(" - according to GPS");
119+
break;
120+
case SFE_UBLOX_LS_SRC_SBAS:
121+
Serial.print(" - according to SBAS");
122+
break;
123+
case SFE_UBLOX_LS_SRC_BEIDOU:
124+
Serial.print(" - according to BeiDou");
125+
break;
126+
case SFE_UBLOX_LS_SRC_GALILEO:
127+
Serial.print(" - according to Galileo");
128+
break;
129+
case SFE_UBLOX_LS_SRC_AIDED:
130+
Serial.print(" - last minute will end at 23:58");
131+
break;
132+
case SFE_UBLOX_LS_SRC_CONFIGURED:
133+
Serial.print(" - as configured)");
134+
break;
135+
case SFE_UBLOX_LS_SRC_UNKNOWN:
136+
default:
137+
Serial.print(" - source unknown");
138+
break;
139+
}
140+
Serial.println();
141+
}
142+
Serial.println();
143+
}

Diff for: keywords.txt

+7
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ UBX_NAV_HPPOSECEF_data_t KEYWORD1
2828
UBX_NAV_HPPOSLLH_data_t KEYWORD1
2929
UBX_NAV_CLOCK_data_t KEYWORD1
3030
UBX_NAV_RELPOSNED_data_t KEYWORD1
31+
UBX_NAV_TIMELS_data_t KEYWORD1
3132

3233
UBX_RXM_SFRBX_data_t KEYWORD1
3334
UBX_RXM_RAWX_data_t KEYWORD1
@@ -264,6 +265,11 @@ initPacketUBXNAVCLOCK KEYWORD2
264265
flushNAVCLOCK KEYWORD2
265266
logNAVCLOCK KEYWORD2
266267

268+
getLeapSecondEvent KEYWORD2
269+
getLeapIndicator KEYWORD2
270+
getCurrentLeapSeconds KEYWORD2
271+
initPacketUBXNAVTIMELS KEYWORD2
272+
267273
getSurveyStatus KEYWORD2
268274
initPacketUBXNAVSVIN KEYWORD2
269275

@@ -597,6 +603,7 @@ UBX_NAV_RELPOSNED LITERAL1
597603
UBX_NAV_RESETODO LITERAL1
598604
UBX_NAV_STATUS LITERAL1
599605
UBX_NAV_SVIN LITERAL1
606+
UBX_NAV_TIMELS LITERAL1
600607
UBX_NAV_VELECEF LITERAL1
601608
UBX_NAV_VELNED LITERAL1
602609

Diff for: src/SparkFun_u-blox_GNSS_Arduino_Library.cpp

+118
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ void SFE_UBLOX_GNSS::end(void)
8585
delete[] currentGeofenceParams;
8686
currentGeofenceParams = NULL; // Redundant?
8787
}
88+
89+
if (packetUBXNAVTIMELS != NULL)
90+
{
91+
delete[] packetUBXNAVTIMELS;
92+
packetUBXNAVTIMELS = NULL; // Redundant?
93+
}
8894

8995
if (packetUBXNAVPOSECEF != NULL)
9096
{
@@ -792,6 +798,9 @@ boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
792798
case UBX_NAV_CLOCK:
793799
if (packetUBXNAVCLOCK != NULL) result = true;
794800
break;
801+
case UBX_NAV_TIMELS:
802+
if (packetUBXNAVTIMELS != NULL) result = true;
803+
break;
795804
case UBX_NAV_SVIN:
796805
if (packetUBXNAVSVIN != NULL) result = true;
797806
break;
@@ -919,6 +928,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID)
919928
case UBX_NAV_CLOCK:
920929
maxSize = UBX_NAV_CLOCK_LEN;
921930
break;
931+
case UBX_NAV_TIMELS:
932+
maxSize = UBX_NAV_TIMELS_LEN;
933+
break;
922934
case UBX_NAV_SVIN:
923935
maxSize = UBX_NAV_SVIN_LEN;
924936
break;
@@ -2060,6 +2072,26 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
20602072
}
20612073
}
20622074
}
2075+
else if (msg->id == UBX_NAV_TIMELS && msg->len == UBX_NAV_TIMELS_LEN)
2076+
{
2077+
//Parse various byte fields into storage - but only if we have memory allocated for it
2078+
if (packetUBXNAVTIMELS != NULL)
2079+
{
2080+
packetUBXNAVTIMELS->data.iTOW = extractLong(msg, 0);
2081+
packetUBXNAVTIMELS->data.version = extractByte(msg, 4);
2082+
packetUBXNAVTIMELS->data.srcOfCurrLs = extractByte(msg, 8);
2083+
packetUBXNAVTIMELS->data.currLs = extractSignedChar(msg, 9);
2084+
packetUBXNAVTIMELS->data.srcOfLsChange = extractByte(msg, 10);
2085+
packetUBXNAVTIMELS->data.lsChange = extractSignedChar(msg, 11);
2086+
packetUBXNAVTIMELS->data.timeToLsEvent = extractSignedLong(msg, 12);
2087+
packetUBXNAVTIMELS->data.dateOfLsGpsWn = extractInt(msg, 16);
2088+
packetUBXNAVTIMELS->data.dateOfLsGpsDn = extractInt(msg, 18);
2089+
packetUBXNAVTIMELS->data.valid.all = extractSignedChar(msg, 23);
2090+
2091+
//Mark all datums as fresh (not read before)
2092+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0xFFFFFFFF;
2093+
}
2094+
}
20632095
else if (msg->id == UBX_NAV_SVIN && msg->len == UBX_NAV_SVIN_LEN)
20642096
{
20652097
//Parse various byte fields into storage - but only if we have memory allocated for it
@@ -6973,6 +7005,53 @@ void SFE_UBLOX_GNSS::logNAVCLOCK(boolean enabled)
69737005
packetUBXNAVCLOCK->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled;
69747006
}
69757007

7008+
// ***** NAV TIMELS automatic support
7009+
7010+
//Reads leap second event information and sets the global variables
7011+
//for future leap second change and number of leap seconds since GPS epoch
7012+
//Returns true if commands was successful
7013+
boolean SFE_UBLOX_GNSS::getLeapSecondEvent(uint16_t maxWait)
7014+
{
7015+
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
7016+
if (packetUBXNAVTIMELS == NULL) // Abort if the RAM allocation failed
7017+
return (false);
7018+
7019+
packetCfg.cls = UBX_CLASS_NAV;
7020+
packetCfg.id = UBX_NAV_TIMELS;
7021+
packetCfg.len = 0;
7022+
packetCfg.startingSpot = 0;
7023+
7024+
//The data is parsed as part of processing the response
7025+
sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait);
7026+
7027+
if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED)
7028+
return (true);
7029+
7030+
if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN)
7031+
{
7032+
return (true);
7033+
}
7034+
7035+
return (false);
7036+
}
7037+
7038+
// PRIVATE: Allocate RAM for packetUBXNAVTIMELS and initialize it
7039+
boolean SFE_UBLOX_GNSS::initPacketUBXNAVTIMELS()
7040+
{
7041+
packetUBXNAVTIMELS = new UBX_NAV_TIMELS_t; //Allocate RAM for the main struct
7042+
if (packetUBXNAVTIMELS == NULL)
7043+
{
7044+
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
7045+
_debugSerial->println(F("initPacketUBXNAVTIMELS: PANIC! RAM allocation failed!"));
7046+
return (false);
7047+
}
7048+
packetUBXNAVTIMELS->automaticFlags.flags.all = 0;
7049+
packetUBXNAVTIMELS->callbackPointer = NULL;
7050+
packetUBXNAVTIMELS->callbackData = NULL;
7051+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0;
7052+
return (true);
7053+
}
7054+
69767055
// ***** NAV SVIN automatic support
69777056

69787057
//Reads survey in status and sets the global variables
@@ -10304,6 +10383,45 @@ float SFE_UBLOX_GNSS::getSurveyInMeanAccuracy(uint16_t maxWait) // Returned as m
1030410383
return (((float)tempFloat) / 10000.0); //Convert 0.1mm to m
1030510384
}
1030610385

10386+
// ***** TIMELS Helper Functions
10387+
10388+
uint8_t SFE_UBLOX_GNSS::getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait)
10389+
{
10390+
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
10391+
if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed
10392+
return 3;
10393+
10394+
if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent == false)
10395+
getLeapSecondEvent(maxWait);
10396+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent = false; //Since we are about to give this to user, mark this data as stale
10397+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.lsChange = false;
10398+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.timeToLsEvent = false;
10399+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false;
10400+
timeToLsEvent = packetUBXNAVTIMELS->data.timeToLsEvent;
10401+
// returns NTP Leap Indicator
10402+
// 0 -no warning
10403+
// 1 -last minute of the day has 61 seconds
10404+
// 2 -last minute of the day has 59 seconds
10405+
// 3 -unknown (clock unsynchronized)
10406+
return ((boolean)packetUBXNAVTIMELS->data.valid.bits.validTimeToLsEvent ? (uint8_t)(packetUBXNAVTIMELS->data.lsChange == -1 ? 2 : packetUBXNAVTIMELS->data.lsChange) : 3);
10407+
}
10408+
10409+
int8_t SFE_UBLOX_GNSS::getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait)
10410+
{
10411+
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
10412+
if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed
10413+
return false;
10414+
10415+
if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs == false)
10416+
getLeapSecondEvent(maxWait);
10417+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs = false; //Since we are about to give this to user, mark this data as stale
10418+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.srcOfCurrLs = false;
10419+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.currLs = false;
10420+
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false;
10421+
source = ((sfe_ublox_ls_src_e)packetUBXNAVTIMELS->data.srcOfCurrLs);
10422+
return ((int8_t)packetUBXNAVTIMELS->data.currLs);
10423+
}
10424+
1030710425
// ***** RELPOSNED Helper Functions and automatic support
1030810426

1030910427
float SFE_UBLOX_GNSS::getRelPosN(uint16_t maxWait) // Returned as m

Diff for: src/SparkFun_u-blox_GNSS_Arduino_Library.h

+24
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,20 @@ enum sfe_ublox_gnss_ids_e
472472
SFE_UBLOX_GNSS_ID_GLONASS
473473
};
474474

475+
// The GNSS identifiers of leap second event info source - used by UBX-NAV-TIMELS
476+
enum sfe_ublox_ls_src_e
477+
{
478+
SFE_UBLOX_LS_SRC_DEFAULT,
479+
SFE_UBLOX_LS_SRC_GLONASS,
480+
SFE_UBLOX_LS_SRC_GPS,
481+
SFE_UBLOX_LS_SRC_SBAS,
482+
SFE_UBLOX_LS_SRC_BEIDOU,
483+
SFE_UBLOX_LS_SRC_GALILEO,
484+
SFE_UBLOX_LS_SRC_AIDED,
485+
SFE_UBLOX_LS_SRC_CONFIGURED,
486+
SFE_UBLOX_LS_SRC_UNKNOWN = 255
487+
};
488+
475489
#ifndef MAX_PAYLOAD_SIZE
476490
// v2.0: keep this for backwards-compatibility, but this is largely superseded by setPacketCfgPayloadSize
477491
#define MAX_PAYLOAD_SIZE 256 //We need ~220 bytes for getProtocolVersion on most ublox modules
@@ -865,6 +879,9 @@ class SFE_UBLOX_GNSS
865879
// Add "auto" support for NAV SVIN - to avoid needing 'global' storage
866880
boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status
867881

882+
// Add "auto" support for NAV TIMELS - to avoid needing 'global' storage
883+
boolean getLeapSecondEvent(uint16_t maxWait); //Reads leap second event info
884+
868885
boolean getRELPOSNED(uint16_t maxWait = defaultMaxWait); //Get Relative Positioning Information of the NED frame
869886
boolean setAutoRELPOSNED(boolean enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic RELPOSNED reports
870887
boolean setAutoRELPOSNED(boolean enabled, boolean implicitUpdate, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic RELPOSNED, 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
@@ -1102,6 +1119,11 @@ class SFE_UBLOX_GNSS
11021119
uint16_t getSurveyInObservationTime(uint16_t maxWait = defaultMaxWait); // Truncated to 65535 seconds
11031120
float getSurveyInMeanAccuracy(uint16_t maxWait = defaultMaxWait); // Returned as m
11041121

1122+
// Helper functions for TIMELS
1123+
1124+
uint8_t getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait = defaultMaxWait);
1125+
int8_t getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait = defaultMaxWait);
1126+
11051127
// Helper functions for RELPOSNED
11061128

11071129
float getRelPosN(uint16_t maxWait = defaultMaxWait); // Returned as m
@@ -1154,6 +1176,7 @@ class SFE_UBLOX_GNSS
11541176
UBX_NAV_HPPOSECEF_t *packetUBXNAVHPPOSECEF = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
11551177
UBX_NAV_HPPOSLLH_t *packetUBXNAVHPPOSLLH = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
11561178
UBX_NAV_CLOCK_t *packetUBXNAVCLOCK = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
1179+
UBX_NAV_TIMELS_t *packetUBXNAVTIMELS = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
11571180
UBX_NAV_SVIN_t *packetUBXNAVSVIN = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
11581181
UBX_NAV_RELPOSNED_t *packetUBXNAVRELPOSNED = NULL; // Pointer to struct. RAM will be allocated for this if/when necessary
11591182

@@ -1227,6 +1250,7 @@ class SFE_UBLOX_GNSS
12271250
boolean initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it
12281251
boolean initPacketUBXNAVHPPOSLLH(); // Allocate RAM for packetUBXNAVHPPOSLLH and initialize it
12291252
boolean initPacketUBXNAVCLOCK(); // Allocate RAM for packetUBXNAVCLOCK and initialize it
1253+
boolean initPacketUBXNAVTIMELS(); // Allocate RAM for packetUBXNAVTIMELS and initialize it
12301254
boolean initPacketUBXNAVSVIN(); // Allocate RAM for packetUBXNAVSVIN and initialize it
12311255
boolean initPacketUBXNAVRELPOSNED(); // Allocate RAM for packetUBXNAVRELPOSNED and initialize it
12321256
boolean initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it

0 commit comments

Comments
 (0)