]>
Commit | Line | Data |
---|---|---|
c386d117 ML |
1 | /* |
2 | chronyd/chronyc - Programs for keeping computer clocks accurate. | |
3 | ||
4 | ********************************************************************** | |
cb74f3e7 | 5 | * Copyright (C) Miroslav Lichvar 2011, 2014 |
c386d117 ML |
6 | * |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of version 2 of the GNU General Public License as | |
9 | * published by the Free Software Foundation. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | * | |
20 | ********************************************************************** | |
21 | ||
22 | ======================================================================= | |
23 | ||
24 | Routines implementing temperature compensation. | |
25 | ||
26 | */ | |
27 | ||
da2c8d90 ML |
28 | #include "config.h" |
29 | ||
ed862c8d | 30 | #include "array.h" |
c386d117 ML |
31 | #include "conf.h" |
32 | #include "local.h" | |
33 | #include "memory.h" | |
34 | #include "util.h" | |
35 | #include "logging.h" | |
36 | #include "sched.h" | |
37 | #include "tempcomp.h" | |
38 | ||
0a56c0e8 ML |
39 | /* Sanity limit (in ppm) */ |
40 | #define MAX_COMP 10.0 | |
41 | ||
c386d117 ML |
42 | static SCH_TimeoutID timeout_id; |
43 | ||
44 | static LOG_FileID logfileid; | |
45 | ||
46 | static char *filename; | |
47 | static double update_interval; | |
48 | static double T0, k0, k1, k2; | |
49 | ||
ed862c8d ML |
50 | struct Point { |
51 | double temp; | |
52 | double comp; | |
53 | }; | |
54 | ||
55 | static ARR_Instance points; | |
56 | ||
57 | static double | |
58 | get_tempcomp(double temp) | |
59 | { | |
60 | unsigned int i; | |
61 | struct Point *p1 = NULL, *p2 = NULL; | |
62 | ||
63 | /* If not configured with points, calculate the compensation from the | |
64 | specified quadratic function */ | |
65 | if (!points) | |
66 | return k0 + (temp - T0) * k1 + (temp - T0) * (temp - T0) * k2; | |
67 | ||
68 | /* Otherwise interpolate/extrapolate between two nearest points */ | |
69 | ||
70 | for (i = 1; i < ARR_GetSize(points); i++) { | |
71 | p2 = (struct Point *)ARR_GetElement(points, i); | |
72 | if (p2->temp >= temp) | |
73 | break; | |
74 | } | |
75 | p1 = p2 - 1; | |
76 | ||
77 | return (temp - p1->temp) / (p2->temp - p1->temp) * | |
78 | (p2->comp - p1->comp) + p1->comp; | |
79 | } | |
80 | ||
c386d117 ML |
81 | static void |
82 | read_timeout(void *arg) | |
83 | { | |
84 | FILE *f; | |
85 | double temp, comp; | |
86 | ||
e18903a6 | 87 | f = UTI_OpenFile(NULL, filename, NULL, 'r', 0); |
c386d117 ML |
88 | |
89 | if (f && fscanf(f, "%lf", &temp) == 1) { | |
ed862c8d | 90 | comp = get_tempcomp(temp); |
c386d117 | 91 | |
0a56c0e8 | 92 | if (fabs(comp) <= MAX_COMP) { |
1a7415a6 | 93 | comp = LCL_SetTempComp(comp); |
c386d117 | 94 | |
f282856c | 95 | DEBUG_LOG("tempcomp updated to %f for %f", comp, temp); |
ed862c8d | 96 | |
c386d117 | 97 | if (logfileid != -1) { |
d0dfa1de | 98 | struct timespec now; |
c386d117 ML |
99 | |
100 | LCL_ReadCookedTime(&now, NULL); | |
101 | LOG_FileWrite(logfileid, "%s %11.4e %11.4e", | |
102 | UTI_TimeToLogForm(now.tv_sec), temp, comp); | |
103 | } | |
7a512ad9 | 104 | } else { |
f282856c | 105 | LOG(LOGS_WARN, "Temperature compensation of %.3f ppm exceeds sanity limit of %.1f", |
7a512ad9 | 106 | comp, MAX_COMP); |
c386d117 | 107 | } |
7a512ad9 | 108 | } else { |
f282856c | 109 | LOG(LOGS_WARN, "Could not read temperature from %s", filename); |
c386d117 ML |
110 | } |
111 | ||
112 | if (f) | |
113 | fclose(f); | |
114 | ||
115 | timeout_id = SCH_AddTimeoutByDelay(update_interval, read_timeout, NULL); | |
116 | } | |
117 | ||
ed862c8d ML |
118 | static void |
119 | read_points(const char *filename) | |
120 | { | |
121 | FILE *f; | |
122 | char line[256]; | |
123 | struct Point *p; | |
124 | ||
e18903a6 | 125 | f = UTI_OpenFile(NULL, filename, NULL, 'R', 0); |
ed862c8d ML |
126 | |
127 | points = ARR_CreateInstance(sizeof (struct Point)); | |
128 | ||
129 | while (fgets(line, sizeof (line), f)) { | |
130 | p = (struct Point *)ARR_GetNewElement(points); | |
131 | if (sscanf(line, "%lf %lf", &p->temp, &p->comp) != 2) { | |
f282856c | 132 | LOG_FATAL("Could not read tempcomp point from %s", filename); |
ed862c8d ML |
133 | break; |
134 | } | |
135 | } | |
136 | ||
137 | fclose(f); | |
138 | ||
139 | if (ARR_GetSize(points) < 2) | |
f282856c | 140 | LOG_FATAL("Not enough points in %s", filename); |
ed862c8d ML |
141 | } |
142 | ||
c386d117 ML |
143 | void |
144 | TMC_Initialise(void) | |
145 | { | |
ed862c8d ML |
146 | char *point_file; |
147 | ||
148 | CNF_GetTempComp(&filename, &update_interval, &point_file, &T0, &k0, &k1, &k2); | |
c386d117 ML |
149 | |
150 | if (filename == NULL) | |
151 | return; | |
152 | ||
153 | if (update_interval <= 0.0) | |
154 | update_interval = 1.0; | |
155 | ||
ed862c8d ML |
156 | if (point_file) |
157 | read_points(point_file); | |
158 | ||
c386d117 ML |
159 | logfileid = CNF_GetLogTempComp() ? LOG_FileOpen("tempcomp", |
160 | " Date (UTC) Time Temp. Comp.") | |
161 | : -1; | |
162 | ||
163 | read_timeout(NULL); | |
164 | } | |
165 | ||
166 | void | |
167 | TMC_Finalise(void) | |
168 | { | |
169 | if (filename == NULL) | |
170 | return; | |
171 | ||
ed862c8d ML |
172 | if (points) |
173 | ARR_DestroyInstance(points); | |
174 | ||
c386d117 | 175 | SCH_RemoveTimeout(timeout_id); |
c386d117 | 176 | } |