diff options
Diffstat (limited to 'ntpd/ntp_loopfilter.c')
-rw-r--r-- | ntpd/ntp_loopfilter.c | 723 |
1 files changed, 348 insertions, 375 deletions
diff --git a/ntpd/ntp_loopfilter.c b/ntpd/ntp_loopfilter.c index d0fa466c9eb3..3eb09e10060a 100644 --- a/ntpd/ntp_loopfilter.c +++ b/ntpd/ntp_loopfilter.c @@ -40,13 +40,15 @@ #define CLOCK_PHI 15e-6 /* max frequency error (s/s) */ #define CLOCK_PLL 16. /* PLL loop gain (log2) */ #define CLOCK_AVG 8. /* parameter averaging constant */ -#define CLOCK_FLL (NTP_MAXPOLL + CLOCK_AVG) /* FLL loop gain */ -#define CLOCK_ALLAN 1500. /* compromise Allan intercept (s) */ +#define CLOCK_FLL .25 /* FLL loop gain */ +#define CLOCK_ALLAN 11 /* Allan intercept (log2 s) */ #define CLOCK_DAY 86400. /* one day in seconds (s) */ #define CLOCK_JUNE (CLOCK_DAY * 30) /* June in seconds (s) */ #define CLOCK_LIMIT 30 /* poll-adjust threshold */ #define CLOCK_PGATE 4. /* poll-adjust gate */ #define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */ +#define FREQTOD(x) ((x) / 65536e6) /* NTP to double */ +#define DTOFREQ(x) ((int32)((x) * 65536e6)) /* double to NTP */ /* * Clock discipline state machine. This is used to control the @@ -54,32 +56,25 @@ * timewarp. * * State < step > step Comments - * ==================================================== - * NSET FREQ step, FREQ no ntp.drift + * ======================================================== + * NSET FREQ step, FREQ freq not set * - * FSET SYNC step, SYNC ntp.drift + * FSET SYNC step, SYNC freq set * - * FREQ if (mu < 900) if (mu < 900) set freq + * FREQ if (mu < 900) if (mu < 900) set freq direct * ignore ignore * else else * freq, SYNC freq, step, SYNC * - * SYNC SYNC if (mu < 900) adjust phase/freq - * ignore - * else - * SPIK + * SYNC SYNC SPIK, ignore adjust phase/freq * - * SPIK SYNC step, SYNC set phase + * SPIK SYNC if (mu < 900) adjust phase/freq + * ignore + * step, SYNC */ -#define S_NSET 0 /* clock never set */ -#define S_FSET 1 /* frequency set from the drift file */ -#define S_SPIK 2 /* spike detected */ -#define S_FREQ 3 /* frequency mode */ -#define S_SYNC 4 /* clock synchronized */ - /* * Kernel PLL/PPS state machine. This is used with the kernel PLL - * modifications described in the README.kernel file. + * modifications described in the documentation. * * If kernel support for the ntp_adjtime() system call is available, the * ntp_control flag is set. The ntp_enable and kern_enable flags can be @@ -97,54 +92,55 @@ * includes TAI offset and is identified by the symbol NTP_API with * value 4. * - * Each update to a prefer peer sets pps_stratum if it survives the - * intersection algorithm and its time is within range. The PPS time - * discipline is enabled (STA_PPSTIME bit set in the status word) when - * pps_stratum is true and the PPS frequency discipline is enabled. If - * the PPS time discipline is enabled and the kernel reports a PPS - * signal is present, the pps_control variable is set to the current - * time. If the current time is later than pps_control by PPS_MAXAGE - * (120 s), this variable is set to zero. + * Each PPS time/frequency discipline can be enabled by the atom driver + * or another driver. If enabled, the STA_PPSTIME and STA_FREQ bits are + * set in the kernel status word; otherwise, these bits are cleared. + * These bits are also cleard if the kernel reports an error. * * If an external clock is present, the clock driver sets STA_CLK in the * status word. When the local clock driver sees this bit, it updates * via this routine, which then calls ntp_adjtime() with the STA_PLL bit * set to zero, in which case the system clock is not adjusted. This is * also a signal for the external clock driver to discipline the system - * clock. + * clock. Unless specified otherwise, all times are in seconds. */ /* * Program variables that can be tinkered. */ -double clock_max = CLOCK_MAX; /* step threshold (s) */ -double clock_minstep = CLOCK_MINSTEP; /* stepout threshold (s) */ -double clock_panic = CLOCK_PANIC; /* panic threshold (s) */ +double clock_max = CLOCK_MAX; /* step threshold */ +double clock_minstep = CLOCK_MINSTEP; /* stepout threshold */ +double clock_panic = CLOCK_PANIC; /* panic threshold */ double clock_phi = CLOCK_PHI; /* dispersion rate (s/s) */ -double allan_xpt = CLOCK_ALLAN; /* Allan intercept (s) */ +u_char allan_xpt = CLOCK_ALLAN; /* Allan intercept (log2 s) */ /* * Program variables */ -static double clock_offset; /* offset (s) */ -double clock_jitter; /* offset jitter (s) */ +static double clock_offset; /* offset */ +double clock_jitter; /* offset jitter */ double drift_comp; /* frequency (s/s) */ double clock_stability; /* frequency stability (wander) (s/s) */ -u_long sys_clocktime; /* last system clock update */ -u_long pps_control; /* last pps update */ -u_long sys_tai; /* UTC offset from TAI (s) */ -static void rstclock P((int, u_long, double)); /* transition function */ +double clock_codec; /* audio codec frequency (samples/s) */ +static u_long clock_epoch; /* last update */ +u_int sys_tai; /* TAI offset from UTC */ +static void rstclock (int, double); /* transition function */ +static double direct_freq(double); /* direct set frequency */ +static void set_freq(double); /* set frequency */ #ifdef KERNEL_PLL -struct timex ntv; /* kernel API parameters */ -int pll_status; /* status bits for kernel pll */ +static struct timex ntv; /* ntp_adjtime() parameters */ +int pll_status; /* last kernel status bits */ +#if defined(STA_NANO) && NTP_API == 4 +static u_int loop_tai; /* last TAI offset */ +#endif /* STA_NANO */ #endif /* KERNEL_PLL */ /* * Clock state machine control flags */ -int ntp_enable; /* clock discipline enabled */ +int ntp_enable = 1; /* clock discipline enabled */ int pll_control; /* kernel support available */ -int kern_enable; /* kernel support enabled */ +int kern_enable = 1; /* kernel support enabled */ int pps_enable; /* kernel PPS discipline enabled */ int ext_enable; /* external clock enabled */ int pps_stratum; /* pps stratum */ @@ -155,9 +151,10 @@ int mode_ntpdate = FALSE; /* exit on first clock set */ * Clock state machine variables */ int state; /* clock discipline state */ -u_char sys_poll = NTP_MINDPOLL; /* time constant/poll (log2 s) */ +u_char sys_poll; /* time constant/poll (log2 s) */ int tc_counter; /* jiggle counter */ double last_offset; /* last offset (s) */ +static u_long last_step; /* last clock step */ /* * Huff-n'-puff filter variables @@ -172,7 +169,7 @@ static double sys_mindly; /* huff-n'-puff filter min delay */ #define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \ MOD_STATUS | MOD_TIMECONST) #ifdef SIGSYS -static void pll_trap P((int)); /* configuration trap */ +static void pll_trap (int); /* configuration trap */ static struct sigaction sigsys; /* current sigaction status */ static struct sigaction newsigsys; /* new sigaction status */ static sigjmp_buf env; /* environment var. for pll_trap() */ @@ -186,11 +183,9 @@ void init_loopfilter(void) { /* - * Initialize state variables. Initially, we expect no drift - * file, so set the state to S_NSET. If a drift file is present, - * it will be detected later and the state set to S_FSET. + * Initialize state variables. */ - rstclock(S_NSET, 0, 0); + sys_poll = ntp_minpoll; clock_jitter = LOGTOD(sys_precision); } @@ -204,7 +199,7 @@ init_loopfilter(void) * 2 clock was stepped * * LOCKCLOCK: The only thing this routine does is set the - * sys_rootdispersion variable equal to the peer dispersion. + * sys_rootdisp variable equal to the peer dispersion. */ int local_clock( @@ -213,29 +208,17 @@ local_clock( ) { int rval; /* return code */ - u_long mu; /* interval since last update (s) */ - double flladj; /* FLL frequency adjustment (ppm) */ - double plladj; /* PLL frequency adjustment (ppm) */ - double clock_frequency; /* clock frequency adjustment (ppm) */ + int osys_poll; /* old system poll */ + double mu; /* interval since last update */ + double clock_frequency; /* clock frequency */ double dtemp, etemp; /* double temps */ -#ifdef OPENSSL - u_int32 *tpt; - int i; - u_int len; - long togo; -#endif /* OPENSSL */ + char tbuf[80]; /* report buffer */ /* * If the loop is opened or the NIST LOCKCLOCK is in use, * monitor and record the offsets anyway in order to determine * the open-loop response and then go home. */ -#ifdef DEBUG - if (debug) - printf( - "local_clock: assocID %d offset %.9f freq %.3f state %d\n", - peer->associd, fp_offset, drift_comp * 1e6, state); -#endif #ifdef LOCKCLOCK return (0); @@ -257,33 +240,31 @@ local_clock( */ if (fabs(fp_offset) > clock_panic && clock_panic > 0 && !allow_panic) { - msyslog(LOG_ERR, - "time correction of %.0f seconds exceeds sanity limit (%.0f); set clock manually to the correct UTC time.", + snprintf(tbuf, sizeof(tbuf), + "%+.0f s; set clock manually within %.0f s.", fp_offset, clock_panic); + report_event(EVNT_SYSFAULT, NULL, tbuf); return (-1); } /* - * If simulating ntpdate, set the clock directly, rather than - * using the discipline. The clock_max defines the step - * threshold, above which the clock will be stepped instead of - * slewed. The value defaults to 128 ms, but can be set to even - * unreasonable values. If set to zero, the clock will never be - * stepped. Note that a slew will persist beyond the life of - * this program. - * - * Note that if ntpdate is active, the terminal does not detach, - * so the termination comments print directly to the console. + * This section simulates ntpdate. If the offset exceeds the + * step threshold (128 ms), step the clock to that time and + * exit. Othewise, slew the clock to that time and exit. Note + * that the slew will persist and eventually complete beyond the + * life of this program. Note that while ntpdate is active, the + * terminal does not detach, so the termination message prints + * directly to the terminal. */ if (mode_ntpdate) { if (fabs(fp_offset) > clock_max && clock_max > 0) { step_systime(fp_offset); - msyslog(LOG_NOTICE, "time reset %+.6f s", + msyslog(LOG_NOTICE, "ntpd: time set %+.6f s", fp_offset); printf("ntpd: time set %+.6fs\n", fp_offset); } else { adj_systime(fp_offset); - msyslog(LOG_NOTICE, "time slew %+.6f s", + msyslog(LOG_NOTICE, "ntpd: time slew %+.6f s", fp_offset); printf("ntpd: time slew %+.6fs\n", fp_offset); } @@ -299,12 +280,8 @@ local_clock( * is most effective if the delays are highly assymetric and * clockhopping is avoided and the clock frequency wander is * relatively small. - * - * Note either there is no prefer peer or this update is from - * the prefer peer. */ - if (sys_huffpuff != NULL && (sys_prefer == NULL || sys_prefer == - peer)) { + if (sys_huffpuff != NULL) { if (peer->delay < sys_huffpuff[sys_huffptr]) sys_huffpuff[sys_huffptr] = peer->delay; if (peer->delay < sys_mindly) @@ -323,100 +300,104 @@ local_clock( } /* - * Clock state machine transition function. This is where the - * action is and defines how the system reacts to large phase - * and frequency errors. There are two main regimes: when the - * offset exceeds the step threshold and when it does not. - * However, if the step threshold is set to zero, a step will - * never occur. See the instruction manual for the details how - * these actions interact with the command line options. + * Clock state machine transition function which defines how the + * system reacts to large phase and frequency excursion. There + * are two main regimes: when the offset exceeds the step + * threshold (128 ms) and when it does not. Under certain + * conditions updates are suspended until the stepout theshold + * (900 s) is exceeded. See the documentation on how these + * thresholds interact with commands and command line options. * - * Note the system poll is set to minpoll only if the clock is - * stepped. Note also the kernel is disabled if step is - * disabled or greater than 0.5 s. + * Note the kernel is disabled if step is disabled or greater + * than 0.5 s or in ntpdate mode. */ - clock_frequency = flladj = plladj = 0; - mu = peer->epoch - sys_clocktime; - if (clock_max == 0 || clock_max > 0.5) - kern_enable = 0; + osys_poll = sys_poll; + if (sys_poll < peer->minpoll) + sys_poll = peer->minpoll; + if (sys_poll > peer->maxpoll) + sys_poll = peer->maxpoll; + mu = current_time - clock_epoch; + clock_frequency = drift_comp; rval = 1; if (fabs(fp_offset) > clock_max && clock_max > 0) { switch (state) { /* - * In S_SYNC state we ignore the first outlyer amd - * switch to S_SPIK state. + * In SYNC state we ignore the first outlyer and switch + * to SPIK state. */ - case S_SYNC: - state = S_SPIK; + case EVNT_SYNC: + snprintf(tbuf, sizeof(tbuf), "%+.6f s", + fp_offset); + report_event(EVNT_SPIK, NULL, tbuf); + state = EVNT_SPIK; return (0); /* - * In S_FREQ state we ignore outlyers and inlyers. At - * the first outlyer after the stepout threshold, - * compute the apparent frequency correction and step - * the phase. + * In FREQ state we ignore outlyers and inlyers. At the + * first outlyer after the stepout threshold, compute + * the apparent frequency correction and step the phase. */ - case S_FREQ: + case EVNT_FREQ: if (mu < clock_minstep) return (0); - clock_frequency = (fp_offset - clock_offset) / - mu; + clock_frequency = direct_freq(fp_offset); /* fall through to S_SPIK */ /* - * In S_SPIK state we ignore succeeding outlyers until + * In SPIK state we ignore succeeding outlyers until * either an inlyer is found or the stepout threshold is * exceeded. */ - case S_SPIK: + case EVNT_SPIK: if (mu < clock_minstep) return (0); /* fall through to default */ /* - * We get here by default in S_NSET and S_FSET states - * and from above in S_FREQ or S_SPIK states. + * We get here by default in NSET and FSET states and + * from above in FREQ or SPIK states. * - * In S_NSET state an initial frequency correction is - * not available, usually because the frequency file has - * not yet been written. Since the time is outside the - * step threshold, the clock is stepped. The frequency - * will be set directly following the stepout interval. + * In NSET state an initial frequency correction is not + * available, usually because the frequency file has not + * yet been written. Since the time is outside the step + * threshold, the clock is stepped. The frequency will + * be set directly following the stepout interval. * - * In S_FSET state the initial frequency has been set - * from the frequency file. Since the time is outside - * the step threshold, the clock is stepped immediately, + * In FSET state the initial frequency has been set from + * the frequency file. Since the time is outside the + * step threshold, the clock is stepped immediately, * rather than after the stepout interval. Guys get - * nervous if it takes 17 minutes to set the clock for + * nervous if it takes 15 minutes to set the clock for * the first time. * - * In S_FREQ and S_SPIK states the stepout threshold has + * In FREQ and SPIK states the stepout threshold has * expired and the phase is still above the step * threshold. Note that a single spike greater than the - * step threshold is always suppressed, even at the - * longer poll intervals. + * step threshold is always suppressed, even with a + * long time constant. */ default: - step_systime(fp_offset); - msyslog(LOG_NOTICE, "time reset %+.6f s", + snprintf(tbuf, sizeof(tbuf), "%+.6f s", fp_offset); + report_event(EVNT_CLOCKRESET, NULL, tbuf); + step_systime(fp_offset); reinit_timer(); tc_counter = 0; - sys_poll = NTP_MINPOLL; - sys_tai = 0; clock_jitter = LOGTOD(sys_precision); rval = 2; - if (state == S_NSET) { - rstclock(S_FREQ, peer->epoch, 0); + if (state == EVNT_NSET || (current_time - + last_step) < clock_minstep * 2) { + rstclock(EVNT_FREQ, 0); return (rval); } + last_step = current_time; break; } - rstclock(S_SYNC, peer->epoch, 0); + rstclock(EVNT_SYNC, 0); } else { /* @@ -432,105 +413,76 @@ local_clock( switch (state) { /* - * In S_NSET state this is the first update received and + * In NSET state this is the first update received and * the frequency has not been initialized. Adjust the * phase, but do not adjust the frequency until after * the stepout threshold. */ - case S_NSET: - rstclock(S_FREQ, peer->epoch, fp_offset); + case EVNT_NSET: + rstclock(EVNT_FREQ, fp_offset); break; /* - * In S_FSET state this is the first update received and + * In FSET state this is the first update received and * the frequency has been initialized. Adjust the phase, * but do not adjust the frequency until the next * update. */ - case S_FSET: - rstclock(S_SYNC, peer->epoch, fp_offset); + case EVNT_FSET: + rstclock(EVNT_SYNC, fp_offset); break; /* - * In S_FREQ state ignore updates until the stepout - * threshold. After that, correct the phase and - * frequency and switch to S_SYNC state. + * In FREQ state ignore updates until the stepout + * threshold. After that, compute the new frequency, but + * do not adjust the phase or frequency until the next + * update. */ - case S_FREQ: + case EVNT_FREQ: if (mu < clock_minstep) return (0); - clock_frequency = (fp_offset - clock_offset) / - mu; - rstclock(S_SYNC, peer->epoch, fp_offset); + clock_frequency = direct_freq(fp_offset); + rstclock(EVNT_SYNC, 0); break; + /* - * We get here by default in S_SYNC and S_SPIK states. - * Here we compute the frequency update due to PLL and - * FLL contributions. + * We get here by default in SYNC and SPIK states. Here + * we compute the frequency update due to PLL and FLL + * contributions. */ default: allow_panic = FALSE; /* * The FLL and PLL frequency gain constants - * depend on the poll interval and Allan + * depend on the time constant and Allan * intercept. The PLL is always used, but - * becomes ineffective above the Allan - * intercept. The FLL is not used below one-half - * the Allan intercept. Above that the loop gain - * increases in steps to 1 / CLOCK_AVG. + * becomes ineffective above the Allan intercept + * where the FLL becomes effective. */ - if (ULOGTOD(sys_poll) > allan_xpt / 2) { - dtemp = CLOCK_FLL - sys_poll; - flladj = (fp_offset - clock_offset) / - (max(mu, allan_xpt) * dtemp); - } + if (sys_poll >= allan_xpt) + clock_frequency += (fp_offset - + clock_offset) / + max(ULOGTOD(sys_poll), mu) * + CLOCK_FLL; /* - * For the PLL the integration interval - * (numerator) is the minimum of the update - * interval and poll interval. This allows - * oversampling, but not undersampling. + * The PLL frequency gain (numerator) depends on + * the minimum of the update interval and Allan + * intercept. This reduces the PLL gain when the + * FLL becomes effective. */ - etemp = min(mu, (u_long)ULOGTOD(sys_poll)); + etemp = min(ULOGTOD(allan_xpt), mu); dtemp = 4 * CLOCK_PLL * ULOGTOD(sys_poll); - plladj = fp_offset * etemp / (dtemp * dtemp); - rstclock(S_SYNC, peer->epoch, fp_offset); + clock_frequency += fp_offset * etemp / (dtemp * + dtemp); + rstclock(EVNT_SYNC, fp_offset); break; } } -#ifdef OPENSSL - /* - * Scan the loopsecond table to determine the TAI offset. If - * there is a scheduled leap in future, set the leap warning, - * but only if less than 30 days before the leap. - */ - tpt = (u_int32 *)tai_leap.ptr; - len = ntohl(tai_leap.vallen) / sizeof(u_int32); - if (tpt != NULL) { - for (i = 0; i < len; i++) { - togo = (long)ntohl(tpt[i]) - - (long)peer->rec.l_ui; - if (togo > 0) { - if (togo < CLOCK_JUNE) - leap_next |= LEAP_ADDSECOND; - break; - } - } -#if defined(STA_NANO) && NTP_API == 4 - if (pll_control && kern_enable && sys_tai == 0) { - memset(&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_TAI; - ntv.constant = i + TAI_1972 - 1; - ntp_adjtime(&ntv); - } -#endif /* STA_NANO */ - sys_tai = i + TAI_1972 - 1; - } -#endif /* OPENSSL */ #ifdef KERNEL_PLL /* * This code segment works when clock adjustments are made using @@ -564,9 +516,6 @@ local_clock( if (ext_enable) { ntv.modes = MOD_STATUS; } else { - struct tm *tm = NULL; - time_t tstamp; - #ifdef STA_NANO ntv.modes = MOD_BITS | MOD_NANO; #else /* STA_NANO */ @@ -585,73 +534,41 @@ local_clock( dtemp); ntv.constant = sys_poll - 4; #endif /* STA_NANO */ - - /* - * The frequency is set directly only if - * clock_frequency is nonzero coming out of FREQ - * state. - */ - if (clock_frequency != 0) { - ntv.modes |= MOD_FREQUENCY; - ntv.freq = (int32)((clock_frequency + - drift_comp) * 65536e6); - } ntv.esterror = (u_int32)(clock_jitter * 1e6); ntv.maxerror = (u_int32)((sys_rootdelay / 2 + - sys_rootdispersion) * 1e6); + sys_rootdisp) * 1e6); ntv.status = STA_PLL; /* - * Set the leap bits in the status word, but - * only on the last day of June or December. - */ - tstamp = peer->rec.l_ui - JAN_1970; - tm = gmtime(&tstamp); - if (tm != NULL) { - if ((tm->tm_mon + 1 == 6 && - tm->tm_mday == 30) || (tm->tm_mon + - 1 == 12 && tm->tm_mday == 31)) { - if (leap_next & LEAP_ADDSECOND) - ntv.status |= STA_INS; - else if (leap_next & - LEAP_DELSECOND) - ntv.status |= STA_DEL; - } - } - - /* - * If the PPS signal is up and enabled, light - * the frequency bit. If the PPS driver is - * working, light the phase bit as well. If not, - * douse the lights, since somebody else may - * have left the switch on. + * Enable/disable the PPS if requested. */ - if (pps_enable && pll_status & STA_PPSSIGNAL) { - ntv.status |= STA_PPSFREQ; - if (pps_stratum < STRATUM_UNSPEC) - ntv.status |= STA_PPSTIME; + if (pps_enable) { + if (!(pll_status & STA_PPSTIME)) + report_event(EVNT_KERN, + NULL, "PPS enabled"); + ntv.status |= STA_PPSTIME | STA_PPSFREQ; } else { - ntv.status &= ~(STA_PPSFREQ | - STA_PPSTIME); + if (pll_status & STA_PPSTIME) + report_event(EVNT_KERN, + NULL, "PPS disabled"); + ntv.status &= ~(STA_PPSTIME | + STA_PPSFREQ); } + if (sys_leap == LEAP_ADDSECOND) + ntv.status |= STA_INS; + else if (sys_leap == LEAP_DELSECOND) + ntv.status |= STA_DEL; } /* * Pass the stuff to the kernel. If it squeals, turn off - * the pig. In any case, fetch the kernel offset and - * frequency and pretend we did it here. + * the pps. In any case, fetch the kernel offset, + * frequency and jitter. */ if (ntp_adjtime(&ntv) == TIME_ERROR) { - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, - "kernel time sync error %04x", ntv.status); - ntv.status &= ~(STA_PPSFREQ | STA_PPSTIME); - } else { - if ((ntv.status ^ pll_status) & ~STA_FLL) - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, - "kernel time sync status change %04x", - ntv.status); + if (!(ntv.status & STA_PPSSIGNAL)) + report_event(EVNT_KERN, NULL, + "PPS no signal"); } pll_status = ntv.status; #ifdef STA_NANO @@ -659,42 +576,39 @@ local_clock( #else /* STA_NANO */ clock_offset = ntv.offset / 1e6; #endif /* STA_NANO */ - clock_frequency = ntv.freq / 65536e6; - flladj = plladj = 0; + clock_frequency = FREQTOD(ntv.freq); /* * If the kernel PPS is lit, monitor its performance. */ if (ntv.status & STA_PPSTIME) { - pps_control = current_time; #ifdef STA_NANO clock_jitter = ntv.jitter / 1e9; #else /* STA_NANO */ clock_jitter = ntv.jitter / 1e6; #endif /* STA_NANO */ } - } else { -#endif /* KERNEL_PLL */ - + +#if defined(STA_NANO) && NTP_API == 4 /* - * We get here if the kernel discipline is not enabled. - * Adjust the clock frequency as the sum of the directly - * computed frequency (if measured) and the PLL and FLL - * increments. + * If the TAI changes, update the kernel TAI. */ - clock_frequency = drift_comp + clock_frequency + - flladj + plladj; -#ifdef KERNEL_PLL + if (loop_tai != sys_tai) { + loop_tai = sys_tai; + ntv.modes = MOD_TAI; + ntv.constant = sys_tai; + ntp_adjtime(&ntv); + } +#endif /* STA_NANO */ } #endif /* KERNEL_PLL */ /* * Clamp the frequency within the tolerance range and calculate - * the frequency change since the last update. + * the frequency difference since the last update. */ if (fabs(clock_frequency) > NTP_MAXFREQ) - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, + msyslog(LOG_NOTICE, "frequency error %.0f PPM exceeds tolerance %.0f PPM", clock_frequency * 1e6, NTP_MAXFREQ * 1e6); dtemp = SQUARE(clock_frequency - drift_comp); @@ -706,14 +620,16 @@ local_clock( drift_comp = clock_frequency; /* - * Calculate the wander as the exponentially weighted frequency - * differences. + * Calculate the wander as the exponentially weighted RMS + * frequency differences. Record the change for the frequency + * file update. */ etemp = SQUARE(clock_stability); clock_stability = SQRT(etemp + (dtemp - etemp) / CLOCK_AVG); + drift_file_sw = TRUE; /* - * Here we adjust the poll interval by comparing the current + * Here we adjust the timeconstan by comparing the current * offset with the clock jitter. If the offset is less than the * clock jitter times a constant, then the averaging interval is * increased, otherwise it is decreased. A bit of hysteresis @@ -740,6 +656,12 @@ local_clock( } /* + * If the time constant has changed, update the poll variables. + */ + if (osys_poll != sys_poll) + poll_update(peer, sys_poll); + + /* * Yibbidy, yibbbidy, yibbidy; that'h all folks. */ record_loop_stats(clock_offset, drift_comp, clock_jitter, @@ -747,9 +669,9 @@ local_clock( #ifdef DEBUG if (debug) printf( - "local_clock: mu %lu jitr %.6f freq %.3f stab %.6f poll %d count %d\n", - mu, clock_jitter, drift_comp * 1e6, - clock_stability * 1e6, sys_poll, tc_counter); + "local_clock: offset %.9f jit %.9f freq %.3f stab %.3f poll %d\n", + clock_offset, clock_jitter, drift_comp * 1e6, + clock_stability * 1e6, sys_poll); #endif /* DEBUG */ return (rval); #endif /* LOCKCLOCK */ @@ -760,7 +682,7 @@ local_clock( * adj_host_clock - Called once every second to update the local clock. * * LOCKCLOCK: The only thing this routine does is increment the - * sys_rootdispersion variable. + * sys_rootdisp variable. */ void adj_host_clock( @@ -774,12 +696,9 @@ adj_host_clock( * NTPv3, NTPv4 does not declare unsynchronized after one day, * since the dispersion check serves this function. Also, * since the poll interval can exceed one day, the old test - * would be counterproductive. Note we do this even with - * external clocks, since the clock driver will recompute the - * maximum error and the local clock driver will pick it up and - * pass to the common refclock routines. Very elegant. + * would be counterproductive. */ - sys_rootdispersion += clock_phi; + sys_rootdisp += clock_phi; #ifndef LOCKCLOCK /* @@ -791,25 +710,11 @@ adj_host_clock( return; /* - * Declare PPS kernel unsync if the pps signal has not been - * heard for a few minutes. - */ - if (pps_control && current_time - pps_control > PPS_MAXAGE) { - if (pps_control) - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, "pps sync disabled"); - pps_control = 0; - } - - /* * Implement the phase and frequency adjustments. The gain - * factor (denominator) is not allowed to increase beyond the - * Allan intercept. It doesn't make sense to average phase noise - * beyond this point and it helps to damp residual offset at the - * longer poll intervals. - */ - adjustment = clock_offset / (CLOCK_PLL * min(ULOGTOD(sys_poll), - allan_xpt)); + * factor (denominator) increases with poll interval, so is + * dominated by the FLL above the Allan intercept. + */ + adjustment = clock_offset / (CLOCK_PLL * ULOGTOD(sys_poll)); clock_offset -= adjustment; adj_systime(adjustment + drift_comp); #endif /* LOCKCLOCK */ @@ -817,29 +722,104 @@ adj_host_clock( /* - * Clock state machine. Enter new state and set state variables. Note we - * use the time of the last clock filter sample, which may be earlier - * than the current time. + * Clock state machine. Enter new state and set state variables. */ static void rstclock( int trans, /* new state */ - u_long update, /* new update time */ double offset /* new offset */ ) { #ifdef DEBUG - if (debug) - printf("local_clock: time %lu offset %.6f freq %.3f state %d\n", - update, offset, drift_comp * 1e6, trans); + if (debug > 1) + printf("local_clock: mu %lu state %d poll %d count %d\n", + current_time - clock_epoch, trans, sys_poll, + tc_counter); #endif + if (trans != state && trans != EVNT_FSET) + report_event(trans, NULL, NULL); state = trans; - sys_clocktime = update; last_offset = clock_offset = offset; + clock_epoch = current_time; +} + +/* + * calc_freq - calculate frequency directly + * + * This is very carefully done. When the offset is first computed at the + * first update, a residual frequency component results. Subsequently, + * updates are suppresed until the end of the measurement interval while + * the offset is amortized. At the end of the interval the frequency is + * calculated from the current offset, residual offset, length of the + * interval and residual frequency component. At the same time the + * frequenchy file is armed for update at the next hourly stats. + */ +static double +direct_freq( + double fp_offset + ) +{ + +#ifdef KERNEL_PLL + /* + * If the kernel is enabled, we need the residual offset to + * calculate the frequency correction. + */ + if (pll_control && kern_enable) { + memset(&ntv, 0, sizeof(ntv)); + ntp_adjtime(&ntv); +#ifdef STA_NANO + clock_offset = ntv.offset / 1e9; +#else /* STA_NANO */ + clock_offset = ntv.offset / 1e6; +#endif /* STA_NANO */ + drift_comp = FREQTOD(ntv.freq); + } +#endif /* KERNEL_PLL */ + set_freq((fp_offset - clock_offset) / (current_time - + clock_epoch) + drift_comp); + wander_resid = 0; + return (drift_comp); } /* + * set_freq - set clock frequency + */ +static void +set_freq( + double freq /* frequency update */ + ) +{ + char tbuf[80]; + + drift_comp = freq; + +#ifdef KERNEL_PLL + /* + * If the kernel is enabled, update the kernel frequency. + */ + if (pll_control && kern_enable) { + memset(&ntv, 0, sizeof(ntv)); + ntv.modes = MOD_FREQUENCY; + ntv.freq = DTOFREQ(drift_comp); + ntp_adjtime(&ntv); + snprintf(tbuf, sizeof(tbuf), "kernel %.3f PPM", + drift_comp * 1e6); + report_event(EVNT_FSET, NULL, tbuf); + } else { + snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", + drift_comp * 1e6); + report_event(EVNT_FSET, NULL, tbuf); + } +#else /* KERNEL_PLL */ + snprintf(tbuf, sizeof(tbuf), "ntpd %.3f PPM", drift_comp * + 1e6); + report_event(EVNT_FSET, NULL, tbuf); +#endif /* KERNEL_PLL */ +} + +/* * huff-n'-puff filter */ void @@ -867,39 +847,36 @@ huffpuff() */ void loop_config( - int item, - double freq + int item, + double freq ) { int i; +#ifdef DEBUG + if (debug > 1) + printf("loop_config: item %d freq %f\n", item, freq); +#endif switch (item) { + /* + * We first assume the kernel supports the ntp_adjtime() + * syscall. If that syscall works, initialize the kernel time + * variables. Otherwise, continue leaving no harm behind. + */ case LOOP_DRIFTINIT: - #ifndef LOCKCLOCK #ifdef KERNEL_PLL - /* - * Assume the kernel supports the ntp_adjtime() syscall. - * If that syscall works, initialize the kernel time - * variables. Otherwise, continue leaving no harm - * behind. While at it, ask to set nanosecond mode. If - * the kernel agrees, rejoice; othewise, it does only - * microseconds. - */ if (mode_ntpdate) break; pll_control = 1; memset(&ntv, 0, sizeof(ntv)); -#ifdef STA_NANO - ntv.modes = MOD_BITS | MOD_NANO; -#else /* STA_NANO */ ntv.modes = MOD_BITS; -#endif /* STA_NANO */ + ntv.status = STA_PLL; ntv.maxerror = MAXDISPERSE; ntv.esterror = MAXDISPERSE; - ntv.status = STA_UNSYNC; + ntv.constant = sys_poll; #ifdef SIGSYS /* * Use sigsetjmp() to save state and then call @@ -935,91 +912,73 @@ loop_config( if (pll_status & STA_CLK) ext_enable = 1; #endif /* STA_NANO */ - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_INFO, - "kernel time sync status %04x", - pll_status); + report_event(EVNT_KERN, NULL, + "kernel time sync enabled"); } #endif /* KERNEL_PLL */ #endif /* LOCKCLOCK */ break; + /* + * Initialize the frequency. If the frequency file is missing or + * broken, set the initial frequency to zero and set the state + * to NSET. Otherwise, set the initial frequency to the given + * value and the state to FSET. + */ case LOOP_DRIFTCOMP: - #ifndef LOCKCLOCK - /* - * If the frequency value is reasonable, set the initial - * frequency to the given value and the state to S_FSET. - * Otherwise, the drift file may be missing or broken, - * so set the frequency to zero. This erases past - * history should somebody break something. - */ - if (freq <= NTP_MAXFREQ && freq >= -NTP_MAXFREQ) { - drift_comp = freq; - rstclock(S_FSET, 0, 0); + if (freq > NTP_MAXFREQ || freq < -NTP_MAXFREQ) { + set_freq(0); + rstclock(EVNT_NSET, 0); } else { - drift_comp = 0; + set_freq(freq); + rstclock(EVNT_FSET, 0); } - -#ifdef KERNEL_PLL - /* - * Sanity check. If the kernel is available, load the - * frequency and light up the loop. Make sure the offset - * is zero to cancel any previous nonsense. If you don't - * want this initialization, remove the ntp.drift file. - */ - if (pll_control && kern_enable) { - memset((char *)&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_OFFSET | MOD_FREQUENCY; - ntv.freq = (int32)(drift_comp * 65536e6); - ntp_adjtime(&ntv); - } -#endif /* KERNEL_PLL */ #endif /* LOCKCLOCK */ break; + /* + * Disable the kernel at shutdown. The microkernel just abandons + * ship. The nanokernel carefully cleans up so applications can + * see this. Note the last programmed offset and frequency are + * left in place. + */ case LOOP_KERN_CLEAR: #ifndef LOCKCLOCK #ifdef KERNEL_PLL - /* Completely turn off the kernel time adjustments. */ - if (pll_control) { + if (pll_control && kern_enable) { memset((char *)&ntv, 0, sizeof(ntv)); - ntv.modes = MOD_BITS | MOD_OFFSET | MOD_FREQUENCY; + ntv.modes = MOD_STATUS; ntv.status = STA_UNSYNC; ntp_adjtime(&ntv); - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_INFO, - "kernel time sync disabled %04x", - ntv.status); + report_event(EVNT_KERN, NULL, + "kernel time sync disabledx"); } #endif /* KERNEL_PLL */ #endif /* LOCKCLOCK */ break; /* - * Special tinker variables for Ulrich Windl. Very dangerous. + * Tinker command variables for Ulrich Windl. Very dangerous. */ - case LOOP_MAX: /* step threshold */ - clock_max = freq; + case LOOP_ALLAN: /* Allan intercept (log2) (allan) */ + allan_xpt = (u_char)freq; break; - case LOOP_PANIC: /* panic threshold */ - clock_panic = freq; + case LOOP_CODEC: /* audio codec frequency (codec) */ + clock_codec = freq / 1e6; break; - - case LOOP_PHI: /* dispersion rate */ - clock_phi = freq; + + case LOOP_PHI: /* dispersion threshold (dispersion) */ + clock_phi = freq / 1e6; break; - case LOOP_MINSTEP: /* watchdog bark */ - clock_minstep = freq; + case LOOP_FREQ: /* initial frequency (freq) */ + set_freq(freq / 1e6); + rstclock(EVNT_FSET, 0); break; - case LOOP_ALLAN: /* Allan intercept */ - allan_xpt = freq; - break; - - case LOOP_HUFFPUFF: /* huff-n'-puff filter length */ + case LOOP_HUFFPUFF: /* huff-n'-puff length (huffpuff) */ if (freq < HUFFPUFF) freq = HUFFPUFF; sys_hufflen = (int)(freq / HUFFPUFF); @@ -1030,10 +989,24 @@ loop_config( sys_mindly = 1e9; break; - case LOOP_FREQ: /* initial frequency */ - drift_comp = freq / 1e6; - rstclock(S_FSET, 0, 0); + case LOOP_PANIC: /* panic threshold (panic) */ + clock_panic = freq; + break; + + case LOOP_MAX: /* step threshold (step) */ + clock_max = freq; + if (clock_max == 0 || clock_max > 0.5) + kern_enable = 0; break; + + case LOOP_MINSTEP: /* stepout threshold (stepout) */ + clock_minstep = freq; + break; + + case LOOP_LEAP: /* not used */ + default: + msyslog(LOG_NOTICE, + "loop_config: unsupported option %d", item); } } |