1/*	$NetBSD: precision.c,v 1.5 2020/05/25 20:47:37 christos Exp $	*/
2
3#include "ntp_unixtime.h"
4
5#include <stdio.h>
6
7#define	DEFAULT_SYS_PRECISION	-99
8
9int default_get_resolution();
10int default_get_precision();
11
12int
13main(
14	int argc,
15	char *argv[]
16	)
17{
18	printf("log2(resolution) = %d, log2(precision) = %d\n",
19	       default_get_resolution(),
20	       default_get_precision());
21	return 0;
22}
23
24/* Find the resolution of the system clock by watching how the current time
25 * changes as we read it repeatedly.
26 *
27 * struct timeval is only good to 1us, which may cause problems as machines
28 * get faster, but until then the logic goes:
29 *
30 * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
31 * probably use the "unused" low order bits as a counter (to force time to be
32 * a strictly increaing variable), incrementing it each time any process
33 * requests the time [[ or maybe time will stand still ? ]].
34 *
35 * SO: the logic goes:
36 *
37 *      IF      the difference from the last time is "small" (< MINSTEP)
38 *      THEN    this machine is "counting" with the low order bits
39 *      ELIF    this is not the first time round the loop
40 *      THEN    this machine *WAS* counting, and has now stepped
41 *      ELSE    this machine has resolution < time to read clock
42 *
43 * SO: if it exits on the first loop, assume "full accuracy" (1us)
44 *     otherwise, take the log2(observered difference, rounded UP)
45 *
46 * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
47 * and the first loop, it doesn't stop too early.
48 * Making it even greater allows MINSTEP to be reduced, assuming that the
49 * chance of MINSTEP-1 other processes getting in and calling gettimeofday
50 * between this processes's calls.
51 * Reducing MINSTEP may be necessary as this sets an upper bound for the time
52 * to actually call gettimeofday.
53 */
54
55#define	DUSECS	1000000
56#define	HUSECS	(1024 * 1024)
57#define	MINSTEP	5	/* some systems increment uS on each call */
58/* Don't use "1" as some *other* process may read too*/
59/*We assume no system actually *ANSWERS* in this time*/
60#define MAXSTEP 20000   /* maximum clock increment (us) */
61#define MINLOOPS 5      /* minimum number of step samples */
62#define	MAXLOOPS HUSECS	/* Assume precision < .1s ! */
63
64int
65default_get_resolution(void)
66{
67	struct timeval tp;
68	struct timezone tzp;
69	long last;
70	int i;
71	long diff;
72	long val;
73	int minsteps = MINLOOPS;	/* need at least this many steps */
74
75	gettimeofday(&tp, &tzp);
76	last = tp.tv_usec;
77	for (i = - --minsteps; i< MAXLOOPS; i++) {
78		gettimeofday(&tp, &tzp);
79		diff = tp.tv_usec - last;
80		if (diff < 0) diff += DUSECS;
81		if (diff > MINSTEP) if (minsteps-- <= 0) break;
82		last = tp.tv_usec;
83	}
84
85	printf("resolution = %ld usec after %d loop%s\n",
86	       diff, i, (i==1) ? "" : "s");
87
88	diff = (diff *3)/2;
89	if (i >= MAXLOOPS) {
90		printf(
91			"     (Boy this machine is fast ! %d loops without a step)\n",
92			MAXLOOPS);
93		diff = 1; /* No STEP, so FAST machine */
94	}
95	if (i == 0) {
96		printf(
97			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
98		diff = 1; /* time to read clock >= resolution */
99	}
100	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
101	printf("     (Oh dear -- that wasn't expected ! I'll guess !)\n");
102	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
103}
104
105/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
106
107/*
108 * This routine calculates the differences between successive calls to
109 * gettimeofday(). If a difference is less than zero, the us field
110 * has rolled over to the next second, so we add a second in us. If
111 * the difference is greater than zero and less than MINSTEP, the
112 * clock has been advanced by a small amount to avoid standing still.
113 * If the clock has advanced by a greater amount, then a timer interrupt
114 * has occurred and this amount represents the precision of the clock.
115 * In order to guard against spurious values, which could occur if we
116 * happen to hit a fat interrupt, we do this for MINLOOPS times and
117 * keep the minimum value obtained.
118 */
119int
120default_get_precision(void)
121{
122	struct timeval tp;
123	struct timezone tzp;
124#ifdef HAVE_GETCLOCK
125	struct timespec ts;
126#endif
127	long last;
128	int i;
129	long diff;
130	long val;
131	long usec;
132
133	usec = 0;
134	val = MAXSTEP;
135#ifdef HAVE_GETCLOCK
136	(void) getclock(TIMEOFDAY, &ts);
137	tp.tv_sec = ts.tv_sec;
138	tp.tv_usec = ts.tv_nsec / 1000;
139#else /*  not HAVE_GETCLOCK */
140	GETTIMEOFDAY(&tp, &tzp);
141#endif /* not HAVE_GETCLOCK */
142	last = tp.tv_usec;
143	for (i = 0; i < MINLOOPS && usec < HUSECS;) {
144#ifdef HAVE_GETCLOCK
145		(void) getclock(TIMEOFDAY, &ts);
146		tp.tv_sec = ts.tv_sec;
147		tp.tv_usec = ts.tv_nsec / 1000;
148#else /*  not HAVE_GETCLOCK */
149		GETTIMEOFDAY(&tp, &tzp);
150#endif /* not HAVE_GETCLOCK */
151		diff = tp.tv_usec - last;
152		last = tp.tv_usec;
153		if (diff < 0)
154		    diff += DUSECS;
155		usec += diff;
156		if (diff > MINSTEP) {
157			i++;
158			if (diff < val)
159			    val = diff;
160		}
161	}
162	printf("precision  = %ld usec after %d loop%s\n",
163	       val, i, (i == 1) ? "" : "s");
164	if (usec >= HUSECS) {
165		printf("     (Boy this machine is fast ! usec was %ld)\n",
166		       usec);
167		val = MINSTEP;	/* val <= MINSTEP; fast machine */
168	}
169	diff = HUSECS;
170	for (i = 0; diff > val; i--)
171	    diff >>= 1;
172	return (i);
173}
174