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