fix #816 to add variety to System Status Word...
[ntpsec.git] / ntpfrob / precision.c
blob6b7a62299a8c7228f616c5663a801d108dea5ac1
1 /*
2 * Copyright the NTPsec project contributors
3 * SPDX-License-Identifier: BSD-2-Clause
4 */
6 #include "config.h"
8 #include <stdio.h>
9 #include <stdbool.h>
11 #include "ntp_types.h"
12 #include "ntp_calendar.h"
13 #include "ntpfrob.h"
15 #define DEFAULT_SYS_PRECISION -99
17 int default_get_resolution(void);
18 int default_get_precision(void);
20 void precision(const iomode mode)
22 if (mode == json) {
23 printf("{\"log2 of resolution\":%d, \"log2 of precision\":%d}\n",
24 default_get_resolution(),
25 default_get_precision());
26 } else {
27 printf("log2(resolution) = %d, log2(precision) = %d\n",
28 default_get_resolution(),
29 default_get_precision());
33 /* Find the resolution of the system clock by watching how the current time
34 * changes as we read it repeatedly.
36 * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
37 * probably use the "unused" low order bits as a counter (to force time to be
38 * a strictly increaing variable), incrementing it each time any process
39 * requests the time [[ or maybe time will stand still ? ]].
41 * SO: the logic goes:
43 * IF the difference from the last time is "small" (< MINSTEP)
44 * THEN this machine is "counting" with the low order bits
45 * ELIF this is not the first time round the loop
46 * THEN this machine *WAS* counting, and has now stepped
47 * ELSE this machine has resolution < time to read clock
49 * SO: if it exits on the first loop, assume "full accuracy" (1us)
50 * otherwise, take the log2(observed difference, rounded UP)
52 * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
53 * and the first loop, it doesn't stop too early.
54 * Making it even greater allows MINSTEP to be reduced, assuming that the
55 * chance of MINSTEP-1 other processes getting in and calling clock_gettime()
56 * between this processes's calls.
57 * Reducing MINSTEP may be necessary as this sets an upper bound for the time
58 * to actually call clock_gettime().
61 #define DNSECS 1000000000L
62 #define HUSECS (1024 * 1024)
63 #define MINSTEP 200 /* assume no system returns less than 200 nansec */
64 /* Don't use "1" as some *other* process may read too */
65 /* We assume no system actually *ANSWERS* in this time */
66 #define MAXSTEP 20000000 /* maximum clock increment (ns) */
67 #define MINLOOPS 5 /* minimum number of step samples */
68 #define MAXLOOPS (HUSECS * 1024) /* Assume precision < .1s ! */
70 int
71 default_get_resolution(void)
73 struct timespec tp = {0, 0};
74 long last;
75 int i;
76 long diff;
77 long val;
78 int minsteps = MINLOOPS; /* need at least this many steps */
80 clock_gettime(CLOCK_REALTIME, &tp);
81 last = tp.tv_nsec;
82 for (i = - --minsteps; i< MAXLOOPS; i++) {
83 clock_gettime(CLOCK_REALTIME, &tp);
84 diff = tp.tv_nsec - last;
85 if (diff < 0) { diff += DNSECS;
87 if (diff > MINSTEP && minsteps-- <= 0) {
88 break;
90 last = tp.tv_nsec;
92 diff /= 1000; /* step down to milliseconds */
94 fprintf(stderr, "resolution = %ld usec after %d loop%s\n",
95 diff, i, (i==1) ? "" : "s");
97 diff = (diff *3)/2;
98 if (i >= MAXLOOPS) {
99 fprintf(stderr,
100 " (Boy this machine is fast ! %d loops without a step)\n",
101 MAXLOOPS);
102 diff = 1; /* No STEP, so FAST machine */
104 if (i == 0) {
105 fprintf(stderr,
106 " (The resolution is less than the time to read the clock -- Assume 1us)\n");
107 diff = 1; /* time to read clock >= resolution */
109 for (i=0, val=HUSECS; val>0; i--, val >>= 1) { if (diff >= val) {
110 return i;
113 fprintf(stderr,
114 " (Oh dear -- that wasn't expected ! I'll guess !)\n");
115 return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
118 /* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
121 * This routine calculates the differences between successive calls to
122 * clock_gettime(REALTIME). If a difference is less than zero, the ns field
123 * has rolled over to the next second, so we add a second in ns. If
124 * the difference is greater than zero and less than MINSTEP, the
125 * clock has been advanced by a small amount to avoid standing still.
126 * If the clock has advanced by a greater amount, then a timer interrupt
127 * has occurred and this amount represents the precision of the clock.
128 * In order to guard against spurious values, which could occur if we
129 * happen to hit a fat interrupt, we do this for MINLOOPS times and
130 * keep the minimum value obtained.
133 default_get_precision(void)
135 struct timespec tp = {0, 0};
136 long last;
137 int i;
138 long diff;
139 long val;
140 long nsec;
142 nsec = 0;
143 val = MAXSTEP;
144 clock_gettime(CLOCK_REALTIME, &tp);
145 last = tp.tv_nsec;
146 for (i = 0; i < MINLOOPS && nsec < HUSECS * 1024;) {
147 clock_gettime(CLOCK_REALTIME, &tp);
148 diff = tp.tv_nsec - last;
149 last = tp.tv_nsec;
150 if (diff < 0) {
151 diff += DNSECS;
153 nsec += diff;
154 if (diff > MINSTEP) {
155 i++;
156 if (diff < val) {
157 val = diff;
161 val /= 1000; /* step down to milliseconds */
162 fprintf(stderr, "precision = %ld usec after %d loop%s\n",
163 val, i, (i == 1) ? "" : "s");
164 if (nsec >= HUSECS * 1024) {
165 fprintf(stderr, " (Boy this machine is fast! nsec was %ld)\n",
166 nsec);
167 val = MINSTEP; /* val <= MINSTEP; fast machine */
169 diff = HUSECS;
170 for (i = 0; diff > val; i--) {
171 diff >>= 1;
173 return (i);
176 /* end */