1/*	$NetBSD$	*/
2
3/*
4 * This program can be used to calibrate the clock reading jitter of a
5 * particular CPU and operating system. It first tickles every element
6 * of an array, in order to force pages into memory, then repeatedly
7 * reads the system clock and, finally, writes out the time values for
8 * later analysis. From this you can determine the jitter and if the
9 * clock ever runs backwards.
10 */
11
12#ifdef HAVE_CONFIG_H
13# include <config.h>
14#endif
15
16#include <stdio.h>
17#include <sys/time.h>
18#include <stdlib.h>
19#include "jitter.h"
20
21#define NBUF	800002
22#define FRAC	4294967296.		/* a bbbbillion */
23#define JAN_1970 2208988800UL		/* Unix base epoch */
24#define CLOCK_GETTIME			/* Solaris hires clock */
25
26int debug;
27char progname[10];
28double sys_residual;
29double average;
30void sys_gettime(l_fp *);
31
32int
33main(
34	int argc,
35	char *argv[]
36	)
37{
38	l_fp tr;
39	int i, j;
40	double dtemp, gtod[NBUF];
41
42	/*
43	 * Force pages into memory
44	 */
45	for (i = 0; i < NBUF; i ++)
46	    gtod[i] = 0;
47
48	/*
49	 * Construct gtod array
50	 */
51	for (i = 0; i < NBUF; i ++) {
52		get_systime(&tr);
53		LFPTOD(&tr, gtod[i]);
54	}
55
56	/*
57	 * Write out gtod array for later processing with Matlab
58	 */
59	average = 0;
60	for (i = 0; i < NBUF - 2; i++) {
61		gtod[i] = gtod[i + 1] - gtod[i];
62		printf("%13.9f\n", gtod[i]);
63		average += gtod[i];
64	}
65
66	/*
67	 * Sort the gtod array and display deciles
68	 */
69	for (i = 0; i < NBUF - 2; i++) {
70		for (j = 0; j <= i; j++) {
71			if (gtod[j] > gtod[i]) {
72				dtemp = gtod[j];
73				gtod[j] = gtod[i];
74				gtod[i] = dtemp;
75			}
76		}
77	}
78	average = average / (NBUF - 2);
79	fprintf(stderr, "Average %13.9f\n", average);
80	fprintf(stderr, "First rank\n");
81	for (i = 0; i < 10; i++)
82		fprintf(stderr, "%2d %13.9f\n", i, gtod[i]);
83	fprintf(stderr, "Last rank\n");
84	for (i = NBUF - 12; i < NBUF - 2; i++)
85		fprintf(stderr, "%2d %13.9f\n", i, gtod[i]);
86	exit(0);
87}
88
89
90/*
91 * get_systime - return system time in NTP timestamp format.
92 */
93void
94get_systime(
95	l_fp *now		/* system time */
96	)
97{
98	double dtemp;
99
100#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETCLOCK)
101	struct timespec ts;	/* seconds and nanoseconds */
102
103	/*
104	 * Convert Unix clock from seconds and nanoseconds to seconds.
105	 */
106# ifdef HAVE_CLOCK_GETTIME
107	clock_gettime(CLOCK_REALTIME, &ts);
108# else
109	getclock(TIMEOFDAY, &ts);
110# endif
111	now->l_i = ts.tv_sec + JAN_1970;
112	dtemp = ts.tv_nsec / 1e9;
113
114#else /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
115	struct timeval tv;	/* seconds and microseconds */
116
117	/*
118	 * Convert Unix clock from seconds and microseconds to seconds.
119	 */
120	gettimeofday(&tv, NULL);
121	now->l_i = tv.tv_sec + JAN_1970;
122	dtemp = tv.tv_usec / 1e6;
123
124#endif /* HAVE_CLOCK_GETTIME || HAVE_GETCLOCK */
125
126	/*
127	 * Renormalize to seconds past 1900 and fraction.
128	 */
129	dtemp += sys_residual;
130	if (dtemp >= 1) {
131		dtemp -= 1;
132		now->l_i++;
133	} else if (dtemp < -1) {
134		dtemp += 1;
135		now->l_i--;
136	}
137	dtemp *= FRAC;
138	now->l_uf = (u_int32)dtemp;
139}
140