static void handle_initial_trim(void) { double rate; long delta_time; double rtc_error_now, sys_error_now; /* The idea is to accumulate some number of samples at 1 second intervals, then do a robust regression fit to this. This should give a good fix on the intercept (=system clock error rel to RTC) at a particular time, removing risk of any particular sample being an outlier. We can then look at the elapsed interval since the epoch recorded in the RTC file, and correct the system time accordingly. */ run_regression(1, &coefs_valid, &coef_ref_time, &coef_seconds_fast, &coef_gain_rate); n_samples_since_regression = 0; /* Set sample number to -1 so the next sample is not used, as it will not yet be corrected for System Trim*/ n_samples = -1; read_coefs_from_file(); if (valid_coefs_from_file) { /* Can process data */ delta_time = coef_ref_time - file_ref_time; rate = 1.0e-6 * file_rate_ppm; rtc_error_now = file_ref_offset + rate * (double) delta_time; /* sys_error_now is positive if the system clock is fast */ sys_error_now = rtc_error_now - coef_seconds_fast; LOG(LOGS_INFO, LOGF_RtcLinux, "System trim from RTC = %f", sys_error_now); LCL_AccumulateOffset(sys_error_now, 0.0); } else { LOG(LOGS_WARN, LOGF_RtcLinux, "No valid file coefficients, cannot trim system time"); } coefs_valid = 0; (after_init_hook)(after_init_hook_arg); operating_mode = OM_NORMAL; return; }
int RTC_Linux_TimePreInit(time_t driftfile_time) { int fd, status; struct rtc_time rtc_raw, rtc_raw_retry; struct tm rtc_tm; time_t rtc_t; double accumulated_error, sys_offset; struct timeval new_sys_time, old_sys_time; coefs_file_name = CNF_GetRtcFile(); setup_config(); read_coefs_from_file(); fd = open(CNF_GetRtcDevice(), O_RDONLY); if (fd < 0) { return 0; /* Can't open it, and won't be able to later */ } /* Retry reading the rtc until both read attempts give the same sec value. This way the race condition is prevented that the RTC has updated itself during the first read operation. */ do { status = ioctl(fd, RTC_RD_TIME, &rtc_raw); if (status >= 0) { status = ioctl(fd, RTC_RD_TIME, &rtc_raw_retry); } } while (status >= 0 && rtc_raw.tm_sec != rtc_raw_retry.tm_sec); /* Read system clock */ LCL_ReadCookedTime(&old_sys_time, NULL); close(fd); if (status >= 0) { /* Convert to seconds since 1970 */ rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_mday = rtc_raw.tm_mday; rtc_tm.tm_mon = rtc_raw.tm_mon; rtc_tm.tm_year = rtc_raw.tm_year; rtc_t = t_from_rtc(&rtc_tm); if (rtc_t != (time_t)(-1)) { /* Work out approximatation to correct time (to about the nearest second) */ if (valid_coefs_from_file) { accumulated_error = file_ref_offset + (rtc_t - file_ref_time) * 1.0e-6 * file_rate_ppm; } else { accumulated_error = 0.0; } /* Correct time */ new_sys_time.tv_sec = rtc_t; /* Average error in the RTC reading */ new_sys_time.tv_usec = 500000; UTI_AddDoubleToTimeval(&new_sys_time, -accumulated_error, &new_sys_time); if (new_sys_time.tv_sec < driftfile_time) { LOG(LOGS_WARN, LOGF_RtcLinux, "RTC time before last driftfile modification (ignored)"); return 0; } UTI_DiffTimevalsToDouble(&sys_offset, &old_sys_time, &new_sys_time); /* Set system time only if the step is larger than 1 second */ if (fabs(sys_offset) >= 1.0) { if (LCL_ApplyStepOffset(sys_offset)) LOG(LOGS_INFO, LOGF_RtcLinux, "System time set from RTC"); } } else { return 0; } } else { return 0; } return 1; }
void RTC_Linux_TimePreInit(void) { int fd, status; struct rtc_time rtc_raw; struct tm rtc_tm; time_t rtc_t, estimated_correct_rtc_t; long interval; double accumulated_error = 0.0; struct timeval new_sys_time, old_sys_time; coefs_file_name = CNF_GetRtcFile(); setup_config(); read_coefs_from_file(); fd = open(CNF_GetRtcDevice(), O_RDONLY); if (fd < 0) { return; /* Can't open it, and won't be able to later */ } status = ioctl(fd, RTC_RD_TIME, &rtc_raw); if (status >= 0) { /* Convert to seconds since 1970 */ rtc_tm.tm_sec = rtc_raw.tm_sec; rtc_tm.tm_min = rtc_raw.tm_min; rtc_tm.tm_hour = rtc_raw.tm_hour; rtc_tm.tm_mday = rtc_raw.tm_mday; rtc_tm.tm_mon = rtc_raw.tm_mon; rtc_tm.tm_year = rtc_raw.tm_year; rtc_t = t_from_rtc(&rtc_tm); if (rtc_t != (time_t)(-1)) { /* Work out approximatation to correct time (to about the nearest second) */ if (valid_coefs_from_file) { interval = rtc_t - file_ref_time; accumulated_error = file_ref_offset + (double)(interval) * 1.0e-6 * file_rate_ppm; /* Correct time */ estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error); } else { estimated_correct_rtc_t = rtc_t - (long)(0.5 + accumulated_error); } new_sys_time.tv_sec = estimated_correct_rtc_t; new_sys_time.tv_usec = 0; /* Set system time only if the step is larger than 1 second */ if (!(gettimeofday(&old_sys_time, NULL) < 0) && (old_sys_time.tv_sec - new_sys_time.tv_sec > 1 || old_sys_time.tv_sec - new_sys_time.tv_sec < -1)) { LOG(LOGS_INFO, LOGF_RtcLinux, "Set system time, error in RTC = %f", accumulated_error); /* Tough luck if this fails */ if (settimeofday(&new_sys_time, NULL) < 0) { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not settimeofday"); } } } else { LOG(LOGS_WARN, LOGF_RtcLinux, "Could not convert RTC reading to seconds since 1/1/1970"); } } close(fd); }