sntp: calibrate to the ntp times rather than the system function.
The system sdk_system_rtc_clock_cali_proc() is rather noisy, so this patch calibrates the RTC count to the NTP time responses. An average of many calls to sdk_system_rtc_clock_cali_proc() is used for initialization. The system calibration value is used to limit the calibration value computed via the ntp times, to 1/16 either way. A 64 bit RTC counter is implemented, and used in the calculations, but might be of some use on its own. The ratio of the ntp time differences and the RTC count differences is filtered a little to keep the changes relatively stable and to filter the jitter.
This commit is contained in:
parent
15964efc0f
commit
b1f7a0493f
3 changed files with 128 additions and 74 deletions
|
|
@ -61,10 +61,9 @@ int sntp_set_servers(char *server_url[], int num_servers);
|
||||||
void sntp_set_update_delay(uint32_t ms);
|
void sntp_set_update_delay(uint32_t ms);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Returns the time read from RTC counter, in seconds from Epoch. If
|
* Returns the time read from RTC counter, in micro seconds from Epoch.
|
||||||
* us is not null, it will be filled with the microseconds.
|
|
||||||
*/
|
*/
|
||||||
time_t sntp_get_rtc_time(int32_t *us);
|
uint64_t sntp_get_rtc_time();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update RTC timer. This function is called by the SNTP module each time
|
* Update RTC timer. This function is called by the SNTP module each time
|
||||||
|
|
|
||||||
|
|
@ -11,17 +11,36 @@
|
||||||
#include <espressif/esp_common.h>
|
#include <espressif/esp_common.h>
|
||||||
#include <esp/timer.h>
|
#include <esp/timer.h>
|
||||||
#include <esp/rtc_regs.h>
|
#include <esp/rtc_regs.h>
|
||||||
|
#include "FreeRTOS.h"
|
||||||
|
#include "semphr.h"
|
||||||
#include "sntp.h"
|
#include "sntp.h"
|
||||||
|
|
||||||
#define TIMER_COUNT RTC.COUNTER
|
#define TIMER_COUNT RTC.COUNTER
|
||||||
|
|
||||||
// daylight settings
|
|
||||||
// Base calculated with value obtained from NTP server (64 bits)
|
// Base calculated with value obtained from NTP server (64 bits)
|
||||||
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
|
#define sntp_base (*((uint64_t*)RTC.SCRATCH))
|
||||||
// Timer value when base was obtained
|
// Timer value when sntp_base was obtained
|
||||||
#define tim_ref (RTC.SCRATCH[2])
|
#define time_ref (RTC.SCRATCH[2])
|
||||||
// Calibration value
|
|
||||||
#define cal (RTC.SCRATCH[3])
|
// RTC counts.
|
||||||
|
static uint64_t rtc_count;
|
||||||
|
|
||||||
|
// NTP time last received.
|
||||||
|
static uint64_t sntp_last;
|
||||||
|
// RTC counts at the processing of NTP sntp_last time.
|
||||||
|
static uint64_t rtc_ref;
|
||||||
|
|
||||||
|
// The numerator and denominator of the calibration ratio. The
|
||||||
|
// difference between the sntp time responses, and the difference in
|
||||||
|
// the RTC counts for these respective events.
|
||||||
|
static uint64_t sntp_diff_sum;
|
||||||
|
static uint64_t rtc_diff_sum;
|
||||||
|
// Calibration value. The ratio of the number of usec over the number
|
||||||
|
// of RTC counts.
|
||||||
|
#define time_cal (RTC.SCRATCH[3])
|
||||||
|
|
||||||
|
// To protect access to the above.
|
||||||
|
static xSemaphoreHandle sntp_mutex = NULL;
|
||||||
|
|
||||||
// Timezone related data.
|
// Timezone related data.
|
||||||
static struct timezone stz;
|
static struct timezone stz;
|
||||||
|
|
@ -32,81 +51,119 @@ void sntp_init(void);
|
||||||
// Sets time zone.
|
// Sets time zone.
|
||||||
// NOTE: Settings do not take effect until SNTP time is updated.
|
// NOTE: Settings do not take effect until SNTP time is updated.
|
||||||
void sntp_set_timezone(const struct timezone *tz) {
|
void sntp_set_timezone(const struct timezone *tz) {
|
||||||
if (tz) {
|
if (tz) {
|
||||||
stz = *tz;
|
stz = *tz;
|
||||||
} else {
|
} else {
|
||||||
stz.tz_minuteswest = 0;
|
stz.tz_minuteswest = 0;
|
||||||
stz.tz_dsttime = 0;
|
stz.tz_dsttime = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialization
|
// Initialization
|
||||||
void sntp_initialize(const struct timezone *tz) {
|
void sntp_initialize(const struct timezone *tz) {
|
||||||
if (tz) {
|
if (tz) {
|
||||||
stz = *tz;
|
stz = *tz;
|
||||||
} else {
|
} else {
|
||||||
stz.tz_minuteswest = 0;
|
stz.tz_minuteswest = 0;
|
||||||
stz.tz_dsttime = 0;
|
stz.tz_dsttime = 0;
|
||||||
}
|
}
|
||||||
sntp_base = 0;
|
|
||||||
// To avoid div by 0 exceptions if requesting time before SNTP config
|
sntp_base = 0UL;
|
||||||
cal = 1;
|
time_ref = TIMER_COUNT;
|
||||||
tim_ref = TIMER_COUNT;
|
rtc_count = 0UL;
|
||||||
sntp_init();
|
sntp_last = 0UL;
|
||||||
|
rtc_ref = 0UL;
|
||||||
|
|
||||||
|
// The system rtc clock function is noisy so repeat to get a
|
||||||
|
// cleaner startup value.
|
||||||
|
uint32_t cal = 0;
|
||||||
|
uint32_t i;
|
||||||
|
for (i = 0; i < 32; i++)
|
||||||
|
cal += sdk_system_rtc_clock_cali_proc();
|
||||||
|
time_cal = cal / 32;
|
||||||
|
sntp_diff_sum = (uint64_t)time_cal << 20;
|
||||||
|
rtc_diff_sum = 1UL << 20;
|
||||||
|
|
||||||
|
vSemaphoreCreateBinary(sntp_mutex);
|
||||||
|
sntp_init();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if a timer wrap has occurred. Compensate sntp_base reference
|
// Return usecs.
|
||||||
// if affirmative.
|
inline uint64_t sntp_get_rtc_time() {
|
||||||
// TODO: think about multitasking and race conditions
|
xSemaphoreTake(sntp_mutex, portMAX_DELAY);
|
||||||
static inline void sntp_check_timer_wrap(uint32_t current_value) {
|
uint32_t tim = TIMER_COUNT;
|
||||||
if (current_value < tim_ref) {
|
// Assume the difference does not overflow in which case
|
||||||
// Timer wrap has occurred, compensate by subtracting 2^32 to ref.
|
// wrapping of the RTC timer still yields a good difference.
|
||||||
sntp_base -= 1LLU<<32;
|
uint32_t diff = tim - time_ref;
|
||||||
// DEBUG
|
time_ref = tim;
|
||||||
printf("\nTIMER WRAPPED!\n");
|
rtc_count += diff;
|
||||||
}
|
uint64_t diff_us = ((uint64_t)diff * time_cal) >> 12;
|
||||||
}
|
uint64_t base = sntp_base + diff_us;
|
||||||
|
sntp_base = base;
|
||||||
// Return secs. If us is not a null pointer, fill it with usecs
|
xSemaphoreGive(sntp_mutex);
|
||||||
inline time_t sntp_get_rtc_time(int32_t *us) {
|
return base;
|
||||||
time_t secs;
|
|
||||||
uint32_t tim;
|
|
||||||
uint64_t base;
|
|
||||||
|
|
||||||
tim = TIMER_COUNT;
|
|
||||||
// Check for timer wrap
|
|
||||||
sntp_check_timer_wrap(tim);
|
|
||||||
base = sntp_base + tim - tim_ref;
|
|
||||||
secs = base * cal / (1000000U<<12);
|
|
||||||
if (us) {
|
|
||||||
*us = base * cal % (1000000U<<12);
|
|
||||||
}
|
|
||||||
return secs;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Syscall implementation. doesn't seem to use tzp.
|
// Syscall implementation. doesn't seem to use tzp.
|
||||||
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
int _gettimeofday_r(struct _reent *r, struct timeval *tp, void *tzp) {
|
||||||
(void)r;
|
(void)r;
|
||||||
// Syscall defined by xtensa newlib defines tzp as void*
|
// Syscall defined by xtensa newlib defines tzp as void*
|
||||||
// So it looks like it is not used. Also check tp is not NULL
|
// So it looks like it is not used. Also check tp is not NULL
|
||||||
if (tzp || !tp) return EINVAL;
|
if (tzp || !tp) return EINVAL;
|
||||||
|
|
||||||
tp->tv_sec = sntp_get_rtc_time((int32_t*)&tp->tv_usec);
|
uint64_t base = sntp_get_rtc_time();
|
||||||
return 0;
|
tp->tv_sec = base / 1000000U;
|
||||||
|
tp->tv_usec = base % 1000000U;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update RTC timer. Called by SNTP module each time it receives an update.
|
// Update RTC timer. Called by SNTP module each time it receives an update.
|
||||||
void sntp_update_rtc(time_t t, uint32_t us) {
|
void sntp_update_rtc(time_t t, uint32_t us) {
|
||||||
// Apply daylight and timezone correction
|
// Apply daylight and timezone correction
|
||||||
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
|
t += (stz.tz_minuteswest + stz.tz_dsttime * 60) * 60;
|
||||||
// DEBUG: Compute and print drift
|
int64_t sntp_correct = (uint64_t)us + (uint64_t)t * 1000000U;
|
||||||
int64_t sntp_current = sntp_base + TIMER_COUNT - tim_ref;
|
|
||||||
int64_t sntp_correct = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
|
|
||||||
printf("\nRTC Adjust: drift = %ld ticks, cal = %d\n", (time_t)(sntp_correct - sntp_current), cal);
|
|
||||||
|
|
||||||
tim_ref = TIMER_COUNT;
|
xSemaphoreTake(sntp_mutex, portMAX_DELAY);
|
||||||
cal = sdk_system_rtc_clock_cali_proc();
|
uint32_t time = TIMER_COUNT;
|
||||||
|
// Assume the difference does not overflow in which case
|
||||||
|
// wrapping of the RTC timer still yields a good difference.
|
||||||
|
uint32_t diff = time - time_ref;
|
||||||
|
time_ref = time;
|
||||||
|
rtc_count += diff;
|
||||||
|
uint64_t diff_us = ((uint64_t)diff * time_cal) >> 12;
|
||||||
|
uint64_t sntp_current = sntp_base + diff_us;
|
||||||
|
|
||||||
sntp_base = (((uint64_t)us + (uint64_t)t * 1000000U)<<12) / cal;
|
if (sntp_correct <= sntp_last) {
|
||||||
|
// Reject this update as it is older than a prior update,
|
||||||
|
// probably an old response arriving out of order.
|
||||||
|
sntp_base = sntp_current;
|
||||||
|
} else {
|
||||||
|
sntp_base = sntp_correct;
|
||||||
|
if (sntp_last > 0) {
|
||||||
|
// Filter the time_cal ratio numerator and denominator
|
||||||
|
// separately - for better precision.
|
||||||
|
uint64_t sntp_diff = sntp_correct - sntp_last;
|
||||||
|
sntp_diff_sum = (sntp_diff_sum * 7 + (sntp_diff << 12)) / 8;
|
||||||
|
uint64_t rtc_diff = rtc_count - rtc_ref;
|
||||||
|
rtc_diff_sum = (rtc_diff_sum * 7 + rtc_diff) / 8;
|
||||||
|
time_cal = sntp_diff_sum / rtc_diff_sum;
|
||||||
|
//
|
||||||
|
// The calibation ratio is constrained to be close to the
|
||||||
|
// system time_cal value under the assumption that the
|
||||||
|
// system value is not too far off.
|
||||||
|
uint32_t sys_cal = sdk_system_rtc_clock_cali_proc();
|
||||||
|
uint32_t sys_cal_max = sys_cal + sys_cal / 16;
|
||||||
|
if (time_cal > sys_cal_max)
|
||||||
|
time_cal = sys_cal_max;
|
||||||
|
uint32_t sys_cal_min = sys_cal - sys_cal / 16;
|
||||||
|
if (time_cal < sys_cal_min)
|
||||||
|
time_cal = sys_cal_min;
|
||||||
|
}
|
||||||
|
sntp_last = sntp_correct;
|
||||||
|
rtc_ref = rtc_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
xSemaphoreGive(sntp_mutex);
|
||||||
|
|
||||||
|
printf("\n****** RTC Adjust: time %d.%d, drift = %d usec, cal = %d\n", (int)t, us, (int)(sntp_correct - sntp_current), time_cal);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,10 @@
|
||||||
// https://www.kernel.org/pub/software/scm/git/docs/git-update-index.html
|
// https://www.kernel.org/pub/software/scm/git/docs/git-update-index.html
|
||||||
//
|
//
|
||||||
|
|
||||||
#error "You need to enter your wifi credentials in this file and follow the instructions here to keep the password safe from Github commits."
|
|
||||||
|
|
||||||
#ifndef __SSID_CONFIG_H__
|
#ifndef __SSID_CONFIG_H__
|
||||||
#define __SSID_CONFIG_H__
|
#define __SSID_CONFIG_H__
|
||||||
|
|
||||||
#define WIFI_SSID "mywifissid"
|
#define WIFI_SSID "BigPond8481"
|
||||||
#define WIFI_PASS "my secret password"
|
#define WIFI_PASS "01234567890000000123456789000000"
|
||||||
|
|
||||||
#endif // __SSID_CONFIG_H__
|
#endif // __SSID_CONFIG_H__
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue