Skip to content

UBX-NAV-TIMELS support added for leap second event #25

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 15, 2021
143 changes: 143 additions & 0 deletions examples/Example28_GetLeapSecondInfo/Example28_GetLeapSecondInfo.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
/*
Getting leap second event info as SNTP Leap Indicator, time to a leap second event and the number of leap seconds since GPS epoch
By: UT2UH
Date: April 14th, 2021
License: MIT. See license file for more information but you can
basically do whatever you want with this code.

This example shows how to query a u-blox module for the leap second event info to cast to SNTP Leap Indicator enumeration.
We also turn off the NMEA output on the I2C port. This decreases the amount of I2C traffic dramatically.

Leave NMEA parsing behind. Now you can simply ask the module for the datums you want!

Feel like supporting open source hardware?
Buy a board from SparkFun!
ZED-F9P RTK2: https://www.sparkfun.com/products/15136
NEO-M8P RTK: https://www.sparkfun.com/products/15005
SAM-M8Q: https://www.sparkfun.com/products/15106

Hardware Connections:
Plug a Qwiic cable into the GNSS and a BlackBoard
If you don't have a platform with a Qwiic connection use the SparkFun Qwiic Breadboard Jumper (https://www.sparkfun.com/products/14425)
Open the serial monitor at 115200 baud to see the output
*/

#include <Wire.h> //Needed for I2C to GNSS

#include <SparkFun_u-blox_GNSS_Arduino_Library.h> //http://librarymanager/All#SparkFun_u-blox_GNSS
SFE_UBLOX_GNSS myGNSS;

typedef enum {
LI_NO_WARNING, //Time leaping not scheduled
LI_LAST_MINUTE_61_SEC, //Last minute has 61 seconds
LI_LAST_MINUTE_59_SEC, //Last minute has 59 seconds
LI_ALARM_CONDITION //The NTP server's clock not synchronized
} ntp_LI_e;


long lastTime = 0; //Simple local timer. Limits amount if I2C traffic to u-blox module.

void setup()
{
Serial.begin(115200);
while (!Serial)
; //Wait for user to open terminal
Serial.println("SparkFun u-blox Example");

Wire.begin();

if (myGNSS.begin() == false) //Connect to the u-blox module using Wire port
{
Serial.println(F("u-blox GNSS not detected at default I2C address. Please check wiring. Freezing."));
while (1)
;
}

// Uncomment the next line if you need to completely reset your module
//myGNSS.factoryDefault(); delay(5000); // Reset everything and wait while the module restarts

myGNSS.setI2COutput(COM_TYPE_UBX); //Set the I2C port to output UBX only (turn off NMEA noise)
myGNSS.saveConfiguration(); //Optional: Save the current settings to flash and BBR

Serial.println("Compare Unix Epoch given with reference one from https://www.epochconverter.com/");

}

void loop()
{
//Query module only every second. Doing it more often will just cause I2C traffic.
//The module only responds when a new position is available
if (millis() - lastTime > 1000)
{
lastTime = millis(); //Update the timer

// getUnixEpoch marks the PVT data as stale so you will get Unix time and PVT time on alternate seconds

uint32_t us; //microseconds returned by getUnixEpoch()
uint32_t epoch = myGNSS.getUnixEpoch();
Serial.print("Unix Epoch rounded: ");
Serial.print(epoch, DEC);
epoch = myGNSS.getUnixEpoch(us);
Serial.print(" Exact Unix Epoch: ");
Serial.print(epoch, DEC);
Serial.print(" micros: ");
Serial.println(us, DEC);
int32_t timeToLeapSecEvent;
ntp_LI_e leapIndicator = (ntp_LI_e)myGNSS.getLeapIndicator(timeToLeapSecEvent);
Serial.print("NTP LI: ");
Serial.print(leapIndicator, DEC);
switch (leapIndicator){
case LI_NO_WARNING:
Serial.print(" - No event scheduled");
break;
case LI_LAST_MINUTE_61_SEC:
Serial.print(" - last minute will end at 23:60");
break;
case LI_LAST_MINUTE_59_SEC:
Serial.print(" - last minute will end at 23:58");
break;
case LI_ALARM_CONDITION:
default:
Serial.print(" - Unknown (clock not synchronized)");
break;
}
Serial.print(". Time to the next leap second event: ");
Serial.println(timeToLeapSecEvent, DEC);

sfe_ublox_ls_src_e leapSecSource;
Serial.print("Leap seconds since GPS Epoch (Jan 6th, 1980): ");
Serial.print(myGNSS.getCurrentLeapSeconds(leapSecSource), DEC);
switch (leapSecSource){
case SFE_UBLOX_LS_SRC_DEFAULT:
Serial.print(" - hardcoded");
break;
case SFE_UBLOX_LS_SRC_GLONASS:
Serial.print(" - derived from GPS and GLONASS time difference");
break;
case SFE_UBLOX_LS_SRC_GPS:
Serial.print(" - according to GPS");
break;
case SFE_UBLOX_LS_SRC_SBAS:
Serial.print(" - according to SBAS");
break;
case SFE_UBLOX_LS_SRC_BEIDOU:
Serial.print(" - according to BeiDou");
break;
case SFE_UBLOX_LS_SRC_GALILEO:
Serial.print(" - according to Galileo");
break;
case SFE_UBLOX_LS_SRC_AIDED:
Serial.print(" - last minute will end at 23:58");
break;
case SFE_UBLOX_LS_SRC_CONFIGURED:
Serial.print(" - as configured)");
break;
case SFE_UBLOX_LS_SRC_UNKNOWN:
default:
Serial.print(" - source unknown");
break;
}
Serial.println();
}
Serial.println();
}
7 changes: 7 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ UBX_NAV_HPPOSECEF_data_t KEYWORD1
UBX_NAV_HPPOSLLH_data_t KEYWORD1
UBX_NAV_CLOCK_data_t KEYWORD1
UBX_NAV_RELPOSNED_data_t KEYWORD1
UBX_NAV_TIMELS_data_t KEYWORD1

UBX_RXM_SFRBX_data_t KEYWORD1
UBX_RXM_RAWX_data_t KEYWORD1
Expand Down Expand Up @@ -264,6 +265,11 @@ initPacketUBXNAVCLOCK KEYWORD2
flushNAVCLOCK KEYWORD2
logNAVCLOCK KEYWORD2

getLeapSecondEvent KEYWORD2
getLeapIndicator KEYWORD2
getCurrentLeapSeconds KEYWORD2
initPacketUBXNAVTIMELS KEYWORD2

getSurveyStatus KEYWORD2
initPacketUBXNAVSVIN KEYWORD2

Expand Down Expand Up @@ -597,6 +603,7 @@ UBX_NAV_RELPOSNED LITERAL1
UBX_NAV_RESETODO LITERAL1
UBX_NAV_STATUS LITERAL1
UBX_NAV_SVIN LITERAL1
UBX_NAV_TIMELS LITERAL1
UBX_NAV_VELECEF LITERAL1
UBX_NAV_VELNED LITERAL1

Expand Down
118 changes: 118 additions & 0 deletions src/SparkFun_u-blox_GNSS_Arduino_Library.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,12 @@ void SFE_UBLOX_GNSS::end(void)
delete[] currentGeofenceParams;
currentGeofenceParams = NULL; // Redundant?
}

if (packetUBXNAVTIMELS != NULL)
{
delete[] packetUBXNAVTIMELS;
packetUBXNAVTIMELS = NULL; // Redundant?
}

if (packetUBXNAVPOSECEF != NULL)
{
Expand Down Expand Up @@ -792,6 +798,9 @@ boolean SFE_UBLOX_GNSS::checkAutomatic(uint8_t Class, uint8_t ID)
case UBX_NAV_CLOCK:
if (packetUBXNAVCLOCK != NULL) result = true;
break;
case UBX_NAV_TIMELS:
if (packetUBXNAVTIMELS != NULL) result = true;
break;
case UBX_NAV_SVIN:
if (packetUBXNAVSVIN != NULL) result = true;
break;
Expand Down Expand Up @@ -919,6 +928,9 @@ uint16_t SFE_UBLOX_GNSS::getMaxPayloadSize(uint8_t Class, uint8_t ID)
case UBX_NAV_CLOCK:
maxSize = UBX_NAV_CLOCK_LEN;
break;
case UBX_NAV_TIMELS:
maxSize = UBX_NAV_TIMELS_LEN;
break;
case UBX_NAV_SVIN:
maxSize = UBX_NAV_SVIN_LEN;
break;
Expand Down Expand Up @@ -2060,6 +2072,26 @@ void SFE_UBLOX_GNSS::processUBXpacket(ubxPacket *msg)
}
}
}
else if (msg->id == UBX_NAV_TIMELS && msg->len == UBX_NAV_TIMELS_LEN)
{
//Parse various byte fields into storage - but only if we have memory allocated for it
if (packetUBXNAVTIMELS != NULL)
{
packetUBXNAVTIMELS->data.iTOW = extractLong(msg, 0);
packetUBXNAVTIMELS->data.version = extractByte(msg, 4);
packetUBXNAVTIMELS->data.srcOfCurrLs = extractByte(msg, 8);
packetUBXNAVTIMELS->data.currLs = extractSignedChar(msg, 9);
packetUBXNAVTIMELS->data.srcOfLsChange = extractByte(msg, 10);
packetUBXNAVTIMELS->data.lsChange = extractSignedChar(msg, 11);
packetUBXNAVTIMELS->data.timeToLsEvent = extractSignedLong(msg, 12);
packetUBXNAVTIMELS->data.dateOfLsGpsWn = extractInt(msg, 16);
packetUBXNAVTIMELS->data.dateOfLsGpsDn = extractInt(msg, 18);
packetUBXNAVTIMELS->data.valid.all = extractSignedChar(msg, 23);

//Mark all datums as fresh (not read before)
packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0xFFFFFFFF;
}
}
else if (msg->id == UBX_NAV_SVIN && msg->len == UBX_NAV_SVIN_LEN)
{
//Parse various byte fields into storage - but only if we have memory allocated for it
Expand Down Expand Up @@ -6973,6 +7005,53 @@ void SFE_UBLOX_GNSS::logNAVCLOCK(boolean enabled)
packetUBXNAVCLOCK->automaticFlags.flags.bits.addToFileBuffer = (uint8_t)enabled;
}

// ***** NAV TIMELS automatic support

//Reads leap second event information and sets the global variables
//for future leap second change and number of leap seconds since GPS epoch
//Returns true if commands was successful
boolean SFE_UBLOX_GNSS::getLeapSecondEvent(uint16_t maxWait)
{
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
if (packetUBXNAVTIMELS == NULL) // Abort if the RAM allocation failed
return (false);

packetCfg.cls = UBX_CLASS_NAV;
packetCfg.id = UBX_NAV_TIMELS;
packetCfg.len = 0;
packetCfg.startingSpot = 0;

//The data is parsed as part of processing the response
sfe_ublox_status_e retVal = sendCommand(&packetCfg, maxWait);

if (retVal == SFE_UBLOX_STATUS_DATA_RECEIVED)
return (true);

if (retVal == SFE_UBLOX_STATUS_DATA_OVERWRITTEN)
{
return (true);
}

return (false);
}

// PRIVATE: Allocate RAM for packetUBXNAVTIMELS and initialize it
boolean SFE_UBLOX_GNSS::initPacketUBXNAVTIMELS()
{
packetUBXNAVTIMELS = new UBX_NAV_TIMELS_t; //Allocate RAM for the main struct
if (packetUBXNAVTIMELS == NULL)
{
if ((_printDebug == true) || (_printLimitedDebug == true)) // This is important. Print this if doing limited debugging
_debugSerial->println(F("initPacketUBXNAVTIMELS: PANIC! RAM allocation failed!"));
return (false);
}
packetUBXNAVTIMELS->automaticFlags.flags.all = 0;
packetUBXNAVTIMELS->callbackPointer = NULL;
packetUBXNAVTIMELS->callbackData = NULL;
packetUBXNAVTIMELS->moduleQueried.moduleQueried.all = 0;
return (true);
}

// ***** NAV SVIN automatic support

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

// ***** TIMELS Helper Functions

uint8_t SFE_UBLOX_GNSS::getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait)
{
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed
return 3;

if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent == false)
getLeapSecondEvent(maxWait);
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validTimeToLsEvent = false; //Since we are about to give this to user, mark this data as stale
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.lsChange = false;
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.timeToLsEvent = false;
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false;
timeToLsEvent = packetUBXNAVTIMELS->data.timeToLsEvent;
// returns NTP Leap Indicator
// 0 -no warning
// 1 -last minute of the day has 61 seconds
// 2 -last minute of the day has 59 seconds
// 3 -unknown (clock unsynchronized)
return ((boolean)packetUBXNAVTIMELS->data.valid.bits.validTimeToLsEvent ? (uint8_t)(packetUBXNAVTIMELS->data.lsChange == -1 ? 2 : packetUBXNAVTIMELS->data.lsChange) : 3);
}

int8_t SFE_UBLOX_GNSS::getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait)
{
if (packetUBXNAVTIMELS == NULL) initPacketUBXNAVTIMELS(); //Check that RAM has been allocated for the TIMELS data
if (packetUBXNAVTIMELS == NULL) //Bail if the RAM allocation failed
return false;

if (packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs == false)
getLeapSecondEvent(maxWait);
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.validCurrLs = false; //Since we are about to give this to user, mark this data as stale
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.srcOfCurrLs = false;
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.currLs = false;
packetUBXNAVTIMELS->moduleQueried.moduleQueried.bits.all = false;
source = ((sfe_ublox_ls_src_e)packetUBXNAVTIMELS->data.srcOfCurrLs);
return ((int8_t)packetUBXNAVTIMELS->data.currLs);
}

// ***** RELPOSNED Helper Functions and automatic support

float SFE_UBLOX_GNSS::getRelPosN(uint16_t maxWait) // Returned as m
Expand Down
24 changes: 24 additions & 0 deletions src/SparkFun_u-blox_GNSS_Arduino_Library.h
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,20 @@ enum sfe_ublox_gnss_ids_e
SFE_UBLOX_GNSS_ID_GLONASS
};

// The GNSS identifiers of leap second event info source - used by UBX-NAV-TIMELS
enum sfe_ublox_ls_src_e
{
SFE_UBLOX_LS_SRC_DEFAULT,
SFE_UBLOX_LS_SRC_GLONASS,
SFE_UBLOX_LS_SRC_GPS,
SFE_UBLOX_LS_SRC_SBAS,
SFE_UBLOX_LS_SRC_BEIDOU,
SFE_UBLOX_LS_SRC_GALILEO,
SFE_UBLOX_LS_SRC_AIDED,
SFE_UBLOX_LS_SRC_CONFIGURED,
SFE_UBLOX_LS_SRC_UNKNOWN = 255
};

#ifndef MAX_PAYLOAD_SIZE
// v2.0: keep this for backwards-compatibility, but this is largely superseded by setPacketCfgPayloadSize
#define MAX_PAYLOAD_SIZE 256 //We need ~220 bytes for getProtocolVersion on most ublox modules
Expand Down Expand Up @@ -865,6 +879,9 @@ class SFE_UBLOX_GNSS
// Add "auto" support for NAV SVIN - to avoid needing 'global' storage
boolean getSurveyStatus(uint16_t maxWait); //Reads survey in status

// Add "auto" support for NAV TIMELS - to avoid needing 'global' storage
boolean getLeapSecondEvent(uint16_t maxWait); //Reads leap second event info

boolean getRELPOSNED(uint16_t maxWait = defaultMaxWait); //Get Relative Positioning Information of the NED frame
boolean setAutoRELPOSNED(boolean enabled, uint16_t maxWait = defaultMaxWait); //Enable/disable automatic RELPOSNED reports
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
Expand Down Expand Up @@ -1102,6 +1119,11 @@ class SFE_UBLOX_GNSS
uint16_t getSurveyInObservationTime(uint16_t maxWait = defaultMaxWait); // Truncated to 65535 seconds
float getSurveyInMeanAccuracy(uint16_t maxWait = defaultMaxWait); // Returned as m

// Helper functions for TIMELS

uint8_t getLeapIndicator(int32_t& timeToLsEvent, uint16_t maxWait = defaultMaxWait);
int8_t getCurrentLeapSeconds(sfe_ublox_ls_src_e& source, uint16_t maxWait = defaultMaxWait);

// Helper functions for RELPOSNED

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

Expand Down Expand Up @@ -1227,6 +1250,7 @@ class SFE_UBLOX_GNSS
boolean initPacketUBXNAVHPPOSECEF(); // Allocate RAM for packetUBXNAVHPPOSECEF and initialize it
boolean initPacketUBXNAVHPPOSLLH(); // Allocate RAM for packetUBXNAVHPPOSLLH and initialize it
boolean initPacketUBXNAVCLOCK(); // Allocate RAM for packetUBXNAVCLOCK and initialize it
boolean initPacketUBXNAVTIMELS(); // Allocate RAM for packetUBXNAVTIMELS and initialize it
boolean initPacketUBXNAVSVIN(); // Allocate RAM for packetUBXNAVSVIN and initialize it
boolean initPacketUBXNAVRELPOSNED(); // Allocate RAM for packetUBXNAVRELPOSNED and initialize it
boolean initPacketUBXRXMSFRBX(); // Allocate RAM for packetUBXRXMSFRBX and initialize it
Expand Down
Loading