]> git.ipfire.org Git - thirdparty/chrony.git/blame - local.c
avoid some static analysis errors
[thirdparty/chrony.git] / local.c
CommitLineData
88840341 1/*
88840341
RC
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
6672f045 5 * Copyright (C) Richard P. Curnow 1997-2003
61272e7c 6 * Copyright (C) Miroslav Lichvar 2011, 2014-2015
88840341
RC
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of version 2 of the GNU General Public License as
10 * published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
8e23110a 19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
88840341
RC
20 *
21 **********************************************************************
22
23 =======================================================================
24
25 The routines in this file present a common local (system) clock
26 interface to the rest of the software.
27
28 They interface with the system specific driver files in sys_*.c
29 */
30
da2c8d90
ML
31#include "config.h"
32
6ca73bf6 33#include "sysincl.h"
88840341 34
cc3a8918 35#include "conf.h"
88840341
RC
36#include "local.h"
37#include "localp.h"
38#include "memory.h"
7f45eb79 39#include "smooth.h"
88840341
RC
40#include "util.h"
41#include "logging.h"
42
43/* ================================================== */
44
45/* Variable to store the current frequency, in ppm */
46static double current_freq_ppm;
47
a12c7c42
ML
48/* Maximum allowed frequency, in ppm */
49static double max_freq_ppm;
50
c386d117
ML
51/* Temperature compensation, in ppm */
52static double temp_comp_ppm;
53
88840341
RC
54/* ================================================== */
55/* Store the system dependent drivers */
56
57static lcl_ReadFrequencyDriver drv_read_freq;
58static lcl_SetFrequencyDriver drv_set_freq;
59static lcl_AccrueOffsetDriver drv_accrue_offset;
60static lcl_ApplyStepOffsetDriver drv_apply_step_offset;
61static lcl_OffsetCorrectionDriver drv_offset_convert;
8f9c2370 62static lcl_SetLeapDriver drv_set_leap;
e14a03a1 63static lcl_SetSyncStatusDriver drv_set_sync_status;
88840341
RC
64
65/* ================================================== */
66
67/* Types and variables associated with handling the parameter change
68 list */
69
70typedef struct _ChangeListEntry {
71 struct _ChangeListEntry *next;
72 struct _ChangeListEntry *prev;
73 LCL_ParameterChangeHandler handler;
74 void *anything;
75} ChangeListEntry;
76
77static ChangeListEntry change_list;
78
79/* ================================================== */
80
81/* Types and variables associated with handling the parameter change
82 list */
83
84typedef struct _DispersionNotifyListEntry {
85 struct _DispersionNotifyListEntry *next;
86 struct _DispersionNotifyListEntry *prev;
87 LCL_DispersionNotifyHandler handler;
88 void *anything;
89} DispersionNotifyListEntry;
90
91static DispersionNotifyListEntry dispersion_notify_list;
92
93/* ================================================== */
94
95static int precision_log;
96static double precision_quantum;
97
cc3a8918
ML
98static double max_clock_error;
99
88840341
RC
100/* ================================================== */
101
102/* Define the number of increments of the system clock that we want
103 to see to be fairly sure that we've got something approaching
104 the minimum increment. Even on a crummy implementation that can't
105 interpolate between 10ms ticks, we should get this done in
106 under 1s of busy waiting. */
107#define NITERS 100
108
d0dfa1de
ML
109#define NSEC_PER_SEC 1000000000
110
b7c7c293
ML
111static double
112measure_clock_precision(void)
88840341 113{
d0dfa1de
ML
114 struct timespec ts, old_ts;
115 int iters, diff, best;
116
117 LCL_ReadRawTime(&old_ts);
88840341 118
d0dfa1de
ML
119 /* Assume we must be better than a second */
120 best = NSEC_PER_SEC;
88840341 121 iters = 0;
d0dfa1de 122
88840341 123 do {
d0dfa1de
ML
124 LCL_ReadRawTime(&ts);
125
126 diff = NSEC_PER_SEC * (ts.tv_sec - old_ts.tv_sec) + (ts.tv_nsec - old_ts.tv_nsec);
127
128 old_ts = ts;
129 if (diff > 0) {
130 if (diff < best)
131 best = diff;
88840341
RC
132 iters++;
133 }
134 } while (iters < NITERS);
6b0198c2 135
d0dfa1de 136 assert(best > 0);
6b0198c2 137
b7c7c293 138 return 1.0e-9 * best;
88840341
RC
139}
140
141/* ================================================== */
142
143void
144LCL_Initialise(void)
145{
146 change_list.next = change_list.prev = &change_list;
147
148 dispersion_notify_list.next = dispersion_notify_list.prev = &dispersion_notify_list;
149
150 /* Null out the system drivers, so that we die
151 if they never get defined before use */
152
153 drv_read_freq = NULL;
154 drv_set_freq = NULL;
155 drv_accrue_offset = NULL;
156 drv_offset_convert = NULL;
157
158 /* This ought to be set from the system driver layer */
159 current_freq_ppm = 0.0;
c386d117 160 temp_comp_ppm = 0.0;
88840341 161
b7c7c293
ML
162 precision_quantum = CNF_GetClockPrecision();
163 if (precision_quantum <= 0.0)
164 precision_quantum = measure_clock_precision();
165
166 precision_quantum = CLAMP(1.0e-9, precision_quantum, 1.0);
167 precision_log = round(log(precision_quantum) / log(2.0));
168 /* NTP code doesn't support smaller log than -30 */
169 assert(precision_log >= -30);
170
171 DEBUG_LOG("Clock precision %.9f (%d)", precision_quantum, precision_log);
cc3a8918 172
a12c7c42
ML
173 /* This is the maximum allowed frequency offset in ppm, the time must
174 never stop or run backwards */
175 max_freq_ppm = CNF_GetMaxDrift();
176 max_freq_ppm = CLAMP(0.0, max_freq_ppm, 500000.0);
177
cc3a8918 178 max_clock_error = CNF_GetMaxClockError() * 1e-6;
88840341
RC
179}
180
181/* ================================================== */
182
183void
184LCL_Finalise(void)
185{
5f6f265f
ML
186 /* Make sure all handlers have been removed */
187 if (change_list.next != &change_list)
188 assert(0);
189 if (dispersion_notify_list.next != &dispersion_notify_list)
190 assert(0);
88840341
RC
191}
192
193/* ================================================== */
194
195/* Routine to read the system precision as a log to base 2 value. */
196int
197LCL_GetSysPrecisionAsLog(void)
198{
199 return precision_log;
200}
201
202/* ================================================== */
203/* Routine to read the system precision in terms of the actual time step */
204
205double
206LCL_GetSysPrecisionAsQuantum(void)
207{
208 return precision_quantum;
209}
210
211/* ================================================== */
212
cc3a8918
ML
213double
214LCL_GetMaxClockError(void)
215{
216 return max_clock_error;
217}
218
219/* ================================================== */
220
88840341
RC
221void
222LCL_AddParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
223{
224 ChangeListEntry *ptr, *new_entry;
225
226 /* Check that the handler is not already registered */
227 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
228 if (!(ptr->handler != handler || ptr->anything != anything)) {
6b0198c2 229 assert(0);
88840341
RC
230 }
231 }
232
233 new_entry = MallocNew(ChangeListEntry);
234
235 new_entry->handler = handler;
236 new_entry->anything = anything;
237
238 /* Chain it into the list */
239 new_entry->next = &change_list;
240 new_entry->prev = change_list.prev;
241 change_list.prev->next = new_entry;
242 change_list.prev = new_entry;
88840341
RC
243}
244
245/* ================================================== */
246
247/* Remove a handler */
88840341
RC
248void LCL_RemoveParameterChangeHandler(LCL_ParameterChangeHandler handler, void *anything)
249{
250
251 ChangeListEntry *ptr;
252 int ok;
253
254 ptr = NULL;
255 ok = 0;
256
257 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
258 if (ptr->handler == handler && ptr->anything == anything) {
259 ok = 1;
260 break;
261 }
262 }
263
6b0198c2 264 assert(ok);
88840341
RC
265
266 /* Unlink entry from the list */
267 ptr->next->prev = ptr->prev;
268 ptr->prev->next = ptr->next;
269
33647339 270 Free(ptr);
88840341
RC
271}
272
273/* ================================================== */
274
0bdac2c7
ML
275int
276LCL_IsFirstParameterChangeHandler(LCL_ParameterChangeHandler handler)
277{
278 return change_list.next->handler == handler;
279}
280
281/* ================================================== */
282
b69b648d 283static void
d0dfa1de 284invoke_parameter_change_handlers(struct timespec *raw, struct timespec *cooked,
b69b648d 285 double dfreq, double doffset,
44c9744d 286 LCL_ChangeType change_type)
b69b648d
ML
287{
288 ChangeListEntry *ptr;
289
290 for (ptr = change_list.next; ptr != &change_list; ptr = ptr->next) {
44c9744d 291 (ptr->handler)(raw, cooked, dfreq, doffset, change_type, ptr->anything);
b69b648d
ML
292 }
293}
294
295/* ================================================== */
296
88840341
RC
297void
298LCL_AddDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
299{
300 DispersionNotifyListEntry *ptr, *new_entry;
301
302 /* Check that the handler is not already registered */
303 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
304 if (!(ptr->handler != handler || ptr->anything != anything)) {
6b0198c2 305 assert(0);
88840341
RC
306 }
307 }
308
309 new_entry = MallocNew(DispersionNotifyListEntry);
310
311 new_entry->handler = handler;
312 new_entry->anything = anything;
313
314 /* Chain it into the list */
315 new_entry->next = &dispersion_notify_list;
316 new_entry->prev = dispersion_notify_list.prev;
317 dispersion_notify_list.prev->next = new_entry;
318 dispersion_notify_list.prev = new_entry;
88840341
RC
319}
320
321/* ================================================== */
322
323/* Remove a handler */
324extern
325void LCL_RemoveDispersionNotifyHandler(LCL_DispersionNotifyHandler handler, void *anything)
326{
327
328 DispersionNotifyListEntry *ptr;
329 int ok;
330
331 ptr = NULL;
332 ok = 0;
333
334 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
335 if (ptr->handler == handler && ptr->anything == anything) {
336 ok = 1;
337 break;
338 }
339 }
340
6b0198c2 341 assert(ok);
88840341
RC
342
343 /* Unlink entry from the list */
344 ptr->next->prev = ptr->prev;
345 ptr->prev->next = ptr->next;
346
33647339 347 Free(ptr);
88840341
RC
348}
349
350/* ================================================== */
88840341
RC
351
352void
d0dfa1de 353LCL_ReadRawTime(struct timespec *ts)
88840341 354{
8d89610f
ML
355#if HAVE_CLOCK_GETTIME
356 if (clock_gettime(CLOCK_REALTIME, ts) < 0)
f282856c 357 LOG_FATAL("clock_gettime() failed : %s", strerror(errno));
8d89610f 358#else
d0dfa1de
ML
359 struct timeval tv;
360
8d89610f 361 if (gettimeofday(&tv, NULL) < 0)
f282856c 362 LOG_FATAL("gettimeofday() failed : %s", strerror(errno));
d0dfa1de
ML
363
364 UTI_TimevalToTimespec(&tv, ts);
8d89610f 365#endif
88840341
RC
366}
367
368/* ================================================== */
369
370void
d0dfa1de 371LCL_ReadCookedTime(struct timespec *result, double *err)
88840341 372{
d0dfa1de 373 struct timespec raw;
88840341
RC
374
375 LCL_ReadRawTime(&raw);
20d898d1
ML
376 LCL_CookTime(&raw, result, err);
377}
88840341 378
20d898d1 379/* ================================================== */
88840341 380
20d898d1 381void
d0dfa1de 382LCL_CookTime(struct timespec *raw, struct timespec *cooked, double *err)
20d898d1
ML
383{
384 double correction;
88840341 385
20d898d1 386 LCL_GetOffsetCorrection(raw, &correction, err);
d0dfa1de 387 UTI_AddDoubleToTimespec(raw, correction, cooked);
88840341
RC
388}
389
390/* ================================================== */
391
20d898d1 392void
d0dfa1de 393LCL_GetOffsetCorrection(struct timespec *raw, double *correction, double *err)
88840341 394{
20d898d1 395 /* Call system specific driver to get correction */
1faeb450 396 (*drv_offset_convert)(raw, correction, err);
88840341
RC
397}
398
399/* ================================================== */
22fda21e 400/* Return current frequency */
88840341
RC
401
402double
403LCL_ReadAbsoluteFrequency(void)
404{
c386d117
ML
405 double freq;
406
22fda21e 407 freq = current_freq_ppm;
c386d117
ML
408
409 /* Undo temperature compensation */
410 if (temp_comp_ppm != 0.0) {
411 freq = (freq + temp_comp_ppm) / (1.0 - 1.0e-6 * temp_comp_ppm);
412 }
413
414 return freq;
88840341
RC
415}
416
417/* ================================================== */
183a648d
ML
418
419static double
420clamp_freq(double freq)
421{
a12c7c42 422 if (freq <= max_freq_ppm && freq >= -max_freq_ppm)
183a648d
ML
423 return freq;
424
f282856c 425 LOG(LOGS_WARN, "Frequency %.1f ppm exceeds allowed maximum", freq);
183a648d 426
a12c7c42 427 return CLAMP(-max_freq_ppm, freq, max_freq_ppm);
183a648d
ML
428}
429
430/* ================================================== */
431
aec97397 432static int
d0dfa1de 433check_offset(struct timespec *now, double offset)
aec97397
ML
434{
435 /* Check if the time will be still sane with accumulated offset */
436 if (UTI_IsTimeOffsetSane(now, -offset))
437 return 1;
438
f282856c 439 LOG(LOGS_WARN, "Adjustment of %.1f seconds is invalid", -offset);
aec97397
ML
440 return 0;
441}
442
443/* ================================================== */
444
88840341
RC
445/* This involves both setting the absolute frequency with the
446 system-specific driver, as well as calling all notify handlers */
447
448void
449LCL_SetAbsoluteFrequency(double afreq_ppm)
450{
d0dfa1de 451 struct timespec raw, cooked;
88840341
RC
452 double dfreq;
453
183a648d
ML
454 afreq_ppm = clamp_freq(afreq_ppm);
455
c386d117
ML
456 /* Apply temperature compensation */
457 if (temp_comp_ppm != 0.0) {
458 afreq_ppm = afreq_ppm * (1.0 - 1.0e-6 * temp_comp_ppm) - temp_comp_ppm;
459 }
460
88840341
RC
461 /* Call the system-specific driver for setting the frequency */
462
1a7415a6 463 afreq_ppm = (*drv_set_freq)(afreq_ppm);
88840341 464
60d0fa29 465 dfreq = (afreq_ppm - current_freq_ppm) / (1.0e6 - current_freq_ppm);
88840341
RC
466
467 LCL_ReadRawTime(&raw);
20d898d1 468 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
469
470 /* Dispatch to all handlers */
44c9744d 471 invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
88840341
RC
472
473 current_freq_ppm = afreq_ppm;
474
475}
476
477/* ================================================== */
478
479void
480LCL_AccumulateDeltaFrequency(double dfreq)
481{
d0dfa1de 482 struct timespec raw, cooked;
1a7415a6
ML
483 double old_freq_ppm;
484
485 old_freq_ppm = current_freq_ppm;
88840341
RC
486
487 /* Work out new absolute frequency. Note that absolute frequencies
488 are handled in units of ppm, whereas the 'dfreq' argument is in
489 terms of the gradient of the (offset) v (local time) function. */
490
d34ebdb4 491 current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
88840341 492
183a648d
ML
493 current_freq_ppm = clamp_freq(current_freq_ppm);
494
88840341 495 /* Call the system-specific driver for setting the frequency */
1a7415a6 496 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
60d0fa29 497 dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
88840341
RC
498
499 LCL_ReadRawTime(&raw);
20d898d1 500 LCL_CookTime(&raw, &cooked, NULL);
88840341
RC
501
502 /* Dispatch to all handlers */
44c9744d 503 invoke_parameter_change_handlers(&raw, &cooked, dfreq, 0.0, LCL_ChangeAdjust);
88840341
RC
504}
505
506/* ================================================== */
507
e86b60a9 508int
c7d0232b 509LCL_AccumulateOffset(double offset, double corr_rate)
88840341 510{
d0dfa1de 511 struct timespec raw, cooked;
88840341
RC
512
513 /* In this case, the cooked time to be passed to the notify clients
514 has to be the cooked time BEFORE the change was made */
515
516 LCL_ReadRawTime(&raw);
20d898d1 517 LCL_CookTime(&raw, &cooked, NULL);
88840341 518
aec97397 519 if (!check_offset(&cooked, offset))
e86b60a9 520 return 0;
aec97397 521
c7d0232b 522 (*drv_accrue_offset)(offset, corr_rate);
88840341
RC
523
524 /* Dispatch to all handlers */
44c9744d 525 invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeAdjust);
e86b60a9
ML
526
527 return 1;
88840341
RC
528}
529
530/* ================================================== */
531
aec97397 532int
88840341
RC
533LCL_ApplyStepOffset(double offset)
534{
d0dfa1de 535 struct timespec raw, cooked;
88840341
RC
536
537 /* In this case, the cooked time to be passed to the notify clients
538 has to be the cooked time BEFORE the change was made */
539
540 LCL_ReadRawTime(&raw);
20d898d1 541 LCL_CookTime(&raw, &cooked, NULL);
88840341 542
aec97397
ML
543 if (!check_offset(&raw, offset))
544 return 0;
545
f6a9c5c1 546 if (!(*drv_apply_step_offset)(offset)) {
0ff449e6 547 LOG(LOGS_ERR, "Could not step system clock");
f6a9c5c1
ML
548 return 0;
549 }
88840341 550
7f45eb79
ML
551 /* Reset smoothing on all clock steps */
552 SMT_Reset(&cooked);
553
88840341 554 /* Dispatch to all handlers */
44c9744d 555 invoke_parameter_change_handlers(&raw, &cooked, 0.0, offset, LCL_ChangeStep);
aec97397
ML
556
557 return 1;
88840341
RC
558}
559
560/* ================================================== */
561
91749ebb 562void
d0dfa1de 563LCL_NotifyExternalTimeStep(struct timespec *raw, struct timespec *cooked,
91749ebb
ML
564 double offset, double dispersion)
565{
9cc609c4
ML
566 LCL_CancelOffsetCorrection();
567
91749ebb 568 /* Dispatch to all handlers */
a33a9551 569 invoke_parameter_change_handlers(raw, cooked, 0.0, offset, LCL_ChangeUnknownStep);
91749ebb
ML
570
571 lcl_InvokeDispersionNotifyHandlers(dispersion);
572}
573
574/* ================================================== */
575
f8db8324
ML
576void
577LCL_NotifyLeap(int leap)
578{
d0dfa1de 579 struct timespec raw, cooked;
f8db8324
ML
580
581 LCL_ReadRawTime(&raw);
582 LCL_CookTime(&raw, &cooked, NULL);
583
0abdc2a3
ML
584 /* Smooth the leap second out */
585 SMT_Leap(&cooked, leap);
586
f8db8324
ML
587 /* Dispatch to all handlers as if the clock was stepped */
588 invoke_parameter_change_handlers(&raw, &cooked, 0.0, -leap, LCL_ChangeStep);
589}
590
591/* ================================================== */
592
e86b60a9 593int
c7d0232b 594LCL_AccumulateFrequencyAndOffset(double dfreq, double doffset, double corr_rate)
88840341 595{
d0dfa1de 596 struct timespec raw, cooked;
88840341
RC
597 double old_freq_ppm;
598
599 LCL_ReadRawTime(&raw);
88840341
RC
600 /* Due to modifying the offset, this has to be the cooked time prior
601 to the change we are about to make */
20d898d1 602 LCL_CookTime(&raw, &cooked, NULL);
88840341 603
aec97397 604 if (!check_offset(&cooked, doffset))
e86b60a9 605 return 0;
aec97397 606
88840341
RC
607 old_freq_ppm = current_freq_ppm;
608
609 /* Work out new absolute frequency. Note that absolute frequencies
610 are handled in units of ppm, whereas the 'dfreq' argument is in
611 terms of the gradient of the (offset) v (local time) function. */
d34ebdb4 612 current_freq_ppm += dfreq * (1.0e6 - current_freq_ppm);
88840341 613
183a648d
ML
614 current_freq_ppm = clamp_freq(current_freq_ppm);
615
f282856c 616 DEBUG_LOG("old_freq=%.3fppm new_freq=%.3fppm offset=%.6fsec",
88840341 617 old_freq_ppm, current_freq_ppm, doffset);
88840341
RC
618
619 /* Call the system-specific driver for setting the frequency */
1a7415a6 620 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
60d0fa29 621 dfreq = (current_freq_ppm - old_freq_ppm) / (1.0e6 - old_freq_ppm);
1a7415a6 622
c7d0232b 623 (*drv_accrue_offset)(doffset, corr_rate);
88840341
RC
624
625 /* Dispatch to all handlers */
58b211d7 626 invoke_parameter_change_handlers(&raw, &cooked, dfreq, doffset, LCL_ChangeAdjust);
e86b60a9
ML
627
628 return 1;
88840341
RC
629}
630
631/* ================================================== */
632
af6ae918
ML
633int
634LCL_AccumulateFrequencyAndOffsetNoHandlers(double dfreq, double doffset, double corr_rate)
635{
636 ChangeListEntry *first_handler;
637 int r;
638
639 first_handler = change_list.next;
640 change_list.next = &change_list;
641
642 r = LCL_AccumulateFrequencyAndOffset(dfreq, doffset, corr_rate);
643
644 change_list.next = first_handler;
645
646 return r;
647}
648
649/* ================================================== */
650
88840341
RC
651void
652lcl_InvokeDispersionNotifyHandlers(double dispersion)
653{
654 DispersionNotifyListEntry *ptr;
655
656 for (ptr = dispersion_notify_list.next; ptr != &dispersion_notify_list; ptr = ptr->next) {
657 (ptr->handler)(dispersion, ptr->anything);
658 }
659
660}
661
662/* ================================================== */
663
664void
665lcl_RegisterSystemDrivers(lcl_ReadFrequencyDriver read_freq,
666 lcl_SetFrequencyDriver set_freq,
667 lcl_AccrueOffsetDriver accrue_offset,
668 lcl_ApplyStepOffsetDriver apply_step_offset,
669 lcl_OffsetCorrectionDriver offset_convert,
e14a03a1
ML
670 lcl_SetLeapDriver set_leap,
671 lcl_SetSyncStatusDriver set_sync_status)
88840341
RC
672{
673 drv_read_freq = read_freq;
674 drv_set_freq = set_freq;
675 drv_accrue_offset = accrue_offset;
676 drv_apply_step_offset = apply_step_offset;
677 drv_offset_convert = offset_convert;
8f9c2370 678 drv_set_leap = set_leap;
e14a03a1 679 drv_set_sync_status = set_sync_status;
88840341
RC
680
681 current_freq_ppm = (*drv_read_freq)();
682
f282856c 683 DEBUG_LOG("Local freq=%.3fppm", current_freq_ppm);
88840341
RC
684}
685
686/* ================================================== */
687/* Look at the current difference between the system time and the NTP
20d2363f 688 time, and make a step to cancel it. */
88840341
RC
689
690int
20d2363f 691LCL_MakeStep(void)
88840341 692{
d0dfa1de 693 struct timespec raw;
15e154c0
ML
694 double correction;
695
696 LCL_ReadRawTime(&raw);
20d898d1 697 LCL_GetOffsetCorrection(&raw, &correction, NULL);
15e154c0 698
aec97397
ML
699 if (!check_offset(&raw, -correction))
700 return 0;
701
15e154c0 702 /* Cancel remaining slew and make the step */
c7d0232b 703 LCL_AccumulateOffset(correction, 0.0);
aec97397
ML
704 if (!LCL_ApplyStepOffset(-correction))
705 return 0;
15e154c0 706
f282856c 707 LOG(LOGS_WARN, "System clock was stepped by %.6f seconds", correction);
88840341 708
15e154c0 709 return 1;
88840341
RC
710}
711
712/* ================================================== */
8f9c2370 713
3cef7f97
ML
714void
715LCL_CancelOffsetCorrection(void)
716{
717 struct timespec raw;
718 double correction;
719
720 LCL_ReadRawTime(&raw);
721 LCL_GetOffsetCorrection(&raw, &correction, NULL);
722 LCL_AccumulateOffset(correction, 0.0);
723}
724
725/* ================================================== */
726
802a98e7
ML
727int
728LCL_CanSystemLeap(void)
729{
730 return drv_set_leap ? 1 : 0;
731}
732
733/* ================================================== */
734
8f9c2370 735void
a768578a 736LCL_SetSystemLeap(int leap, int tai_offset)
8f9c2370
ML
737{
738 if (drv_set_leap) {
a768578a 739 (drv_set_leap)(leap, tai_offset);
8f9c2370 740 }
8f9c2370
ML
741}
742
743/* ================================================== */
c386d117 744
1a7415a6 745double
c386d117
ML
746LCL_SetTempComp(double comp)
747{
1a7415a6
ML
748 double uncomp_freq_ppm;
749
c386d117 750 if (temp_comp_ppm == comp)
1a7415a6 751 return comp;
c386d117
ML
752
753 /* Undo previous compensation */
754 current_freq_ppm = (current_freq_ppm + temp_comp_ppm) /
755 (1.0 - 1.0e-6 * temp_comp_ppm);
756
1a7415a6
ML
757 uncomp_freq_ppm = current_freq_ppm;
758
c386d117
ML
759 /* Apply new compensation */
760 current_freq_ppm = current_freq_ppm * (1.0 - 1.0e-6 * comp) - comp;
761
c386d117 762 /* Call the system-specific driver for setting the frequency */
1a7415a6 763 current_freq_ppm = (*drv_set_freq)(current_freq_ppm);
c386d117 764
1a7415a6
ML
765 temp_comp_ppm = (uncomp_freq_ppm - current_freq_ppm) /
766 (1.0e-6 * uncomp_freq_ppm + 1.0);
767
768 return temp_comp_ppm;
c386d117
ML
769}
770
771/* ================================================== */
e14a03a1
ML
772
773void
774LCL_SetSyncStatus(int synchronised, double est_error, double max_error)
775{
776 if (drv_set_sync_status) {
777 (drv_set_sync_status)(synchronised, est_error, max_error);
778 }
779}
780
781/* ================================================== */