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