kern.c revision 290001
1/*
2 * This program simulates a first-order, type-II phase-lock loop using
3 * actual code segments from modified kernel distributions for SunOS,
4 * Ultrix and OSF/1 kernels. These segments do not use any licensed code.
5 */
6
7#ifdef HAVE_CONFIG_H
8# include <config.h>
9#endif
10
11#include <stdio.h>
12#include <ctype.h>
13#include <math.h>
14#include <sys/time.h>
15
16#ifdef HAVE_TIMEX_H
17# include "timex.h"
18#endif
19
20/*
21 * Phase-lock loop definitions
22 */
23#define HZ 100			/* timer interrupt frequency (Hz) */
24#define MAXPHASE 512000		/* max phase error (us) */
25#define MAXFREQ 200		/* max frequency error (ppm) */
26#define TAU 2			/* time constant (shift 0 - 6) */
27#define POLL 16			/* interval between updates (s) */
28#define MAXSEC 1200		/* max interval between updates (s) */
29
30/*
31 * Function declarations
32 */
33void hardupdate();
34void hardclock();
35void second_overflow();
36
37/*
38 * Kernel variables
39 */
40int tick;			/* timer interrupt period (us) */
41int fixtick;			/* amortization constant (ppm) */
42struct timeval timex;		/* ripoff of kernel time variable */
43
44/*
45 * Phase-lock loop variables
46 */
47int time_status = TIME_BAD;	/* clock synchronization status */
48long time_offset = 0;		/* time adjustment (us) */
49long time_constant = 0;		/* pll time constant */
50long time_tolerance = MAXFREQ;	/* frequency tolerance (ppm) */
51long time_precision = 1000000 / HZ; /* clock precision (us) */
52long time_maxerror = MAXPHASE;	/* maximum error (us) */
53long time_esterror = MAXPHASE;	/* estimated error (us) */
54long time_phase = 0;		/* phase offset (scaled us) */
55long time_freq = 0;		/* frequency offset (scaled ppm) */
56long time_adj = 0;		/* tick adjust (scaled 1 / HZ) */
57long time_reftime = 0;		/* time at last adjustment (s) */
58
59/*
60 * Simulation variables
61 */
62double timey = 0;		/* simulation time (us) */
63long timez = 0;			/* current error (us) */
64long poll_interval = 0;		/* poll counter */
65
66/*
67 * Simulation test program
68 */
69int
70main(
71	int argc,
72	char *argv[]
73	)
74{
75	tick = 1000000 / HZ;
76	fixtick = 1000000 % HZ;
77	timex.tv_sec = 0;
78	timex.tv_usec = MAXPHASE;
79	time_freq = 0;
80	time_constant = TAU;
81	printf("tick %d us, fixtick %d us\n", tick, fixtick);
82	printf("      time    offset      freq   _offset     _freq      _adj\n");
83
84	/*
85	 * Grind the loop until ^C
86	 */
87	while (1) {
88		timey += (double)(1000000) / HZ;
89		if (timey >= 1000000)
90		    timey -= 1000000;
91		hardclock();
92		if (timex.tv_usec >= 1000000) {
93			timex.tv_usec -= 1000000;
94			timex.tv_sec++;
95			second_overflow();
96			poll_interval++;
97			if (!(poll_interval % POLL)) {
98				timez = (long)timey - timex.tv_usec;
99				if (timez > 500000)
100				    timez -= 1000000;
101				if (timez < -500000)
102				    timez += 1000000;
103				hardupdate(timez);
104				printf("%10li%10li%10.2f  %08lx  %08lx  %08lx\n",
105				       timex.tv_sec, timez,
106				       (double)time_freq / (1 << SHIFT_KF),
107				       time_offset, time_freq, time_adj);
108			}
109		}
110	}
111}
112
113/*
114 * This routine simulates the ntp_adjtime() call
115 *
116 * For default SHIFT_UPDATE = 12, offset is limited to +-512 ms, the
117 * maximum interval between updates is 4096 s and the maximum frequency
118 * offset is +-31.25 ms/s.
119 */
120void
121hardupdate(
122	long offset
123	)
124{
125	long ltemp, mtemp;
126
127	time_offset = offset << SHIFT_UPDATE;
128	mtemp = timex.tv_sec - time_reftime;
129	time_reftime = timex.tv_sec;
130	if (mtemp > MAXSEC)
131	    mtemp = 0;
132
133	/* ugly multiply should be replaced */
134	if (offset < 0)
135	    time_freq -= (-offset * mtemp) >>
136		    (time_constant + time_constant);
137	else
138	    time_freq += (offset * mtemp) >>
139		    (time_constant + time_constant);
140	ltemp = time_tolerance << SHIFT_KF;
141	if (time_freq > ltemp)
142	    time_freq = ltemp;
143	else if (time_freq < -ltemp)
144	    time_freq = -ltemp;
145	if (time_status == TIME_BAD)
146	    time_status = TIME_OK;
147}
148
149/*
150 * This routine simulates the timer interrupt
151 */
152void
153hardclock(void)
154{
155	int ltemp, time_update;
156
157	time_update = tick;	/* computed by adjtime() */
158	time_phase += time_adj;
159	if (time_phase < -FINEUSEC) {
160		ltemp = -time_phase >> SHIFT_SCALE;
161		time_phase += ltemp << SHIFT_SCALE;
162		time_update -= ltemp;
163	}
164	else if (time_phase > FINEUSEC) {
165		ltemp = time_phase >> SHIFT_SCALE;
166		time_phase -= ltemp << SHIFT_SCALE;
167		time_update += ltemp;
168	}
169	timex.tv_usec += time_update;
170}
171
172/*
173 * This routine simulates the overflow of the microsecond field
174 *
175 * With SHIFT_SCALE = 23, the maximum frequency adjustment is +-256 us
176 * per tick, or 25.6 ms/s at a clock frequency of 100 Hz. The time
177 * contribution is shifted right a minimum of two bits, while the frequency
178 * contribution is a right shift. Thus, overflow is prevented if the
179 * frequency contribution is limited to half the maximum or 15.625 ms/s.
180 */
181void
182second_overflow(void)
183{
184	int ltemp;
185
186	time_maxerror += time_tolerance;
187	if (time_offset < 0) {
188		ltemp = -time_offset >>
189			(SHIFT_KG + time_constant);
190		time_offset += ltemp;
191		time_adj = -(ltemp <<
192			     (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE));
193	} else {
194		ltemp = time_offset >>
195			(SHIFT_KG + time_constant);
196		time_offset -= ltemp;
197		time_adj = ltemp <<
198			(SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
199	}
200	if (time_freq < 0)
201	    time_adj -= -time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
202	else
203	    time_adj += time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
204	time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ);
205
206	/* ugly divide should be replaced */
207	if (timex.tv_sec % 86400 == 0) {
208		switch (time_status) {
209
210		    case TIME_INS:
211			timex.tv_sec--; /* !! */
212			time_status = TIME_OOP;
213			break;
214
215		    case TIME_DEL:
216			timex.tv_sec++;
217			time_status = TIME_OK;
218			break;
219
220		    case TIME_OOP:
221			time_status = TIME_OK;
222			break;
223		}
224	}
225}
226