]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add temperature compensation
authorMiroslav Lichvar <mlichvar@redhat.com>
Mon, 12 Apr 2010 15:07:57 +0000 (17:07 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Tue, 27 Apr 2010 12:35:11 +0000 (14:35 +0200)
A new tempcomp directive can be used to specify a file for reading
current temperature, update interval and compensation coefficients. The
clock frequency corrections are applied in local module and are invisible
in upper layers. The measurements and corrections can be logged to
tempcomp.log file.

Makefile.in
chrony.texi
conf.c
conf.h
local.c
local.h
logging.c
main.c
tempcomp.c [new file with mode: 0644]
tempcomp.h [new file with mode: 0644]

index 23a83fee66f4fcd403829dd7424200c23308d86f..fd12f799ef59e3d5ab3d68ec697d561feb947007 100644 (file)
@@ -46,7 +46,7 @@ OBJS = util.o sched.o regress.o local.o \
        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@
 
index 01be63538b0ae556d1976212dbf8f16cb9abec46..e0ab4f1d04a4bf3fa4adfb8ad5e2dddff4963ca3 100644 (file)
@@ -1200,6 +1200,7 @@ directives can occur in any order in the file.
 * 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 }}}
@@ -1790,6 +1791,9 @@ This option logs information about the system's real-time clock.
 @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
@@ -1807,6 +1811,7 @@ log measurements statistics tracking
 * 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
@@ -2065,6 +2070,36 @@ that the local clock is slow. [-6.741777e-07]
 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 }}}
@@ -2563,6 +2598,49 @@ chronyc's @code{online} (@pxref{online command}) command when the link has been
 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
diff --git a/conf.c b/conf.c
index d1d075845b513cd6ee70311fcb36b6f05d67cb53..dd8940b36c77027c252922db42af3b984c5db9ff 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -105,6 +105,7 @@ static void parse_linux_hz(const char *);
 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 */
@@ -125,6 +126,7 @@ static int do_log_statistics = 0;
 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 = ".";
@@ -183,6 +185,11 @@ static IPAddr bind_cmd_address4, bind_cmd_address6;
  * 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;
@@ -239,6 +246,7 @@ static const Command commands[] = {
   {"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},
@@ -679,6 +687,9 @@ parse_log(const char *line)
       } 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;
       }
@@ -1114,6 +1125,34 @@ parse_broadcast(const char *line)
 
 /* ================================================== */
 
+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)
 {
@@ -1273,6 +1312,7 @@ CNF_GetLogRtc(void)
 }
 
 /* ================================================== */
+
 int
 CNF_GetLogRefclocks(void)
 {
@@ -1281,6 +1321,14 @@ CNF_GetLogRefclocks(void)
 
 /* ================================================== */
 
+int
+CNF_GetLogTempComp(void)
+{
+  return do_log_tempcomp;
+}
+
+/* ================================================== */
+
 char *
 CNF_GetKeysFile(void)
 {
@@ -1514,3 +1562,16 @@ CNF_GetLockMemory(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;
+}
diff --git a/conf.h b/conf.h
index 1ec99f2ea0072ab4a9236340f2bf5365b721fa6a..31f4cf065d7b62c3e923b2174c78d291d97e8855 100644 (file)
--- a/conf.h
+++ b/conf.h
@@ -53,6 +53,7 @@ extern int CNF_GetLogStatistics(void);
 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);
@@ -81,4 +82,6 @@ extern void CNF_SetupAccessRestrictions(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 */
diff --git a/local.c b/local.c
index 3c5efb35523b2e8c4d38488e0c0c0f85e51e85c3..dbda7444982382b337fc58103ec0b8b7c0587275 100644 (file)
--- a/local.c
+++ b/local.c
@@ -45,6 +45,9 @@
 /* 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 */
 
@@ -152,6 +155,7 @@ LCL_Initialise(void)
 
   /* This ought to be set from the system driver layer */
   current_freq_ppm = 0.0;
+  temp_comp_ppm = 0.0;
 
   calculate_sys_precision();
 }
@@ -360,7 +364,16 @@ LCL_GetOffsetCorrection(struct timeval *raw, double *correction, double *err)
 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;
 }
 
 /* ================================================== */
@@ -374,6 +387,11 @@ LCL_SetAbsoluteFrequency(double afreq_ppm)
   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);
@@ -580,3 +598,26 @@ LCL_SetLeap(int leap)
 }
 
 /* ================================================== */
+
+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;
+}
+
+/* ================================================== */
diff --git a/local.h b/local.h
index bef1a3e01d82a02cf95ba65b5d17336030c9e3ad..2b9ea82d5f264c608392b7dbbbd1072b3f0c4a65 100644 (file)
--- a/local.h
+++ b/local.h
@@ -188,4 +188,9 @@ extern int LCL_MakeStep(double threshold);
    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 */
index 8271c001743170c2d41eff347a34b73f5a91d610..a2f471e692953c94f2a3fa365b18e576ab45d148 100644 (file)
--- a/logging.c
+++ b/logging.c
@@ -58,7 +58,7 @@ struct LogFile {
 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];
 
diff --git a/main.c b/main.c
index 31f4a5c93efcc3a848042d4f7126ec8295b9a2fa..7da6250b2c7debab3e5a7810cbb2107cb8469998 100644 (file)
--- a/main.c
+++ b/main.c
@@ -53,6 +53,7 @@
 #include "clientlog.h"
 #include "broadcast.h"
 #include "nameserv.h"
+#include "tempcomp.h"
 
 /* ================================================== */
 
@@ -86,6 +87,7 @@ MAI_CleanupAndExit(void)
     SRC_DumpSources();
   }
 
+  TMC_Finalise();
   MNL_Finalise();
   ACQ_Finalise();
   KEY_Finalise();
@@ -326,6 +328,7 @@ int main
   KEY_Initialise();
   ACQ_Initialise();
   MNL_Initialise();
+  TMC_Initialise();
 
   LOG_CreateLogFileDir();
 
diff --git a/tempcomp.c b/tempcomp.c
new file mode 100644 (file)
index 0000000..d7f02ad
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+  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);
+}
diff --git a/tempcomp.h b/tempcomp.h
new file mode 100644 (file)
index 0000000..de65469
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+  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);