summaryrefslogtreecommitdiffstats
path: root/tempcomp.c
diff options
context:
space:
mode:
Diffstat (limited to 'tempcomp.c')
-rw-r--r--tempcomp.c180
1 files changed, 180 insertions, 0 deletions
diff --git a/tempcomp.c b/tempcomp.c
new file mode 100644
index 0000000..f57e5cc
--- /dev/null
+++ b/tempcomp.c
@@ -0,0 +1,180 @@
+/*
+ chronyd/chronyc - Programs for keeping computer clocks accurate.
+
+ **********************************************************************
+ * Copyright (C) Miroslav Lichvar 2011, 2014
+ *
+ * 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 "config.h"
+
+#include "array.h"
+#include "conf.h"
+#include "local.h"
+#include "memory.h"
+#include "util.h"
+#include "logging.h"
+#include "sched.h"
+#include "tempcomp.h"
+
+/* Sanity limit (in ppm) */
+#define MAX_COMP 10.0
+
+static SCH_TimeoutID timeout_id;
+
+static LOG_FileID logfileid;
+
+static char *filename;
+static double update_interval;
+static double T0, k0, k1, k2;
+
+struct Point {
+ double temp;
+ double comp;
+};
+
+static ARR_Instance points;
+
+static double
+get_tempcomp(double temp)
+{
+ unsigned int i;
+ struct Point *p1 = NULL, *p2 = NULL;
+
+ /* If not configured with points, calculate the compensation from the
+ specified quadratic function */
+ if (!points)
+ return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2;
+
+ /* Otherwise interpolate/extrapolate between two nearest points */
+
+ for (i = 1; i < ARR_GetSize(points); i++) {
+ p2 = (struct Point *)ARR_GetElement(points, i);
+ if (p2->temp >= temp)
+ break;
+ }
+ p1 = p2 - 1;
+
+ return (temp - p1->temp) / (p2->temp - p1->temp) *
+ (p2->comp - p1->comp) + p1->comp;
+}
+
+static void
+read_timeout(void *arg)
+{
+ FILE *f;
+ double temp, comp;
+
+ f = fopen(filename, "r");
+
+ if (f && fscanf(f, "%lf", &temp) == 1) {
+ comp = get_tempcomp(temp);
+
+ if (fabs(comp) <= MAX_COMP) {
+ comp = LCL_SetTempComp(comp);
+
+ DEBUG_LOG("tempcomp updated to %f for %f", comp, temp);
+
+ if (logfileid != -1) {
+ struct timespec now;
+
+ LCL_ReadCookedTime(&now, NULL);
+ LOG_FileWrite(logfileid, "%s %11.4e %11.4e",
+ UTI_TimeToLogForm(now.tv_sec), temp, comp);
+ }
+ } else {
+ LOG(LOGS_WARN, "Temperature compensation of %.3f ppm exceeds sanity limit of %.1f",
+ comp, MAX_COMP);
+ }
+ } else {
+ LOG(LOGS_WARN, "Could not read temperature from %s", filename);
+ }
+
+ if (f)
+ fclose(f);
+
+ timeout_id = SCH_AddTimeoutByDelay(update_interval, read_timeout, NULL);
+}
+
+static void
+read_points(const char *filename)
+{
+ FILE *f;
+ char line[256];
+ struct Point *p;
+
+ f = fopen(filename, "r");
+ if (!f) {
+ LOG_FATAL("Could not open tempcomp point file %s", filename);
+ return;
+ }
+
+ points = ARR_CreateInstance(sizeof (struct Point));
+
+ while (fgets(line, sizeof (line), f)) {
+ p = (struct Point *)ARR_GetNewElement(points);
+ if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) {
+ LOG_FATAL("Could not read tempcomp point from %s", filename);
+ break;
+ }
+ }
+
+ fclose(f);
+
+ if (ARR_GetSize(points) < 2)
+ LOG_FATAL("Not enough points in %s", filename);
+}
+
+void
+TMC_Initialise(void)
+{
+ char *point_file;
+
+ CNF_GetTempComp(&filename, &update_interval, &point_file, &T0, &k0, &k1, &k2);
+
+ if (filename == NULL)
+ return;
+
+ if (update_interval <= 0.0)
+ update_interval = 1.0;
+
+ if (point_file)
+ read_points(point_file);
+
+ logfileid = CNF_GetLogTempComp() ? LOG_FileOpen("tempcomp",
+ " Date (UTC) Time Temp. Comp.")
+ : -1;
+
+ read_timeout(NULL);
+}
+
+void
+TMC_Finalise(void)
+{
+ if (filename == NULL)
+ return;
+
+ if (points)
+ ARR_DestroyInstance(points);
+
+ SCH_RemoveTimeout(timeout_id);
+}