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: */
33: void hardupdate();
34: void hardclock();
35: void second_overflow();
36:
37: /*
38: * Kernel variables
39: */
40: int tick; /* timer interrupt period (us) */
41: int fixtick; /* amortization constant (ppm) */
42: struct timeval timex; /* ripoff of kernel time variable */
43:
44: /*
45: * Phase-lock loop variables
46: */
47: int time_status = TIME_BAD; /* clock synchronization status */
48: long time_offset = 0; /* time adjustment (us) */
49: long time_constant = 0; /* pll time constant */
50: long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
51: long time_precision = 1000000 / HZ; /* clock precision (us) */
52: long time_maxerror = MAXPHASE; /* maximum error (us) */
53: long time_esterror = MAXPHASE; /* estimated error (us) */
54: long time_phase = 0; /* phase offset (scaled us) */
55: long time_freq = 0; /* frequency offset (scaled ppm) */
56: long time_adj = 0; /* tick adjust (scaled 1 / HZ) */
57: long time_reftime = 0; /* time at last adjustment (s) */
58:
59: /*
60: * Simulation variables
61: */
62: double timey = 0; /* simulation time (us) */
63: long timez = 0; /* current error (us) */
64: long poll_interval = 0; /* poll counter */
65:
66: /*
67: * Simulation test program
68: */
69: int
70: main(
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: */
120: void
121: hardupdate(
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: */
152: void
153: hardclock(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: */
181: void
182: second_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: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>