154359Sroberto/*
254359Sroberto * This program can be used to calibrate the clock reading jitter of a
354359Sroberto * particular CPU and operating system. It first tickles every element
4182007Sroberto * of an array, in order to force pages into memory, then repeatedly
5182007Sroberto * reads the system clock and, finally, writes out the time values for
6182007Sroberto * later analysis. From this you can determine the jitter and if the
7182007Sroberto * clock ever runs backwards.
854359Sroberto */
982498Sroberto
10182007Sroberto#ifdef HAVE_CONFIG_H
11182007Sroberto# include <config.h>
12182007Sroberto#endif
13182007Sroberto
1482498Sroberto#include <stdio.h>
1554359Sroberto#include <sys/time.h>
16182007Sroberto#include <stdlib.h>
17282408Scy#include "ntp_fp.h"
1854359Sroberto
19280849Scy#define NBUF	800002
20182007Sroberto#define JAN_1970 2208988800UL		/* Unix base epoch */
21182007Sroberto#define CLOCK_GETTIME			/* Solaris hires clock */
2254359Sroberto
23182007Srobertochar progname[10];
24182007Srobertodouble sys_residual;
25182007Srobertodouble average;
26182007Srobertovoid sys_gettime(l_fp *);
27182007Sroberto
2854359Srobertoint
2954359Srobertomain(
3054359Sroberto	int argc,
3154359Sroberto	char *argv[]
3254359Sroberto	)
3354359Sroberto{
34182007Sroberto	l_fp tr;
35182007Sroberto	int i, j;
36182007Sroberto	double dtemp, gtod[NBUF];
3754359Sroberto
3854359Sroberto	/*
3954359Sroberto	 * Force pages into memory
4054359Sroberto	 */
4154359Sroberto	for (i = 0; i < NBUF; i ++)
4254359Sroberto	    gtod[i] = 0;
4354359Sroberto
4454359Sroberto	/*
4554359Sroberto	 * Construct gtod array
4654359Sroberto	 */
4754359Sroberto	for (i = 0; i < NBUF; i ++) {
48182007Sroberto		get_systime(&tr);
49182007Sroberto		LFPTOD(&tr, gtod[i]);
5054359Sroberto	}
5154359Sroberto
5254359Sroberto	/*
53182007Sroberto	 * Write out gtod array for later processing with Matlab
5454359Sroberto	 */
55182007Sroberto	average = 0;
5654359Sroberto	for (i = 0; i < NBUF - 2; i++) {
5754359Sroberto		gtod[i] = gtod[i + 1] - gtod[i];
58182007Sroberto		printf("%13.9f\n", gtod[i]);
59182007Sroberto		average += gtod[i];
6054359Sroberto	}
6154359Sroberto
6254359Sroberto	/*
6354359Sroberto	 * Sort the gtod array and display deciles
6454359Sroberto	 */
6554359Sroberto	for (i = 0; i < NBUF - 2; i++) {
6654359Sroberto		for (j = 0; j <= i; j++) {
6754359Sroberto			if (gtod[j] > gtod[i]) {
68182007Sroberto				dtemp = gtod[j];
6954359Sroberto				gtod[j] = gtod[i];
70182007Sroberto				gtod[i] = dtemp;
7154359Sroberto			}
7254359Sroberto		}
7354359Sroberto	}
74182007Sroberto	average = average / (NBUF - 2);
75182007Sroberto	fprintf(stderr, "Average %13.9f\n", average);
7654359Sroberto	fprintf(stderr, "First rank\n");
7754359Sroberto	for (i = 0; i < 10; i++)
78182007Sroberto		fprintf(stderr, "%2d %13.9f\n", i, gtod[i]);
7954359Sroberto	fprintf(stderr, "Last rank\n");
8054359Sroberto	for (i = NBUF - 12; i < NBUF - 2; i++)
81182007Sroberto		fprintf(stderr, "%2d %13.9f\n", i, gtod[i]);
8254359Sroberto	exit(0);
8354359Sroberto}
84182007Sroberto
85182007Sroberto
86182007Sroberto/*
87182007Sroberto * get_systime - return system time in NTP timestamp format.
88182007Sroberto */
89182007Srobertovoid
90182007Srobertoget_systime(
91182007Sroberto	l_fp *now		/* system time */
92182007Sroberto	)
93182007Sroberto{
94182007Sroberto	double dtemp;
95182007Sroberto
96182007Sroberto#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
97182007Sroberto	struct timespec ts;	/* seconds and nanoseconds */
98182007Sroberto
99182007Sroberto	/*
100182007Sroberto	 * Convert Unix clock from seconds and nanoseconds to seconds.
101182007Sroberto	 */
102182007Sroberto# ifdef HAVE_CLOCK_GETTIME
103182007Sroberto	clock_gettime(CLOCK_REALTIME, &ts);
104182007Sroberto# else
105182007Sroberto	getclock(TIMEOFDAY, &ts);
106182007Sroberto# endif
107182007Sroberto	now->l_i = ts.tv_sec + JAN_1970;
108182007Sroberto	dtemp = ts.tv_nsec / 1e9;
109182007Sroberto
110182007Sroberto#else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
111182007Sroberto	struct timeval tv;	/* seconds and microseconds */
112182007Sroberto
113182007Sroberto	/*
114182007Sroberto	 * Convert Unix clock from seconds and microseconds to seconds.
115182007Sroberto	 */
116182007Sroberto	gettimeofday(&tv, NULL);
117182007Sroberto	now->l_i = tv.tv_sec + JAN_1970;
118182007Sroberto	dtemp = tv.tv_usec / 1e6;
119182007Sroberto
120182007Sroberto#endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
121182007Sroberto
122182007Sroberto	/*
123182007Sroberto	 * Renormalize to seconds past 1900 and fraction.
124182007Sroberto	 */
125182007Sroberto	dtemp += sys_residual;
126182007Sroberto	if (dtemp >= 1) {
127182007Sroberto		dtemp -= 1;
128182007Sroberto		now->l_i++;
129182007Sroberto	} else if (dtemp < -1) {
130182007Sroberto		dtemp += 1;
131182007Sroberto		now->l_i--;
132182007Sroberto	}
133182007Sroberto	dtemp *= FRAC;
134182007Sroberto	now->l_uf = (u_int32)dtemp;
135182007Sroberto}
136