From 1efc9be6c3338b1699df742c5cb4e54f68fe4ba0 Mon Sep 17 00:00:00 2001 From: Verkehrsrot Date: Sun, 4 Aug 2019 15:17:50 +0200 Subject: [PATCH] GPS handling and timesync code refactored --- include/globals.h | 2 -- include/gpsread.h | 9 ++---- src/gpsread.cpp | 76 +++++++++++++++++++++++++++++++--------------- src/main.cpp | 4 +-- src/rcommand.cpp | 1 + src/senddata.cpp | 1 + src/timekeeper.cpp | 7 +---- src/timesync.cpp | 19 ++++++------ 8 files changed, 69 insertions(+), 50 deletions(-) diff --git a/include/globals.h b/include/globals.h index ea477a95..e8e8bb48 100644 --- a/include/globals.h +++ b/include/globals.h @@ -82,8 +82,6 @@ typedef struct { uint8_t satellites; uint16_t hdop; int16_t altitude; - uint32_t time_age; - tmElements_t timedate; } gpsStatus_t; typedef struct { diff --git a/include/gpsread.h b/include/gpsread.h index eca6645c..3a780530 100644 --- a/include/gpsread.h +++ b/include/gpsread.h @@ -10,18 +10,15 @@ #endif #define NMEA_FRAME_SIZE 82 // NEMA has a maxium of 82 bytes per record -#define NMEA_BUFFERTIME 50 // 50ms safety time regardless extern TinyGPSPlus gps; // Make TinyGPS++ instance globally availabe -extern gpsStatus_t - gps_status; // Make struct for storing gps data globally available extern TaskHandle_t GpsTask; int gps_init(void); -void IRAM_ATTR gps_storetime(gpsStatus_t *gps_store); +int gps_config(); void gps_storelocation(gpsStatus_t *gps_store); void gps_loop(void *pvParameters); -time_t fetch_gpsTime(gpsStatus_t value, uint16_t *msec); -int gps_config(); +time_t fetch_gpsTime(uint16_t *msec); +time_t fetch_gpsTime(void); #endif \ No newline at end of file diff --git a/src/gpsread.cpp b/src/gpsread.cpp index 068127e6..eb97b4a4 100644 --- a/src/gpsread.cpp +++ b/src/gpsread.cpp @@ -5,7 +5,12 @@ // Local logging tag static const char TAG[] = __FILE__; +// we use NMEA $GPZDA sentence field 1 for time synchronization +// $GPZDA gives time for preceding pps pulse, but does not has a constant offset TinyGPSPlus gps; +TinyGPSCustom gpstime(gps, "GPZDA", 1); // field 1 = UTC time +static const String ZDA_Request = "$EIGPQ,ZDA*39\r\n"; + gpsStatus_t gps_status = {0}; TaskHandle_t GpsTask; @@ -27,7 +32,7 @@ int gps_init(void) { return 0; } -#if defined GPS_SERIAL +#ifdef GPS_SERIAL GPS_Serial.begin(GPS_SERIAL); ESP_LOGI(TAG, "Using serial GPS"); #elif defined GPS_I2C @@ -55,11 +60,11 @@ int gps_config() { int rslt = 1; // success #if defined GPS_SERIAL - /* to come */ + /* insert user configuration here, if needed */ #elif defined GPS_I2C - /* to come */ + /* insert user configuration here, if needed */ #endif return rslt; @@ -68,7 +73,7 @@ int gps_config() { // store current GPS location data in struct void gps_storelocation(gpsStatus_t *gps_store) { if (gps.location.isUpdated() && gps.location.isValid() && - (gps.time.age() < 1500)) { + (gps.location.age() < 1500)) { gps_store->latitude = (int32_t)(gps.location.lat() * 1e6); gps_store->longitude = (int32_t)(gps.location.lng() * 1e6); gps_store->satellites = (uint8_t)gps.satellites.value(); @@ -77,34 +82,55 @@ void gps_storelocation(gpsStatus_t *gps_store) { } } -// store current GPS timedate in struct -void IRAM_ATTR gps_storetime(gpsStatus_t *gps_store) { +// function to fetch current time from struct; note: this is costly +time_t fetch_gpsTime(uint16_t *msec) { - if (gps.time.isUpdated() && gps.date.isValid() && (gps.time.age() < 1000)) { + time_t time_sec = 0; - gps_store->time_age = gps.time.age() + nmea_txDelay_ms; - gps_store->timedate.Second = gps.time.second(); - gps_store->timedate.Minute = gps.time.minute(); - gps_store->timedate.Hour = gps.time.hour(); - gps_store->timedate.Day = gps.date.day(); - gps_store->timedate.Month = gps.date.month(); - gps_store->timedate.Year = + // poll NMEA $GPZDA sentence +#ifdef GPS_SERIAL + GPS_Serial.print(ZDA_Request); +#elif defined GPS_I2C + Wire.print(ZDA_Request); +#endif + + // wait for gps NMEA answer + vTaskDelay(tx_Ticks(NMEA_FRAME_SIZE, GPS_SERIAL)); + + // did we get a current time? + if (gpstime.isUpdated() && gpstime.isValid()) { + + tmElements_t tm; + + String rawtime = gpstime.value(); + uint32_t time_bcd = rawtime.toFloat() * 100; + uint32_t delay_ms = gpstime.age() + nmea_txDelay_ms; + uint8_t year = CalendarYrToTm(gps.date.year()); // year offset from 1970 in microTime.h - } else - gps_store->timedate = {0}; -} + ESP_LOGD(TAG, "time [bcd]: %u", time_bcd); -// function to fetch current time from struct; note: this is costly -time_t fetch_gpsTime(gpsStatus_t value, uint16_t *msec) { + tm.Second = (time_bcd / 100) % 100; // second + tm.Minute = (time_bcd / 10000) % 100; // minute + tm.Hour = time_bcd / 1000000; // hour + tm.Day = gps.date.day(); // day + tm.Month = gps.date.month(); // month + tm.Year = year; // year - *msec = 1000 - value.time_age; - time_t t = timeIsValid(makeTime(value.timedate)); - ESP_LOGD(TAG, "GPS time: %d | time age: %d", t, value.time_age); - return t; + // add protocol delay to time with millisecond precision + time_sec = makeTime(tm) + delay_ms / 1000; + *msec = (delay_ms % 1000) ? delay_ms % 1000 : 0; + } + + return timeIsValid(time_sec); } // fetch_gpsTime() +time_t fetch_gpsTime(void) { + uint16_t msec; + return fetch_gpsTime(&msec); +} + // GPS serial feed FreeRTos Task void gps_loop(void *pvParameters) { @@ -113,7 +139,7 @@ void gps_loop(void *pvParameters) { while (1) { if (cfg.payloadmask && GPS_DATA) { -#if defined GPS_SERIAL +#ifdef GPS_SERIAL // feed GPS decoder with serial NMEA data from GPS device while (GPS_Serial.available()) { gps.encode(GPS_Serial.read()); @@ -128,7 +154,7 @@ void gps_loop(void *pvParameters) { } // if // show NMEA data in verbose mode, useful for debugging GPS - ESP_LOGV(TAG, "GPS NMEA data: passed %d / failed: %d / with fix: %d", + ESP_LOGV(TAG, "GPS NMEA data: passed %u / failed: %u / with fix: %u", gps.passedChecksum(), gps.failedChecksum(), gps.sentencesWithFix()); diff --git a/src/main.cpp b/src/main.cpp index d88722b5..72422bbc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -419,9 +419,9 @@ void setup() { #warning you did not specify a time source, time will not be synched #endif - // initialize gps time +// initialize gps time #if (HAS_GPS) - gps_storetime(&gps_status); + fetch_gpsTime(); #endif #if (defined HAS_IF482 || defined HAS_DCF77) diff --git a/src/rcommand.cpp b/src/rcommand.cpp index c2a1fb37..415283a9 100644 --- a/src/rcommand.cpp +++ b/src/rcommand.cpp @@ -253,6 +253,7 @@ void get_status(uint8_t val[]) { void get_gps(uint8_t val[]) { ESP_LOGI(TAG, "Remote command: get gps status"); #if (HAS_GPS) + gpsStatus_t gps_status; gps_storelocation(&gps_status); payload.reset(); payload.addGPS(gps_status); diff --git a/src/senddata.cpp b/src/senddata.cpp index 8b95f219..fd5df963 100644 --- a/src/senddata.cpp +++ b/src/senddata.cpp @@ -87,6 +87,7 @@ void sendCounter() { case GPS_DATA: // send GPS position only if we have a fix if (gps.location.isValid()) { + gpsStatus_t gps_status; gps_storelocation(&gps_status); payload.reset(); payload.addGPS(gps_status); diff --git a/src/timekeeper.cpp b/src/timekeeper.cpp index b6a2051b..4d01f7d9 100644 --- a/src/timekeeper.cpp +++ b/src/timekeeper.cpp @@ -30,7 +30,7 @@ void calibrateTime(void) { #if (HAS_GPS) // fetch recent time from last NMEA record - t = fetch_gpsTime(gps_status, &t_msec); + t = fetch_gpsTime(&t_msec); if (t) { timeSource = _gps; goto finish; @@ -124,11 +124,6 @@ void IRAM_ATTR CLOCKIRQ(void) { SyncToPPS(); // advance systime, see microTime.h - // store recent gps time -#if (HAS_GPS) - gps_storetime(&gps_status); -#endif - // advance wall clock, if we have #if (defined HAS_IF482 || defined HAS_DCF77) xTaskNotifyFromISR(ClockTask, uint32_t(now()), eSetBits, diff --git a/src/timesync.cpp b/src/timesync.cpp index 9499502d..6e168ae3 100644 --- a/src/timesync.cpp +++ b/src/timesync.cpp @@ -211,22 +211,22 @@ int recv_timesync_ans(uint8_t seq_no, uint8_t buf[], uint8_t buf_len) { void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, timesource_t mytimesource) { - time_t time_to_set = (time_t)(t_sec); + time_t time_to_set = (time_t)(t_sec + t_msec / 1000); if (timeIsValid(time_to_set)) { - ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0, - time_to_set, t_msec); // wait until top of second with millisecond precision - if (t_msec) { - vTaskDelay(pdMS_TO_TICKS(1000 - t_msec)); + if (t_msec % 1000) { time_to_set++; + vTaskDelay(pdMS_TO_TICKS(1000 - t_msec % 1000)); } -// if we got a timesource, set RTC time and calibrate RTC_INT pulse on top of -// second + ESP_LOGD(TAG, "[%0.3f] UTC epoch time: %d.%03d sec", millis() / 1000.0, + time_to_set, t_msec % 1000); + +// if we got a timesource, set RTC time and RTC_INT pulse on top of second #ifdef HAS_RTC - if (mytimesource != _rtc) + if ((mytimesource == _gps) || (mytimesource == _lora)) set_rtctime(time_to_set); #endif @@ -234,6 +234,7 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, #if (!defined GPS_INT && !defined RTC_INT) timerWrite(ppsIRQ, 0); // reset pps timer CLOCKIRQ(); // fire clock pps, this advances time 1 sec + time_to_set--; #endif setTime(time_to_set); // set the time on top of second @@ -249,8 +250,8 @@ void IRAM_ATTR setMyTime(uint32_t t_sec, uint16_t t_msec, } } +// create task for timeserver handshake processing, called from main.cpp void timesync_init() { - // create task for timeserver handshake processing, called from main.cpp xTaskCreatePinnedToCore(process_timesync_req, // task function "timesync_req", // name of task 2048, // stack size of task