154359Sroberto#include "ntp_unixtime.h"
254359Sroberto
382498Sroberto#include <stdio.h>
482498Sroberto
554359Sroberto#define	DEFAULT_SYS_PRECISION	-99
654359Sroberto
754359Srobertoint default_get_resolution();
854359Srobertoint default_get_precision();
954359Sroberto
1054359Srobertoint
1154359Srobertomain(
1254359Sroberto	int argc,
1354359Sroberto	char *argv[]
1454359Sroberto	)
1554359Sroberto{
1654359Sroberto	printf("log2(resolution) = %d, log2(precision) = %d\n",
1754359Sroberto	       default_get_resolution(),
1854359Sroberto	       default_get_precision());
1954359Sroberto	return 0;
2054359Sroberto}
2154359Sroberto
2254359Sroberto/* Find the resolution of the system clock by watching how the current time
2354359Sroberto * changes as we read it repeatedly.
2454359Sroberto *
2554359Sroberto * struct timeval is only good to 1us, which may cause problems as machines
2654359Sroberto * get faster, but until then the logic goes:
2754359Sroberto *
2854359Sroberto * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
2954359Sroberto * probably use the "unused" low order bits as a counter (to force time to be
3054359Sroberto * a strictly increaing variable), incrementing it each time any process
3154359Sroberto * requests the time [[ or maybe time will stand still ? ]].
3254359Sroberto *
3354359Sroberto * SO: the logic goes:
3454359Sroberto *
3554359Sroberto *      IF      the difference from the last time is "small" (< MINSTEP)
3654359Sroberto *      THEN    this machine is "counting" with the low order bits
3754359Sroberto *      ELIF    this is not the first time round the loop
3854359Sroberto *      THEN    this machine *WAS* counting, and has now stepped
3954359Sroberto *      ELSE    this machine has resolution < time to read clock
4054359Sroberto *
4154359Sroberto * SO: if it exits on the first loop, assume "full accuracy" (1us)
4254359Sroberto *     otherwise, take the log2(observered difference, rounded UP)
4354359Sroberto *
4454359Sroberto * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
4554359Sroberto * and the first loop, it doesn't stop too early.
4654359Sroberto * Making it even greater allows MINSTEP to be reduced, assuming that the
4754359Sroberto * chance of MINSTEP-1 other processes getting in and calling gettimeofday
4854359Sroberto * between this processes's calls.
4954359Sroberto * Reducing MINSTEP may be necessary as this sets an upper bound for the time
5054359Sroberto * to actually call gettimeofday.
5154359Sroberto */
5254359Sroberto
5354359Sroberto#define	DUSECS	1000000
5454359Sroberto#define	HUSECS	(1024 * 1024)
5554359Sroberto#define	MINSTEP	5	/* some systems increment uS on each call */
5654359Sroberto/* Don't use "1" as some *other* process may read too*/
5754359Sroberto/*We assume no system actually *ANSWERS* in this time*/
5854359Sroberto#define MAXSTEP 20000   /* maximum clock increment (us) */
5954359Sroberto#define MINLOOPS 5      /* minimum number of step samples */
6054359Sroberto#define	MAXLOOPS HUSECS	/* Assume precision < .1s ! */
6154359Sroberto
6254359Srobertoint
6354359Srobertodefault_get_resolution(void)
6454359Sroberto{
6554359Sroberto	struct timeval tp;
6654359Sroberto	struct timezone tzp;
6754359Sroberto	long last;
6854359Sroberto	int i;
6954359Sroberto	long diff;
7054359Sroberto	long val;
7154359Sroberto	int minsteps = MINLOOPS;	/* need at least this many steps */
7254359Sroberto
7354359Sroberto	gettimeofday(&tp, &tzp);
7454359Sroberto	last = tp.tv_usec;
7554359Sroberto	for (i = - --minsteps; i< MAXLOOPS; i++) {
7654359Sroberto		gettimeofday(&tp, &tzp);
7754359Sroberto		diff = tp.tv_usec - last;
7854359Sroberto		if (diff < 0) diff += DUSECS;
7954359Sroberto		if (diff > MINSTEP) if (minsteps-- <= 0) break;
8054359Sroberto		last = tp.tv_usec;
8154359Sroberto	}
8254359Sroberto
8354359Sroberto	printf("resolution = %ld usec after %d loop%s\n",
8454359Sroberto	       diff, i, (i==1) ? "" : "s");
8554359Sroberto
8654359Sroberto	diff = (diff *3)/2;
8754359Sroberto	if (i >= MAXLOOPS) {
8854359Sroberto		printf(
8954359Sroberto			"     (Boy this machine is fast ! %d loops without a step)\n",
9054359Sroberto			MAXLOOPS);
9154359Sroberto		diff = 1; /* No STEP, so FAST machine */
9254359Sroberto	}
9354359Sroberto	if (i == 0) {
9454359Sroberto		printf(
9554359Sroberto			"     (The resolution is less than the time to read the clock -- Assume 1us)\n");
9654359Sroberto		diff = 1; /* time to read clock >= resolution */
9754359Sroberto	}
9854359Sroberto	for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
9954359Sroberto	printf("     (Oh dear -- that wasn't expected ! I'll guess !)\n");
10054359Sroberto	return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
10154359Sroberto}
10254359Sroberto
10354359Sroberto/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
10454359Sroberto
10554359Sroberto/*
10654359Sroberto * This routine calculates the differences between successive calls to
10754359Sroberto * gettimeofday(). If a difference is less than zero, the us field
10854359Sroberto * has rolled over to the next second, so we add a second in us. If
10954359Sroberto * the difference is greater than zero and less than MINSTEP, the
11054359Sroberto * clock has been advanced by a small amount to avoid standing still.
11154359Sroberto * If the clock has advanced by a greater amount, then a timer interrupt
11254359Sroberto * has occurred and this amount represents the precision of the clock.
11354359Sroberto * In order to guard against spurious values, which could occur if we
11454359Sroberto * happen to hit a fat interrupt, we do this for MINLOOPS times and
11554359Sroberto * keep the minimum value obtained.
11654359Sroberto */
11754359Srobertoint
11854359Srobertodefault_get_precision(void)
11954359Sroberto{
12054359Sroberto	struct timeval tp;
12154359Sroberto	struct timezone tzp;
12254359Sroberto#ifdef HAVE_GETCLOCK
12354359Sroberto	struct timespec ts;
12454359Sroberto#endif
12554359Sroberto	long last;
12654359Sroberto	int i;
12754359Sroberto	long diff;
12854359Sroberto	long val;
12954359Sroberto	long usec;
13054359Sroberto
13154359Sroberto	usec = 0;
13254359Sroberto	val = MAXSTEP;
13354359Sroberto#ifdef HAVE_GETCLOCK
13454359Sroberto	(void) getclock(TIMEOFDAY, &ts);
13554359Sroberto	tp.tv_sec = ts.tv_sec;
13654359Sroberto	tp.tv_usec = ts.tv_nsec / 1000;
13754359Sroberto#else /*  not HAVE_GETCLOCK */
13854359Sroberto	GETTIMEOFDAY(&tp, &tzp);
13954359Sroberto#endif /* not HAVE_GETCLOCK */
14054359Sroberto	last = tp.tv_usec;
14154359Sroberto	for (i = 0; i < MINLOOPS && usec < HUSECS;) {
14254359Sroberto#ifdef HAVE_GETCLOCK
14354359Sroberto		(void) getclock(TIMEOFDAY, &ts);
14454359Sroberto		tp.tv_sec = ts.tv_sec;
14554359Sroberto		tp.tv_usec = ts.tv_nsec / 1000;
14654359Sroberto#else /*  not HAVE_GETCLOCK */
14754359Sroberto		GETTIMEOFDAY(&tp, &tzp);
14854359Sroberto#endif /* not HAVE_GETCLOCK */
14954359Sroberto		diff = tp.tv_usec - last;
15054359Sroberto		last = tp.tv_usec;
15154359Sroberto		if (diff < 0)
15254359Sroberto		    diff += DUSECS;
15354359Sroberto		usec += diff;
15454359Sroberto		if (diff > MINSTEP) {
15554359Sroberto			i++;
15654359Sroberto			if (diff < val)
15754359Sroberto			    val = diff;
15854359Sroberto		}
15954359Sroberto	}
16054359Sroberto	printf("precision  = %ld usec after %d loop%s\n",
16154359Sroberto	       val, i, (i == 1) ? "" : "s");
16254359Sroberto	if (usec >= HUSECS) {
16354359Sroberto		printf("     (Boy this machine is fast ! usec was %ld)\n",
16454359Sroberto		       usec);
16554359Sroberto		val = MINSTEP;	/* val <= MINSTEP; fast machine */
16654359Sroberto	}
16754359Sroberto	diff = HUSECS;
16854359Sroberto	for (i = 0; diff > val; i--)
16954359Sroberto	    diff >>= 1;
17054359Sroberto	return (i);
17154359Sroberto}
172