timesync code refactored

This commit is contained in:
Klaus K Wilting 2020-03-07 23:18:36 +01:00
parent 85c342e3d7
commit f5399d8108
3 changed files with 37 additions and 58 deletions

View File

@ -5,12 +5,11 @@
#include "irqhandler.h" #include "irqhandler.h"
#include "timekeeper.h" #include "timekeeper.h"
#define TIME_SYNC_FRAME_LENGTH 0x07 // timeserver answer frame length [bytes] #define TIME_SYNC_FRAME_LENGTH 6 // timeserver answer frame length [bytes]
#define TIME_SYNC_FIXUP 16 // compensation for processing time [milliseconds] #define TIME_SYNC_FIXUP 16 // compensation for processing time [milliseconds]
#define TIMEREQUEST_MAX_SEQNO 0xfe // threshold for wrap around seqno #define TIMEREQUEST_MAX_SEQNO 0xfe // threshold for wrap around seqNo
#define TIMEREQUEST_END \ #define TIMEREQUEST_END (TIMEREQUEST_MAX_SEQNO + 1) // end of handshake marker
(TIMEREQUEST_MAX_SEQNO + 1) // marker for end of timesync handshake #define GPS_UTC_DIFF 315964800 // seconds diff between gps and utc epoch
#define GPS_UTC_DIFF 315964800
enum timesync_t { enum timesync_t {
timesync_tx, timesync_tx,

View File

@ -472,7 +472,7 @@ void myRxCallback(void *pUserData, uint8_t port, const uint8_t *pMsg,
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
case TIMEPORT: case TIMEPORT:
// get and store gwtime from payload // get and store gwtime from payload
timesync_serverAnswer(&pMsg, nMsg); timesync_serverAnswer(const_cast<uint8_t *>(pMsg), nMsg);
#endif #endif
// decode any piggybacked downlink MAC commands if we want to print those // decode any piggybacked downlink MAC commands if we want to print those

View File

@ -3,11 +3,13 @@
///--> IMPORTANT LICENSE NOTE for timesync option 1 in this file <--/// ///--> IMPORTANT LICENSE NOTE for timesync option 1 in this file <--///
PLEASE NOTE: There is a patent filed for the time sync algorithm used in the PLEASE NOTE: There is a patent filed for the time sync algorithm used in the
code of this file. The shown implementation example is covered by the code of this file for timesync option TIME_SYNC_LORASERVER. The shown
repository's licencse, but you may not be eligible to deploy the applied implementation example is covered by the repository's licencse, but you may not
algorithm in applications without granted license by the patent holder. be eligible to deploy the applied algorithm in applications without granted
license by the patent holder.
You may use timesync option 2 if you do not want or cannot accept this. You may use timesync option TIME_SYNC_LORAWAN if you do not want or cannot
accept this.
*/ */
@ -27,9 +29,7 @@ static const char TAG[] = __FILE__;
static bool timeSyncPending = false; static bool timeSyncPending = false;
static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO), static uint8_t time_sync_seqNo = (uint8_t)random(TIMEREQUEST_MAX_SEQNO),
sample_idx; sample_idx;
static uint16_t timestamp_msec; static uint32_t timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps];
static uint32_t timestamp_sec,
timesync_timestamp[TIME_SYNC_SAMPLES][no_of_timestamps];
static TaskHandle_t timeSyncProcTask = NULL; static TaskHandle_t timeSyncProcTask = NULL;
// create task for timeserver handshake processing, called from main.cpp // create task for timeserver handshake processing, called from main.cpp
@ -64,7 +64,7 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
// this task is an endless loop, waiting in blocked mode, until it is // this task is an endless loop, waiting in blocked mode, until it is
// unblocked by timesync_sendReq(). It then waits to be notified from // unblocked by timesync_sendReq(). It then waits to be notified from
// timesync_serverAnswer(), which is called from LMIC each time a timestamp // timesync_serverAnswer(), which is called from LMIC each time a timestamp
// from the timesource via LORAWAN arrived. // from the timesource via LORAWAN arrived.
// --- asnychronous part: generate and collect timestamps from gateway --- // --- asnychronous part: generate and collect timestamps from gateway ---
@ -89,31 +89,31 @@ void IRAM_ATTR timesync_processReq(void *taskparameter) {
// send timesync request to timeserver or networkserver // send timesync request to timeserver or networkserver
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// timesync option 1: use external timeserver (for LoRAWAN < 1.0.3) // ask user's timeserver (for LoRAWAN < 1.0.3)
// ask timeserver
payload.reset(); payload.reset();
payload.addByte(time_sync_seqNo); payload.addByte(time_sync_seqNo);
SendPayload(TIMEPORT, prio_high); SendPayload(TIMEPORT, prio_high);
#elif (TIME_SYNC_LORAWAN) #elif (TIME_SYNC_LORAWAN)
// timesync option 2: use LoRAWAN network time (requires LoRAWAN >= 1.0.3) // ask network (requires LoRAWAN >= 1.0.3)
// ask networkserver
LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo); LMIC_requestNetworkTime(timesync_serverAnswer, &time_sync_seqNo);
// open a receive window to immediately get DevTimeAns
LMIC_sendAlive();
#endif #endif
// open a receive window to immediately get the answer (Class A device) // open a receive window to immediately get the answer (Class A device)
LMIC_sendAlive(); // LMIC_sendAlive();
// wait until a timestamp was received // wait until a timestamp was received
if (xTaskNotifyWait(0x00, ULONG_MAX, &seqNo, if (xTaskNotifyWait(0x00, ULONG_MAX, &seqNo,
pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) { pdMS_TO_TICKS(TIME_SYNC_TIMEOUT * 1000)) == pdFALSE) {
ESP_LOGW(TAG, "[%0.3f] Timesync handshake error: timeout", ESP_LOGW(TAG, "[%0.3f] Timesync aborted: timed out", millis() / 1000.0);
millis() / 1000.0);
goto Fail; // no valid sequence received before timeout goto Fail; // no valid sequence received before timeout
} }
// check if we are in handshake with server // check if we are in handshake with server
if (seqNo != time_sync_seqNo) { if (seqNo != time_sync_seqNo) {
ESP_LOGW(TAG, "[%0.3f] Timesync handshake aborted", millis() / 1000.0); ESP_LOGW(TAG, "[%0.3f] Timesync aborted: handshake out of sync",
millis() / 1000.0);
goto Fail; goto Fail;
} }
@ -178,6 +178,7 @@ void timesync_storeReq(uint32_t timestamp, timesync_t timestamp_type) {
// callback function to receive network time server answer // callback function to receive network time server answer
void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) { void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
// if no timesync handshake is pending then exit // if no timesync handshake is pending then exit
if (!timeSyncPending) if (!timeSyncPending)
return; return;
@ -186,34 +187,30 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
mask_user_IRQ(); mask_user_IRQ();
int rc = 0; int rc = 0;
uint32_t timestamp_sec; uint8_t seqNo = *(uint8_t *)pUserData;
uint16_t timestamp_msec; uint16_t timestamp_msec;
uint32_t timestamp_sec;
#if (TIME_SYNC_LORASERVER) #if (TIME_SYNC_LORASERVER)
// store LMIC time when we received the timesync answer
timesync_storeReq(osticks2ms(os_getTime()), timesync_rx);
// pUserData: contains pointer to payload buffer // pUserData: contains pointer to payload buffer
// flag: length of buffer // flag: length of buffer
/* // store LMIC time when we received the timesync answer
parse 6 byte timesync_answer: timesync_storeReq(osticks2ms(os_getTime()), timesync_rx);
byte meaning // parse timesync_answer:
1 sequence number (taken from node's time_sync_req) // byte meaning
2..5 current second (from epoch time 1970) // 0 sequence number (taken from node's time_sync_req)
6 1/250ths fractions of current second // 1..4 current second (from UTC epoch)
*/ // 5 1/250ths fractions of current second
// Explicit conversion from void* to uint8_t* to avoid compiler errors // swap byte order from msb to lsb, note: this is a platform dependent hack
uint8_t *p = (uint8_t *)pUserData; timestamp_sec = __builtin_bswap32(*(uint32_t *)(pUserData + 1));
// Get payload buffer from pUserData
uint8_t *buf = p;
// extract 1 byte timerequest sequence number from payload // one step being 1/250th sec * 1000 = 4msec
uint8_t seqNo = buf[0]; timestamp_msec = *(uint8_t *)(pUserData + 5);
buf++; timestamp_msec *= 4;
// if no time is available or spurious buffer then exit // if no time is available or spurious buffer then exit
if (flag != TIME_SYNC_FRAME_LENGTH) { if (flag != TIME_SYNC_FRAME_LENGTH) {
@ -226,29 +223,12 @@ void IRAM_ATTR timesync_serverAnswer(void *pUserData, int flag) {
goto Exit; // failure goto Exit; // failure
} }
// pointer to 4 bytes msb order
uint32_t *timestamp_ptr;
// extract 4 bytes containing gateway time in UTC seconds since unix
// epoch and convert it to uint32_t, octet order is big endian
timestamp_ptr = (uint32_t *)buf;
// swap byte order from msb to lsb, note: this is a platform dependent hack
timestamp_sec = __builtin_bswap32(*timestamp_ptr);
buf += 4;
// extract 1 byte containing fractional seconds in 2^-8 second steps
// one step being 1/250th sec * 1000 = 4msec
timestamp_msec = buf[0] * 4;
goto Finish; goto Finish;
#elif (TIME_SYNC_LORAWAN) #elif (TIME_SYNC_LORAWAN)
// pUserData: contains pointer to SeqNo // pUserData: contains pointer to SeqNo
// flagSuccess: indicates if we got a recent time from the network // flag: indicates if we got a recent time from the network
// Explicit conversion from void* to uint8_t* to avoid compiler errors
uint8_t *p = (uint8_t *)pUserData;
// Get seqNo from pUserData
uint8_t seqNo = *p;
if (flag != 1) { if (flag != 1) {
ESP_LOGW(TAG, "[%0.3f] Network did not answer time request", ESP_LOGW(TAG, "[%0.3f] Network did not answer time request",