|
Packit |
96c956 |
/*
|
|
Packit |
96c956 |
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
**********************************************************************
|
|
Packit |
96c956 |
* Copyright (C) Richard P. Curnow 1997-2003
|
|
Packit |
96c956 |
* Copyright (C) Miroslav Lichvar 2011, 2014-2015
|
|
Packit |
96c956 |
*
|
|
Packit |
96c956 |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
96c956 |
* it under the terms of version 2 of the GNU General Public License as
|
|
Packit |
96c956 |
* published by the Free Software Foundation.
|
|
Packit |
96c956 |
*
|
|
Packit |
96c956 |
* This program is distributed in the hope that it will be useful, but
|
|
Packit |
96c956 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
96c956 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit |
96c956 |
* General Public License for more details.
|
|
Packit |
96c956 |
*
|
|
Packit |
96c956 |
* You should have received a copy of the GNU General Public License along
|
|
Packit |
96c956 |
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
Packit |
96c956 |
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
Packit |
96c956 |
*
|
|
Packit |
96c956 |
**********************************************************************
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
=======================================================================
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
The routines in this file present a common local (system) clock
|
|
Packit |
96c956 |
interface to the rest of the software.
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
They interface with the system specific driver files in sys_*.c
|
|
Packit |
96c956 |
*/
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
#include "config.h"
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
#include "sysincl.h"
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
#include "conf.h"
|
|
Packit |
96c956 |
#include "local.h"
|
|
Packit |
96c956 |
#include "localp.h"
|
|
Packit |
96c956 |
#include "memory.h"
|
|
Packit |
96c956 |
#include "smooth.h"
|
|
Packit |
96c956 |
#include "util.h"
|
|
Packit |
96c956 |
#include "logging.h"
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Variable to store the current frequency, in ppm */
|
|
Packit |
96c956 |
static double current_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Maximum allowed frequency, in ppm */
|
|
Packit |
96c956 |
static double max_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Temperature compensation, in ppm */
|
|
Packit |
96c956 |
static double temp_comp_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
/* Store the system dependent drivers */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static lcl_ReadFrequencyDriver drv_read_freq;
|
|
Packit |
96c956 |
static lcl_SetFrequencyDriver drv_set_freq;
|
|
Packit |
96c956 |
static lcl_AccrueOffsetDriver drv_accrue_offset;
|
|
Packit |
96c956 |
static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
|
|
Packit |
96c956 |
static lcl_OffsetCorrectionDriver drv_offset_convert;
|
|
Packit |
96c956 |
static lcl_SetLeapDriver drv_set_leap;
|
|
Packit |
96c956 |
static lcl_SetSyncStatusDriver drv_set_sync_status;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Types and variables associated with handling the parameter change
|
|
Packit |
96c956 |
list */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
typedef struct _ChangeListEntry {
|
|
Packit |
96c956 |
struct _ChangeListEntry *next;
|
|
Packit |
96c956 |
struct _ChangeListEntry *prev;
|
|
Packit |
96c956 |
LCL_ParameterChangeHandler handler;
|
|
Packit |
96c956 |
void *anything;
|
|
Packit |
96c956 |
} ChangeListEntry;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static ChangeListEntry change_list;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Types and variables associated with handling the parameter change
|
|
Packit |
96c956 |
list */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
typedef struct _DispersionNotifyListEntry {
|
|
Packit |
96c956 |
struct _DispersionNotifyListEntry *next;
|
|
Packit |
96c956 |
struct _DispersionNotifyListEntry *prev;
|
|
Packit |
96c956 |
LCL_DispersionNotifyHandler handler;
|
|
Packit |
96c956 |
void *anything;
|
|
Packit |
96c956 |
} DispersionNotifyListEntry;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static DispersionNotifyListEntry dispersion_notify_list;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static int precision_log;
|
|
Packit |
96c956 |
static double precision_quantum;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static double max_clock_error;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Define the number of increments of the system clock that we want
|
|
Packit |
96c956 |
to see to be fairly sure that we've got something approaching
|
|
Packit |
96c956 |
the minimum increment. Even on a crummy implementation that can't
|
|
Packit |
96c956 |
interpolate between 10ms ticks, we should get this done in
|
|
Packit |
96c956 |
under 1s of busy waiting. */
|
|
Packit |
96c956 |
#define NITERS 100
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
#define NSEC_PER_SEC 1000000000
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static void
|
|
Packit |
96c956 |
calculate_sys_precision(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec ts, old_ts;
|
|
Packit |
96c956 |
int iters, diff, best;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&old_ts);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Assume we must be better than a second */
|
|
Packit |
96c956 |
best = NSEC_PER_SEC;
|
|
Packit |
96c956 |
iters = 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
do {
|
|
Packit |
96c956 |
LCL_ReadRawTime(&ts);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
old_ts = ts;
|
|
Packit |
96c956 |
if (diff > 0) {
|
|
Packit |
96c956 |
if (diff < best)
|
|
Packit |
96c956 |
best = diff;
|
|
Packit |
96c956 |
iters++;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
} while (iters < NITERS);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
assert(best > 0);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
precision_quantum = 1.0e-9 * best;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Get rounded log2 value of the measured precision */
|
|
Packit |
96c956 |
precision_log = 0;
|
|
Packit |
96c956 |
while (best < 707106781) {
|
|
Packit |
96c956 |
precision_log--;
|
|
Packit |
96c956 |
best *= 2;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
assert(precision_log >= -30);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_Initialise(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
change_list.next = change_list.prev = &change_list;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Null out the system drivers, so that we die
|
|
Packit |
96c956 |
if they never get defined before use */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
drv_read_freq = NULL;
|
|
Packit |
96c956 |
drv_set_freq = NULL;
|
|
Packit |
96c956 |
drv_accrue_offset = NULL;
|
|
Packit |
96c956 |
drv_offset_convert = NULL;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* This ought to be set from the system driver layer */
|
|
Packit |
96c956 |
current_freq_ppm = 0.0;
|
|
Packit |
96c956 |
temp_comp_ppm = 0.0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
calculate_sys_precision();
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* This is the maximum allowed frequency offset in ppm, the time must
|
|
Packit |
96c956 |
never stop or run backwards */
|
|
Packit |
96c956 |
max_freq_ppm = CNF_GetMaxDrift();
|
|
Packit |
96c956 |
max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
max_clock_error = CNF_GetMaxClockError() * 1e-6;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_Finalise(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
while (change_list.next != &change_list)
|
|
Packit |
96c956 |
LCL_RemoveParameterChangeHandler(change_list.next->handler,
|
|
Packit |
96c956 |
change_list.next->anything);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
while (dispersion_notify_list.next != &dispersion_notify_list)
|
|
Packit |
96c956 |
LCL_RemoveDispersionNotifyHandler(dispersion_notify_list.next->handler,
|
|
Packit |
96c956 |
dispersion_notify_list.next->anything);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Routine to read the system precision as a log to base 2 value. */
|
|
Packit |
96c956 |
int
|
|
Packit |
96c956 |
LCL_GetSysPrecisionAsLog(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
return precision_log;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
/* Routine to read the system precision in terms of the actual time step */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
double
|
|
Packit |
96c956 |
LCL_GetSysPrecisionAsQuantum(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
return precision_quantum;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
double
|
|
Packit |
96c956 |
LCL_GetMaxClockError(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
return max_clock_error;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
ChangeListEntry *ptr, *new_entry;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Check that the handler is not already registered */
|
|
Packit |
96c956 |
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
|
Packit |
96c956 |
assert(0);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
new_entry = MallocNew(ChangeListEntry);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
new_entry->handler = handler;
|
|
Packit |
96c956 |
new_entry->anything = anything;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Chain it into the list */
|
|
Packit |
96c956 |
new_entry->next = &change_list;
|
|
Packit |
96c956 |
new_entry->prev = change_list.prev;
|
|
Packit |
96c956 |
change_list.prev->next = new_entry;
|
|
Packit |
96c956 |
change_list.prev = new_entry;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Remove a handler */
|
|
Packit |
96c956 |
void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
ChangeListEntry *ptr;
|
|
Packit |
96c956 |
int ok;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
ptr = NULL;
|
|
Packit |
96c956 |
ok = 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
if (ptr->handler == handler && ptr->anything == anything) {
|
|
Packit |
96c956 |
ok = 1;
|
|
Packit |
96c956 |
break;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
assert(ok);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Unlink entry from the list */
|
|
Packit |
96c956 |
ptr->next->prev = ptr->prev;
|
|
Packit |
96c956 |
ptr->prev->next = ptr->next;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
Free(ptr);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
int
|
|
Packit |
96c956 |
LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
return change_list.next->handler == handler;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static void
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
|
|
Packit |
96c956 |
double dfreq, double doffset,
|
|
Packit |
96c956 |
LCL_ChangeType change_type)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
ChangeListEntry *ptr;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
(ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
DispersionNotifyListEntry *ptr, *new_entry;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Check that the handler is not already registered */
|
|
Packit |
96c956 |
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
if (!(ptr->handler != handler || ptr->anything != anything)) {
|
|
Packit |
96c956 |
assert(0);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
new_entry = MallocNew(DispersionNotifyListEntry);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
new_entry->handler = handler;
|
|
Packit |
96c956 |
new_entry->anything = anything;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Chain it into the list */
|
|
Packit |
96c956 |
new_entry->next = &dispersion_notify_list;
|
|
Packit |
96c956 |
new_entry->prev = dispersion_notify_list.prev;
|
|
Packit |
96c956 |
dispersion_notify_list.prev->next = new_entry;
|
|
Packit |
96c956 |
dispersion_notify_list.prev = new_entry;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Remove a handler */
|
|
Packit |
96c956 |
extern
|
|
Packit |
96c956 |
void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
DispersionNotifyListEntry *ptr;
|
|
Packit |
96c956 |
int ok;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
ptr = NULL;
|
|
Packit |
96c956 |
ok = 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
if (ptr->handler == handler && ptr->anything == anything) {
|
|
Packit |
96c956 |
ok = 1;
|
|
Packit |
96c956 |
break;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
assert(ok);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Unlink entry from the list */
|
|
Packit |
96c956 |
ptr->next->prev = ptr->prev;
|
|
Packit |
96c956 |
ptr->prev->next = ptr->next;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
Free(ptr);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_ReadRawTime(struct timespec *ts)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
#if HAVE_CLOCK_GETTIME
|
|
Packit |
96c956 |
if (clock_gettime(CLOCK_REALTIME, ts) < 0)
|
|
Packit |
96c956 |
LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
|
|
Packit |
96c956 |
#else
|
|
Packit |
96c956 |
struct timeval tv;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (gettimeofday(&tv, NULL) < 0)
|
|
Packit |
96c956 |
LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
UTI_TimevalToTimespec(&tv, ts);
|
|
Packit |
96c956 |
#endif
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_ReadCookedTime(struct timespec *result, double *err)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, result, err);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
double correction;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_GetOffsetCorrection(raw, &correction, err);
|
|
Packit |
96c956 |
UTI_AddDoubleToTimespec(raw, correction, cooked);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
/* Call system specific driver to get correction */
|
|
Packit |
96c956 |
(*drv_offset_convert)(raw, correction, err);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
/* Return current frequency */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
double
|
|
Packit |
96c956 |
LCL_ReadAbsoluteFrequency(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
double freq;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
freq = current_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Undo temperature compensation */
|
|
Packit |
96c956 |
if (temp_comp_ppm != 0.0) {
|
|
Packit |
96c956 |
freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
return freq;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static double
|
|
Packit |
96c956 |
clamp_freq(double freq)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
|
|
Packit |
96c956 |
return freq;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
static int
|
|
Packit |
96c956 |
check_offset(struct timespec *now, double offset)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
/* Check if the time will be still sane with accumulated offset */
|
|
Packit |
96c956 |
if (UTI_IsTimeOffsetSane(now, -offset))
|
|
Packit |
96c956 |
return 1;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
|
|
Packit |
96c956 |
return 0;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* This involves both setting the absolute frequency with the
|
|
Packit |
96c956 |
system-specific driver, as well as calling all notify handlers */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_SetAbsoluteFrequency(double afreq_ppm)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
double dfreq;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
afreq_ppm = clamp_freq(afreq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Apply temperature compensation */
|
|
Packit |
96c956 |
if (temp_comp_ppm != 0.0) {
|
|
Packit |
96c956 |
afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Call the system-specific driver for setting the frequency */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
afreq_ppm = (*drv_set_freq)(afreq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
current_freq_ppm = afreq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_AccumulateDeltaFrequency(double dfreq)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
double old_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
old_freq_ppm = current_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Work out new absolute frequency. Note that absolute frequencies
|
|
Packit |
96c956 |
are handled in units of ppm, whereas the 'dfreq' argument is in
|
|
Packit |
96c956 |
terms of the gradient of the (offset) v (local time) function. */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
current_freq_ppm = clamp_freq(current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Call the system-specific driver for setting the frequency */
|
|
Packit |
96c956 |
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
|
|
Packit |
96c956 |
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_AccumulateOffset(double offset, double corr_rate)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* In this case, the cooked time to be passed to the notify clients
|
|
Packit |
96c956 |
has to be the cooked time BEFORE the change was made */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (!check_offset(&cooked, offset))
|
|
Packit |
96c956 |
return;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
(*drv_accrue_offset)(offset, corr_rate);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
int
|
|
Packit |
96c956 |
LCL_ApplyStepOffset(double offset)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* In this case, the cooked time to be passed to the notify clients
|
|
Packit |
96c956 |
has to be the cooked time BEFORE the change was made */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (!check_offset(&raw, offset))
|
|
Packit |
96c956 |
return 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (!(*drv_apply_step_offset)(offset)) {
|
|
Packit |
96c956 |
LOG(LOGS_ERR, "Could not step system clock");
|
|
Packit |
96c956 |
return 0;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Reset smoothing on all clock steps */
|
|
Packit |
96c956 |
SMT_Reset(&cooked);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
return 1;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
|
|
Packit |
96c956 |
double offset, double dispersion)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
lcl_InvokeDispersionNotifyHandlers(dispersion);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_NotifyLeap(int leap)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Smooth the leap second out */
|
|
Packit |
96c956 |
SMT_Leap(&cooked, leap);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers as if the clock was stepped */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw, cooked;
|
|
Packit |
96c956 |
double old_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
/* Due to modifying the offset, this has to be the cooked time prior
|
|
Packit |
96c956 |
to the change we are about to make */
|
|
Packit |
96c956 |
LCL_CookTime(&raw, &cooked, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (!check_offset(&cooked, doffset))
|
|
Packit |
96c956 |
return;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
old_freq_ppm = current_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Work out new absolute frequency. Note that absolute frequencies
|
|
Packit |
96c956 |
are handled in units of ppm, whereas the 'dfreq' argument is in
|
|
Packit |
96c956 |
terms of the gradient of the (offset) v (local time) function. */
|
|
Packit |
96c956 |
current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
current_freq_ppm = clamp_freq(current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
|
|
Packit |
96c956 |
old_freq_ppm, current_freq_ppm, doffset);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Call the system-specific driver for setting the frequency */
|
|
Packit |
96c956 |
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
|
|
Packit |
96c956 |
dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
(*drv_accrue_offset)(doffset, corr_rate);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Dispatch to all handlers */
|
|
Packit |
96c956 |
invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
lcl_InvokeDispersionNotifyHandlers(double dispersion)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
DispersionNotifyListEntry *ptr;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
|
|
Packit |
96c956 |
(ptr->handler)(dispersion, ptr->anything);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
|
|
Packit |
96c956 |
lcl_SetFrequencyDriver set_freq,
|
|
Packit |
96c956 |
lcl_AccrueOffsetDriver accrue_offset,
|
|
Packit |
96c956 |
lcl_ApplyStepOffsetDriver apply_step_offset,
|
|
Packit |
96c956 |
lcl_OffsetCorrectionDriver offset_convert,
|
|
Packit |
96c956 |
lcl_SetLeapDriver set_leap,
|
|
Packit |
96c956 |
lcl_SetSyncStatusDriver set_sync_status)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
drv_read_freq = read_freq;
|
|
Packit |
96c956 |
drv_set_freq = set_freq;
|
|
Packit |
96c956 |
drv_accrue_offset = accrue_offset;
|
|
Packit |
96c956 |
drv_apply_step_offset = apply_step_offset;
|
|
Packit |
96c956 |
drv_offset_convert = offset_convert;
|
|
Packit |
96c956 |
drv_set_leap = set_leap;
|
|
Packit |
96c956 |
drv_set_sync_status = set_sync_status;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
current_freq_ppm = (*drv_read_freq)();
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
/* Look at the current difference between the system time and the NTP
|
|
Packit |
96c956 |
time, and make a step to cancel it. */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
int
|
|
Packit |
96c956 |
LCL_MakeStep(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
struct timespec raw;
|
|
Packit |
96c956 |
double correction;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LCL_ReadRawTime(&raw;;
|
|
Packit |
96c956 |
LCL_GetOffsetCorrection(&raw, &correction, NULL);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (!check_offset(&raw, -correction))
|
|
Packit |
96c956 |
return 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Cancel remaining slew and make the step */
|
|
Packit |
96c956 |
LCL_AccumulateOffset(correction, 0.0);
|
|
Packit |
96c956 |
if (!LCL_ApplyStepOffset(-correction))
|
|
Packit |
96c956 |
return 0;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
return 1;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
int
|
|
Packit |
96c956 |
LCL_CanSystemLeap(void)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
return drv_set_leap ? 1 : 0;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_SetSystemLeap(int leap, int tai_offset)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
if (drv_set_leap) {
|
|
Packit |
96c956 |
(drv_set_leap)(leap, tai_offset);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
double
|
|
Packit |
96c956 |
LCL_SetTempComp(double comp)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
double uncomp_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
if (temp_comp_ppm == comp)
|
|
Packit |
96c956 |
return comp;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Undo previous compensation */
|
|
Packit |
96c956 |
current_freq_ppm = (current_freq_ppm + temp_comp_ppm) /
|
|
Packit |
96c956 |
(1.0 - 1.0e-6 * temp_comp_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
uncomp_freq_ppm = current_freq_ppm;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Apply new compensation */
|
|
Packit |
96c956 |
current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp;
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* Call the system-specific driver for setting the frequency */
|
|
Packit |
96c956 |
current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
temp_comp_ppm = (uncomp_freq_ppm - current_freq_ppm) /
|
|
Packit |
96c956 |
(1.0e-6 * uncomp_freq_ppm + 1.0);
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
return temp_comp_ppm;
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
void
|
|
Packit |
96c956 |
LCL_SetSyncStatus(int synchronised, double est_error, double max_error)
|
|
Packit |
96c956 |
{
|
|
Packit |
96c956 |
if (drv_set_sync_status) {
|
|
Packit |
96c956 |
(drv_set_sync_status)(synchronised, est_error, max_error);
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
}
|
|
Packit |
96c956 |
|
|
Packit |
96c956 |
/* ================================================== */
|