nameserv.o acquire.o manual.o addrfilt.o \
cmdparse.o mkdirpp.o rtc.o pktlength.o clientlog.o \
broadcast.o refclock.o refclock_shm.o refclock_sock.o \
- refclock_pps.o
+ refclock_pps.o tempcomp.o
EXTRA_OBJS=@EXTRA_OBJECTS@
* server directive:: Specify an NTP server
* sched_priority directive:: Require real-time scheduling and specify a priority for it.
* lock_all directive:: Require that chronyd be locked into RAM.
+* tempcomp directive:: Specify temperature sensor and compensation coefficients
@end menu
@c }}}
@item refclocks
This option logs the raw and filtered reference clock measurements to
a file called refclocks.log.
+@item tempcomp
+This option logs the temperature measurements and system rate
+compensations to a file called tempcomp.log.
@end table
The files are written to the directory specified by the logdir
* tracking log:: The format of the tracking log
* RTC log:: The format of the RTC log
* refclocks log:: The format of the refclocks log
+* tempcomp log:: The format of the tempcomp log
@end menu
@c }}}
@c {{{ measurements.log
Assumed dispersion of the sample. [1.000e-06]
@end enumerate
+A banner is periodically written to the log file to indicate the
+meanings of the columns.
+@c }}}
+@c {{{ tempcomp.log
+@node tempcomp log
+@subsubsection Tempcomp log file format
+
+An example line (which actually appears as a single line in the file)
+from the tempcomp log file is shown below.
+
+@example
+2010-04-19 10:39:48 2.8000e+04 3.6600e-01
+@end example
+
+The columns are as follows (the quantities in square brackets are the
+values from the example line above) :
+
+@enumerate 1
+@item
+Date [2010-04-19]
+@item
+Hour:Minute:Second [10:39:48]. Note that the
+date/time pair is expressed in UTC, not the local time zone.
+@item
+Temperature read from tempcomp file. [2.8000e+04]
+@item
+Applied compensation in ppm, positive means the system clock is
+running faster than it would be without the compensation. [3.6600e-01]
+@end enumerate
+
A banner is periodically written to the log file to indicate the
meanings of the columns.
@c }}}
established, to enable measurements to start.)
@end table
+@c }}}
+@c {{{ tempcomp
+@node tempcomp directive
+@subsection tempcomp
+Normally, changes in rate of drift of the system clock are caused mainly by
+changes in temperature of the crystal oscillator on the mainboard.
+
+If there are available temperature measurements from a sensor close to the
+oscillator, @code{tempcomp} directive can be used to compensate for the changes
+in rate and possibly improve clock accuracy.
+
+Whether it will really help depends on many factors, including resolution of
+the sensor, noise in measurements, time source polling interval, compensation
+update interval, how good are the temperature coefficients, and how close is
+the sensor to the oscillator. The frequency reported in tracking.log should
+be more stable and the offsets should be smaller.
+
+The directive has six parameters: path to the file which contains current
+temperature in text format, update interval (in seconds), and temperature
+coefficients T0, k0, k1, k2.
+
+The frequency compensation is calculated (in ppm) as
+
+@code{k0 + (T - T0) * k1 + (T - T0)^2 * k2}
+
+The result has to be between -10 ppm and 10 ppm, otherwise the measurement is
+considered to be faulty and will be ignored. The k0 coefficient can be used to
+get the results in that range.
+
+Valid measurements and calculated corrections are logged to tempcomp.log file if
+enabled with @code{log tempcomp} directive.
+
+An example of use is
+
+@example
+tempcomp /sys/class/hwmon/hwmon1/device/temp2_input 30 26000 0.0 0.000183 0.0
+@end example
+
+The measured temperature will be read from the file in Linux sysfs filesystem
+every 30 seconds. When the temperature is 26 degress (26000), the system clock
+frequency will not be adjusted. When it is 27 degrees (27000), the clock will
+be set to run 0.183ppm faster than it would be without the compensation, etc.
+
@c }}}
@c }}}
@c {{{ S:Running chronyc
static void parse_linux_freq_scale(const char *);
static void parse_sched_priority(const char *);
static void parse_lockall(const char *);
+static void parse_tempcomp(const char *);
/* ================================================== */
/* Configuration variables */
static int do_log_tracking = 0;
static int do_log_rtc = 0;
static int do_log_refclocks = 0;
+static int do_log_tempcomp = 0;
static int do_dump_on_exit = 0;
static char *logdir = ".";
static char *dumpdir = ".";
* chronyds being started. */
static char *pidfile = "/var/run/chronyd.pid";
+/* Temperature sensor, update interval and compensation coefficients */
+static char *tempcomp_file = NULL;
+static double tempcomp_interval;
+static double tempcomp_T0, tempcomp_k0, tempcomp_k1, tempcomp_k2;
+
/* Boolean for whether the Linux HZ value has been overridden, and the
* new value. */
static int set_linux_hz = 0;
{"rtcdevice", 9, parse_rtcdevice},
{"pidfile", 7, parse_pidfile},
{"broadcast", 9, parse_broadcast},
+ {"tempcomp", 8, parse_tempcomp},
{"linux_hz", 8, parse_linux_hz},
{"linux_freq_scale", 16, parse_linux_freq_scale},
{"sched_priority", 14, parse_sched_priority},
} else if (!strncmp(line, "refclocks", 9)) {
do_log_refclocks = 1;
line += 9;
+ } else if (!strncmp(line, "tempcomp", 8)) {
+ do_log_tempcomp = 1;
+ line += 8;
} else {
break;
}
/* ================================================== */
+static void
+parse_tempcomp(const char *line)
+{
+ const char *tmp;
+
+ while (isspace(line[0]))
+ line++;
+ tmp = line;
+ while (line[0] != '\0' && !isspace(line[0]))
+ line++;
+
+ if (line == tmp) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read tempcomp filename at line %d", line_number);
+ return;
+ }
+
+ if (sscanf(line, "%lf %lf %lf %lf %lf", &tempcomp_interval, &tempcomp_T0, &tempcomp_k0, &tempcomp_k1, &tempcomp_k2) != 5) {
+ LOG(LOGS_WARN, LOGF_Configure, "Could not read tempcomp interval or coefficients at line %d", line_number);
+ return;
+ }
+
+ tempcomp_file = MallocArray(char, 1 + line - tmp);
+ strncpy(tempcomp_file, tmp, line - tmp);
+ tempcomp_file[line - tmp] = '\0';
+}
+
+/* ================================================== */
+
static void
parse_linux_hz(const char *line)
{
}
/* ================================================== */
+
int
CNF_GetLogRefclocks(void)
{
/* ================================================== */
+int
+CNF_GetLogTempComp(void)
+{
+ return do_log_tempcomp;
+}
+
+/* ================================================== */
+
char *
CNF_GetKeysFile(void)
{
{
return lock_memory;
}
+
+/* ================================================== */
+
+void
+CNF_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k1, double *k2)
+{
+ *file = tempcomp_file;
+ *interval = tempcomp_interval;
+ *T0 = tempcomp_T0;
+ *k0 = tempcomp_k0;
+ *k1 = tempcomp_k1;
+ *k2 = tempcomp_k2;
+}
extern int CNF_GetLogTracking(void);
extern int CNF_GetLogRtc(void);
extern int CNF_GetLogRefclocks(void);
+extern int CNF_GetLogTempComp(void);
extern char *CNF_GetKeysFile(void);
extern char *CNF_GetRtcFile(void);
extern unsigned long CNF_GetCommandKey(void);
extern int CNF_GetSchedPriority(void);
extern int CNF_GetLockMemory(void);
+extern void CNF_GetTempComp(char **file, double *interval, double *T0, double *k0, double *k1, double *k2);
+
#endif /* GOT_CONF_H */
/* Variable to store the current frequency, in ppm */
static double current_freq_ppm;
+/* Temperature compensation, in ppm */
+static double temp_comp_ppm;
+
/* ================================================== */
/* Store the system dependent drivers */
/* This ought to be set from the system driver layer */
current_freq_ppm = 0.0;
+ temp_comp_ppm = 0.0;
calculate_sys_precision();
}
double
LCL_ReadAbsoluteFrequency(void)
{
- return (*drv_read_freq)();
+ double freq;
+
+ freq = (*drv_read_freq)();
+
+ /* Undo temperature compensation */
+ if (temp_comp_ppm != 0.0) {
+ freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm);
+ }
+
+ return freq;
}
/* ================================================== */
struct timeval raw, cooked;
double dfreq;
+ /* Apply temperature compensation */
+ if (temp_comp_ppm != 0.0) {
+ afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
+ }
+
/* Call the system-specific driver for setting the frequency */
(*drv_set_freq)(afreq_ppm);
}
/* ================================================== */
+
+void
+LCL_SetTempComp(double comp)
+{
+ if (temp_comp_ppm == comp)
+ return;
+
+ /* Undo previous compensation */
+ current_freq_ppm = (current_freq_ppm + temp_comp_ppm) /
+ (1.0 - 1.0e-6 * temp_comp_ppm);
+
+ /* Apply new compensation */
+ current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp;
+
+ temp_comp_ppm = comp;
+
+ /* Call the system-specific driver for setting the frequency */
+ (*drv_set_freq)(current_freq_ppm);
+
+ return;
+}
+
+/* ================================================== */
and zero cancels scheduled leap second. */
extern void LCL_SetLeap(int leap);
+/* Routine to set a frequency correction (in ppm) that should be applied
+ to local clock to compensate for temperature changes. A positive
+ argument means that the clock frequency should be increased. */
+extern void LCL_SetTempComp(double comp);
+
#endif /* GOT_LOCAL_H */
static int n_filelogs = 0;
/* Increase this when adding a new logfile */
-#define MAX_FILELOGS 5
+#define MAX_FILELOGS 6
static struct LogFile logfiles[MAX_FILELOGS];
#include "clientlog.h"
#include "broadcast.h"
#include "nameserv.h"
+#include "tempcomp.h"
/* ================================================== */
SRC_DumpSources();
}
+ TMC_Finalise();
MNL_Finalise();
ACQ_Finalise();
KEY_Finalise();
KEY_Initialise();
ACQ_Initialise();
MNL_Initialise();
+ TMC_Initialise();
LOG_CreateLogFileDir();
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Routines implementing temperature compensation.
+
+ */
+
+#include "conf.h"
+#include "local.h"
+#include "memory.h"
+#include "util.h"
+#include "logging.h"
+#include "sched.h"
+#include "tempcomp.h"
+
+static SCH_TimeoutID timeout_id;
+
+static LOG_FileID logfileid;
+
+static char *filename;
+static double update_interval;
+static double T0, k0, k1, k2;
+
+static void
+read_timeout(void *arg)
+{
+ FILE *f;
+ double temp, comp;
+
+ f = fopen(filename, "r");
+
+ if (f && fscanf(f, "%lf", &temp) == 1) {
+ comp = k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
+
+ /* Don't allow corrections above 10 ppm */
+ if (fabs(comp) < 10.0) {
+ LCL_SetTempComp(comp);
+
+ if (logfileid != -1) {
+ struct timeval now;
+
+ LCL_ReadCookedTime(&now, NULL);
+ LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
+ UTI_TimeToLogForm(now.tv_sec), temp, comp);
+ }
+ }
+ }
+
+ if (f)
+ fclose(f);
+
+ timeout_id = SCH_AddTimeoutByDelay(update_interval, read_timeout, NULL);
+}
+
+void
+TMC_Initialise(void)
+{
+ CNF_GetTempComp(&filename, &update_interval, &T0, &k0, &k1, &k2);
+
+ if (filename == NULL)
+ return;
+
+ if (update_interval <= 0.0)
+ update_interval = 1.0;
+
+ logfileid = CNF_GetLogTempComp() ? LOG_FileOpen("tempcomp",
+ " Date (UTC) Time Temp. Comp.")
+ : -1;
+
+ read_timeout(NULL);
+}
+
+void
+TMC_Finalise(void)
+{
+ if (filename == NULL)
+ return;
+
+ SCH_RemoveTimeout(timeout_id);
+ Free(filename);
+}
--- /dev/null
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2010
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ **********************************************************************
+
+ =======================================================================
+
+ Header file for temperature compensation.
+
+ */
+
+extern void TMC_Initialise(void);
+extern void TMC_Finalise(void);