Home | History | Annotate | Line # | Download | only in util
kern.c revision 1.1.1.2
      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 }
    226