timesync fixes

This commit is contained in:
Verkehrsrot 2019-03-24 00:15:04 +01:00
parent 0b50a2e6e1
commit cad13b72a1
7 changed files with 56 additions and 86 deletions

View File

@ -6,12 +6,12 @@
#include "timesync.h" #include "timesync.h"
#include "timekeeper.h" #include "timekeeper.h"
#define TIME_SYNC_SAMPLES 2 // number of time requests for averaging #define TIME_SYNC_SAMPLES 1 // number of time requests for averaging
#define TIME_SYNC_CYCLE 20 // delay between two time samples [seconds] #define TIME_SYNC_CYCLE 20 // delay between two time samples [seconds]
#define TIME_SYNC_TIMEOUT 120 // timeout waiting for timeserver answer [seconds] #define TIME_SYNC_TIMEOUT 120 // timeout waiting for timeserver answer [seconds]
#define TIME_SYNC_TRIGGER 100 // deviation triggering a time sync [milliseconds] #define TIME_SYNC_TRIGGER 100 // deviation triggering a time sync [milliseconds]
#define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes] #define TIME_SYNC_FRAME_LENGTH 0x06 // timeserver answer frame length [bytes]
#define TIME_SYNC_FIXUP 0 // calibration to fixup processing time [milliseconds] #define TIME_SYNC_FIXUP 30 // calibration to fixup processing time [milliseconds]
void send_timesync_req(void); void send_timesync_req(void);
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len); int recv_timesync_ans(uint8_t buf[], uint8_t buf_len);

View File

@ -56,13 +56,10 @@ void irqHandler(void *pvParameters) {
#ifdef HAS_DISPLAY #ifdef HAS_DISPLAY
void IRAM_ATTR DisplayIRQ() { void IRAM_ATTR DisplayIRQ() {
portENTER_CRITICAL_ISR(&mux); BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits, xTaskNotifyFromISR(irqHandlerTask, DISPLAY_IRQ, eSetBits,
&xHigherPriorityTaskWoken); &xHigherPriorityTaskWoken);
portEXIT_CRITICAL_ISR(&mux);
if (xHigherPriorityTaskWoken) if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();
} }
@ -70,13 +67,10 @@ void IRAM_ATTR DisplayIRQ() {
#ifdef HAS_BUTTON #ifdef HAS_BUTTON
void IRAM_ATTR ButtonIRQ() { void IRAM_ATTR ButtonIRQ() {
portENTER_CRITICAL_ISR(&mux); BaseType_t xHigherPriorityTaskWoken = pdFALSE;
BaseType_t xHigherPriorityTaskWoken;
xHigherPriorityTaskWoken = pdFALSE;
xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits, xTaskNotifyFromISR(irqHandlerTask, BUTTON_IRQ, eSetBits,
&xHigherPriorityTaskWoken); &xHigherPriorityTaskWoken);
portEXIT_CRITICAL_ISR(&mux);
if (xHigherPriorityTaskWoken) if (xHigherPriorityTaskWoken)
portYIELD_FROM_ISR(); portYIELD_FROM_ISR();

View File

@ -180,7 +180,7 @@ void setup() {
strcat_P(features, " PSRAM"); strcat_P(features, " PSRAM");
#endif #endif
// set external power mode to off // set external power mode
#ifdef EXT_POWER_SW #ifdef EXT_POWER_SW
pinMode(EXT_POWER_SW, OUTPUT); pinMode(EXT_POWER_SW, OUTPUT);
digitalWrite(EXT_POWER_SW, EXT_POWER_ON); digitalWrite(EXT_POWER_SW, EXT_POWER_ON);

View File

@ -1,4 +1,4 @@
#ifdef USE_OTA #if (USE_OTA)
/* /*
Parts of this code: Parts of this code:

View File

@ -24,7 +24,7 @@ uint8_t rtc_init(void) {
Rtc.SetIsRunning(true); Rtc.SetIsRunning(true);
} }
#ifdef TIME_SYNC_COMPILEDATE #if (TIME_SYNC_COMPILEDATE)
// initialize a blank RTC without battery backup with compiled time // initialize a blank RTC without battery backup with compiled time
RtcDateTime tt = Rtc.GetDateTime(); RtcDateTime tt = Rtc.GetDateTime();
time_t t = tt.Epoch32Time(); // sec2000 -> epoch time_t t = tt.Epoch32Time(); // sec2000 -> epoch

View File

@ -122,10 +122,9 @@ void timepulse_start(void) {
void IRAM_ATTR CLOCKIRQ(void) { void IRAM_ATTR CLOCKIRQ(void) {
portENTER_CRITICAL_ISR(&mux); portENTER_CRITICAL_ISR(&mux);
BaseType_t xHigherPriorityTaskWoken; BaseType_t xHigherPriorityTaskWoken = pdFALSE;
xHigherPriorityTaskWoken = pdFALSE;
SyncToPPS(); // calibrates UTC systime, see microTime.h SyncToPPS(); // calibrates UTC systime and advances it +1, see microTime.h
if (ClockTask != NULL) if (ClockTask != NULL)
xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits,
@ -215,7 +214,8 @@ void clock_loop(void *taskparameter) { // ClockTask
#define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute #define nextmin(t) (t + DCF77_FRAME_SIZE + 1) // next minute
uint32_t printtime; uint32_t printtime;
time_t t = *((time_t *)taskparameter); // UTC time seconds time_t t = *((time_t *)taskparameter), last_printtime = 0; // UTC time seconds
TickType_t startTime;
#ifdef HAS_DCF77 #ifdef HAS_DCF77
uint8_t *DCFpulse; // pointer on array with DCF pulse bits uint8_t *DCFpulse; // pointer on array with DCF pulse bits
@ -228,18 +228,24 @@ void clock_loop(void *taskparameter) { // ClockTask
// output the next second's pulse after timepulse arrived // output the next second's pulse after timepulse arrived
for (;;) { for (;;) {
// ensure the notification state is not already pending
xTaskNotifyWait(0x00, ULONG_MAX, &printtime, xTaskNotifyWait(0x00, ULONG_MAX, &printtime,
portMAX_DELAY); // wait for timepulse portMAX_DELAY); // wait for timepulse
startTime = xTaskGetTickCount();
t = time_t(printtime); // UTC time seconds t = time_t(printtime); // UTC time seconds
// no confident time -> suppress clock output // no confident or no recent time -> suppress clock output
if ((timeStatus() == timeNotSet) || !(timeIsValid(t))) if ((timeStatus() == timeNotSet) || !(timeIsValid(t)) ||
(t == last_printtime))
continue; continue;
last_printtime = t;
#if defined HAS_IF482 #if defined HAS_IF482
vTaskDelay(txDelay); // wait until moment to fire vTaskDelayUntil(&startTime, txDelay); // wait until moment to fire
IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second IF482.print(IF482_Frame(t + 1)); // note: if482 telegram for *next* second
#elif defined HAS_DCF77 #elif defined HAS_DCF77

View File

@ -27,7 +27,6 @@ typedef std::chrono::system_clock myClock;
typedef myClock::time_point myClock_timepoint; typedef myClock::time_point myClock_timepoint;
typedef std::chrono::duration<long long int, std::ratio<1, 1000>> typedef std::chrono::duration<long long int, std::ratio<1, 1000>>
myClock_msecTick; myClock_msecTick;
typedef std::chrono::duration<double> myClock_secTick;
myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES]; myClock_timepoint time_sync_tx[TIME_SYNC_SAMPLES];
myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES]; myClock_timepoint time_sync_rx[TIME_SYNC_SAMPLES];
@ -63,11 +62,11 @@ void send_timesync_req() {
// task for sending time sync requests // task for sending time sync requests
void process_timesync_req(void *taskparameter) { void process_timesync_req(void *taskparameter) {
uint32_t seq_no = 0, time_to_set_us, time_to_set_ms;
uint16_t time_to_set_fraction_msec;
uint8_t k = 0, i = 0; uint8_t k = 0, i = 0;
uint16_t time_to_set_fraction_msec;
uint32_t seq_no = 0;
time_t time_to_set; time_t time_to_set;
auto time_offset = myClock_msecTick::zero(); auto time_offset_ms = myClock_msecTick::zero();
// wait until we are joined // wait until we are joined
while (!LMIC.devaddr) { while (!LMIC.devaddr) {
@ -98,81 +97,67 @@ void process_timesync_req(void *taskparameter) {
else { // calculate time diff from collected timestamps else { // calculate time diff from collected timestamps
k = seq_no % TIME_SYNC_SAMPLES; k = seq_no % TIME_SYNC_SAMPLES;
auto t_tx = time_point_cast<milliseconds>( // cumulate timepoint diffs
time_sync_tx[k]); // timepoint when node TX_completed time_offset_ms += time_point_cast<milliseconds>(time_sync_rx[k]) -
auto t_rx = time_point_cast<milliseconds>( time_point_cast<milliseconds>(time_sync_tx[k]);
time_sync_rx[k]); // timepoint when message was seen on gateway
time_offset += t_rx - t_tx; // cumulate timepoint diffs
if (i < TIME_SYNC_SAMPLES - 1) { if (i < TIME_SYNC_SAMPLES - 1) {
// wait until next cycle // wait until next cycle
vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000)); vTaskDelay(pdMS_TO_TICKS(TIME_SYNC_CYCLE * 1000));
} else { } else {
// send flush to open a receive window for last time_sync_answer // send flush to open a receive window for last time_sync_answer
// payload.reset(); payload.reset();
// payload.addByte(0x99); payload.addByte(0x99);
// SendPayload(RCMDPORT, prio_high); SendPayload(RCMDPORT, prio_high);
// Send a alive open a receive window for last time_sync_answer
// Send a payload-less message to open a receive window for last // void LMIC_sendAlive();
// time_sync_answer
void LMIC_sendAlive();
} }
} }
} // for } // for
// calculate time offset from collected diffs // average time offset from collected diffs
time_offset /= TIME_SYNC_SAMPLES; time_offset_ms /= TIME_SYNC_SAMPLES;
ESP_LOGD(TAG, "[%0.3f] avg time diff: %0.3f sec", millis() / 1000.0,
myClock_secTick(time_offset).count());
// calculate absolute time offset with millisecond precision using time base // calculate time offset with millisecond precision using time base
// of LMIC os, since we use LMIC's ostime_t txEnd as tx timestamp // of LMIC os, since we use LMIC's ostime_t txEnd as tx timestamp
time_offset += milliseconds(osticks2ms(os_getTime())); time_offset_ms += milliseconds(osticks2ms(os_getTime()));
// apply calibration factor for processing time // apply calibration factor for processing time
time_offset += milliseconds(TIME_SYNC_FIXUP); time_offset_ms += milliseconds(TIME_SYNC_FIXUP);
// convert to whole seconds
time_to_set = static_cast<time_t>(myClock_secTick(time_offset).count());
// time_to_set =
// static_cast<time_t>(duration_cast<seconds>(time_offset).count());
// calculate absolute time in UTC epoch
// convert to whole seconds, floor
time_to_set = (time_t)(time_offset_ms.count() / 1000) + 1;
// calculate fraction milliseconds // calculate fraction milliseconds
time_to_set_fraction_msec = static_cast<uint16_t>(time_offset.count() % 1000); time_to_set_fraction_msec = (uint16_t)(time_offset_ms.count() % 1000);
ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec", ESP_LOGD(TAG, "[%0.3f] Calculated UTC epoch time: %d.%03d sec",
millis() / 1000.0, time_to_set, time_to_set_fraction_msec); millis() / 1000.0, time_to_set, time_to_set_fraction_msec);
// adjust system time // adjust system time
if (timeIsValid(time_to_set)) { if (timeIsValid(time_to_set)) {
if (abs(time_offset.count()) >=
TIME_SYNC_TRIGGER) { // milliseconds threshold
// wait until top of second // wait until top of second
vTaskDelay(pdMS_TO_TICKS(1000 - time_to_set_fraction_msec)); vTaskDelay(pdMS_TO_TICKS(1000 - time_to_set_fraction_msec));
time_to_set++; // advance time 1 sec wait time
time_to_set++; // advance time the one waited second
#if (!defined GPS_INT && !defined RTC_INT) #if (!defined GPS_INT && !defined RTC_INT)
// sync esp32 hardware timer based pps to top of second // sync esp32 hardware timer based pps to top of second
timerRestart(ppsIRQ); // reset pps timer timerRestart(ppsIRQ); // reset pps timer
CLOCKIRQ(); // fire clock pps interrupt CLOCKIRQ(); // fire clock pps interrupt
#elif defined HAS_RTC
// calibrate RTC and RTC_INT pulse on top of second
set_rtctime(time_to_set);
#endif #endif
setTime(time_to_set); // set the time on top of second setTime(time_to_set); // set the time on top of second
#ifdef HAS_RTC timeSource = _lora;
set_rtctime(time_to_set); // calibrate RTC if we have one timesyncer.attach(TIME_SYNC_INTERVAL * 60,
#endif timeSync); // set to regular repeat
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time was adjusted",
millis() / 1000.0);
timeSource = _lora;
timesyncer.attach(TIME_SYNC_INTERVAL * 60,
timeSync); // set to regular repeat
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time adjusted by %.3f sec",
millis() / 1000.0, myClock_secTick(time_offset).count());
} else
ESP_LOGI(TAG, "[%0.3f] Timesync finished, time is up to date",
millis() / 1000.0);
} else } else
ESP_LOGW(TAG, "[%0.3f] Timesync failed, outdated time calculated", ESP_LOGW(TAG, "[%0.3f] Timesync failed, outdated time calculated",
millis() / 1000.0); millis() / 1000.0);
@ -196,21 +181,6 @@ void store_time_sync_req(uint32_t t_txEnd_ms) {
t_txEnd_ms % 1000); t_txEnd_ms % 1000);
} }
/*
// called from lorawan.cpp after time_sync_req was sent
void store_time_sync_req_pwm(void) {
uint8_t k = time_sync_seqNo % TIME_SYNC_SAMPLES;
time_sync_tx[k] += milliseconds(t_txEnd_ms);
ESP_LOGD(TAG, "[%0.3f] Timesync request #%d sent at %d.%03d",
millis() / 1000.0, time_sync_seqNo, t_txEnd_ms / 1000,
t_txEnd_ms % 1000);
}
*/
// process timeserver timestamp answer, called from lorawan.cpp // process timeserver timestamp answer, called from lorawan.cpp
int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) { int recv_timesync_ans(uint8_t buf[], uint8_t buf_len) {