aboutsummaryrefslogtreecommitdiff
path: root/ntpd/ntp_loopfilter.c
diff options
context:
space:
mode:
Diffstat (limited to 'ntpd/ntp_loopfilter.c')
-rw-r--r--ntpd/ntp_loopfilter.c723
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);
}
}