diff options
Diffstat (limited to 'ntpd/refclock_wwv.c')
-rw-r--r-- | ntpd/refclock_wwv.c | 301 |
1 files changed, 149 insertions, 152 deletions
diff --git a/ntpd/refclock_wwv.c b/ntpd/refclock_wwv.c index b7e0d9a6b1df..b03cfe128702 100644 --- a/ntpd/refclock_wwv.c +++ b/ntpd/refclock_wwv.c @@ -39,11 +39,12 @@ * tuned automatically using this program as propagation conditions * change throughout the weasons, both day and night. * - * The driver receives, demodulates and decodes the radio signals when - * connected to the audio codec of a workstation running Solaris, SunOS - * FreeBSD or Linux, and with a little help, other workstations with - * similar codecs or sound cards. In this implementation, only one audio - * driver and codec can be supported on a single machine. + * The driver requires an audio codec or sound card with sampling rate 8 + * kHz and mu-law companding. This is the same standard as used by the + * telephone industry and is supported by most hardware and operating + * systems, including Solaris, SunOS, FreeBSD, NetBSD and Linux. In this + * implementation, only one audio driver and codec can be supported on a + * single machine. * * The demodulation and decoding algorithms used in this driver are * based on those developed for the TAPR DSP93 development board and the @@ -68,6 +69,10 @@ * It does not seem useful to select the compact disc player port. Fudge * flag3 enables audio monitoring of the input signal. For this purpose, * the monitor gain is set to a default value. + * + * CEVNT_BADTIME invalid date or time + * CEVNT_PROP propagation failure - no stations heard + * CEVNT_TIMEOUT timeout (see newgame() below) */ /* * General definitions. These ordinarily do not need to be changed. @@ -92,6 +97,7 @@ #define TCKSIZ (TCKCYC * MS) /* tick filter size */ #define NCHAN 5 /* number of radio channels */ #define AUDIO_PHI 5e-6 /* dispersion growth factor */ +#define TBUF 128 /* max monitor line length */ /* * Tunable parameters. The DGAIN parameter can be changed to fit the @@ -102,12 +108,10 @@ * radio is not tunable, the DCHAN parameter can be changed to fit the * expected best propagation frequency: higher if further from the * transmitter, lower if nearer. The compromise value works for the US - * right coast. The FREQ_OFFSET parameter can be used as a frequency - * vernier to correct codec requency if greater than MAXFREQ. + * right coast. */ #define DCHAN 3 /* default radio channel (15 Mhz) */ #define DGAIN 5. /* subcarrier gain */ -#define FREQ_OFFSET 0. /* codec frequency correction (PPM) */ /* * General purpose status bits (status) @@ -133,6 +137,7 @@ #define FGATE 0x0010 /* frequency gate */ #define DGATE 0x0020 /* data pulse amplitude error */ #define BGATE 0x0040 /* data pulse width error */ +#define METRIC 0x0080 /* one or more stations heard */ #define LEPSEC 0x1000 /* leap minute */ /* @@ -150,10 +155,10 @@ * These bits indicate various alarm conditions, which are decoded to * form the quality character included in the timecode. */ -#define CMPERR 1 /* digit or misc bit compare error */ -#define LOWERR 2 /* low bit or digit amplitude or SNR */ -#define NINERR 4 /* less than nine digits in minute */ -#define SYNERR 8 /* not tracking second sync */ +#define CMPERR 0x1 /* digit or misc bit compare error */ +#define LOWERR 0x2 /* low bit or digit amplitude or SNR */ +#define NINERR 0x4 /* less than nine digits in minute */ +#define SYNERR 0x8 /* not tracking second sync */ /* * Watchcat timeouts (watch) @@ -225,15 +230,21 @@ #define SECWAR 0x40 /* 3 leap second warning */ /* - * The on-time synchronization point for the driver is the second epoch - * sync pulse produced by the FIR matched filters. As the 5-ms delay of - * these filters is compensated, the program delay is 1.1 ms due to the - * 600-Hz IIR bandpass filter. The measured receiver delay is 4.7 ms and - * the codec delay less than 0.2 ms. The additional propagation delay - * specific to each receiver location can be programmed in the fudge - * time1 and time2 values for WWV and WWVH, respectively. + * The on-time synchronization point is the positive-going zero crossing + * of the first cycle of the 5-ms second pulse. The IIR baseband filter + * phase delay is 0.91 ms, while the receiver delay is approximately 4.7 + * ms at 1000 Hz. The fudge value -0.45 ms due to the codec and other + * causes was determined by calibrating to a PPS signal from a GPS + * receiver. The additional propagation delay specific to each receiver + * location can be programmed in the fudge time1 and time2 values for + * WWV and WWVH, respectively. + * + * The resulting offsets with a 2.4-GHz P4 running FreeBSD 6.1 are + * generally within .02 ms short-term with .02 ms jitter. The long-term + * offsets vary up to 0.3 ms due to ionosperhic layer height variations. + * The processor load due to the driver is 5.8 percent. */ -#define PDELAY (.0011 + .0047 + .0002) /* net system delay (s) */ +#define PDELAY ((.91 + 4.7 - 0.45) / 1000) /* system delay (s) */ /* * Table of sine values at 4.5-degree increments. This is used by the @@ -365,7 +376,7 @@ struct progx progx[] = { }; /* - * BCD coefficients for maximum likelihood digit decode + * BCD coefficients for maximum-likelihood digit decode */ #define P15 1. /* max positive number */ #define N15 -1. /* max negative number */ @@ -447,14 +458,13 @@ char dstcod[] = { /* * The decoding matrix consists of nine row vectors, one for each digit * of the timecode. The digits are stored from least to most significant - * order. The maximum likelihood timecode is formed from the digits - * corresponding to the maximum likelihood values reading in the + * order. The maximum-likelihood timecode is formed from the digits + * corresponding to the maximum-likelihood values reading in the * opposite order: yy ddd hh:mm. */ struct decvec { int radix; /* radix (3, 4, 6, 10) */ int digit; /* current clock digit */ - int mldigit; /* maximum likelihood digit */ int count; /* match count */ double digprb; /* max digit probability */ double digsnr; /* likelihood function (dB) */ @@ -503,6 +513,7 @@ struct wwvunit { l_fp tick; /* audio sample increment */ double phase, freq; /* logical clock phase and frequency */ double monitor; /* audio monitor point */ + double pdelay; /* propagation delay (s) */ #ifdef ICOM int fd_icom; /* ICOM file descriptor */ #endif /* ICOM */ @@ -513,7 +524,7 @@ struct wwvunit { * Audio codec variables */ double comp[SIZE]; /* decompanding table */ - int port; /* codec port */ + int port; /* codec port */ int gain; /* codec gain */ int mongain; /* codec monitor gain */ int clipcnt; /* sample clipped count */ @@ -568,32 +579,32 @@ struct wwvunit { /* * Function prototypes */ -static int wwv_start P((int, struct peer *)); -static void wwv_shutdown P((int, struct peer *)); -static void wwv_receive P((struct recvbuf *)); -static void wwv_poll P((int, struct peer *)); +static int wwv_start (int, struct peer *); +static void wwv_shutdown (int, struct peer *); +static void wwv_receive (struct recvbuf *); +static void wwv_poll (int, struct peer *); /* * More function prototypes */ -static void wwv_epoch P((struct peer *)); -static void wwv_rf P((struct peer *, double)); -static void wwv_endpoc P((struct peer *, int)); -static void wwv_rsec P((struct peer *, double)); -static void wwv_qrz P((struct peer *, struct sync *, int)); -static void wwv_corr4 P((struct peer *, struct decvec *, - double [], double [][4])); -static void wwv_gain P((struct peer *)); -static void wwv_tsec P((struct peer *)); -static int timecode P((struct wwvunit *, char *)); -static double wwv_snr P((double, double)); -static int carry P((struct decvec *)); -static int wwv_newchan P((struct peer *)); -static void wwv_newgame P((struct peer *)); -static double wwv_metric P((struct sync *)); -static void wwv_clock P((struct peer *)); +static void wwv_epoch (struct peer *); +static void wwv_rf (struct peer *, double); +static void wwv_endpoc (struct peer *, int); +static void wwv_rsec (struct peer *, double); +static void wwv_qrz (struct peer *, struct sync *, int); +static void wwv_corr4 (struct peer *, struct decvec *, + double [], double [][4]); +static void wwv_gain (struct peer *); +static void wwv_tsec (struct peer *); +static int timecode (struct wwvunit *, char *); +static double wwv_snr (double, double); +static int carry (struct decvec *); +static int wwv_newchan (struct peer *); +static void wwv_newgame (struct peer *); +static double wwv_metric (struct sync *); +static void wwv_clock (struct peer *); #ifdef ICOM -static int wwv_qsy P((struct peer *, int)); +static int wwv_qsy (struct peer *, int); #endif /* ICOM */ static double qsy[NCHAN] = {2.5, 5, 10, 15, 20}; /* frequencies (MHz) */ @@ -705,7 +716,9 @@ wwv_start( /* * Initialize autotune if available. Note that the ICOM select * code must be less than 128, so the high order bit can be used - * to select the line speed 0 (9600 bps) or 1 (1200 bps). + * to select the line speed 0 (9600 bps) or 1 (1200 bps). Note + * we don't complain if the ICOM device is not there; but, if it + * is, the radio better be working. */ temp = 0; #ifdef DEBUG @@ -719,25 +732,14 @@ wwv_start( else up->fd_icom = icom_init("/dev/icom", B9600, temp); - if (up->fd_icom < 0) { - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, - "icom: %m"); - up->errflg = CEVNT_FAULT; - } } if (up->fd_icom > 0) { if (wwv_qsy(peer, DCHAN) != 0) { - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, - "icom: radio not found"); - up->errflg = CEVNT_FAULT; + msyslog(LOG_NOTICE, "icom: radio not found"); close(up->fd_icom); up->fd_icom = 0; } else { - NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_NOTICE, - "icom: autotune enabled"); + msyslog(LOG_NOTICE, "icom: autotune enabled"); } } #endif /* ICOM */ @@ -837,8 +839,7 @@ wwv_receive( * per second, which results in a frequency change of * 125 PPM. */ - up->phase += up->freq / SECOND; - up->phase += FREQ_OFFSET / 1e6; + up->phase += (up->freq + clock_codec) / SECOND; if (up->phase >= .5) { up->phase -= 1.; } else if (up->phase < -.5) { @@ -884,8 +885,6 @@ wwv_poll( pp = peer->procptr; up = (struct wwvunit *)pp->unitptr; - if (pp->coderecv == pp->codeproc) - up->errflg = CEVNT_TIMEOUT; if (up->errflg) refclock_report(peer, up->errflg); up->errflg = 0; @@ -974,7 +973,6 @@ wwv_rf( static int epopos; /* epoch second sync position buffer */ static int iniflg; /* initialization flag */ - int pdelay; /* propagation delay (samples) */ int epoch; /* comb filter index */ double dtemp; int i; @@ -1012,7 +1010,7 @@ wwv_rf( * compensate for the radio audio response at 100 Hz. * * Matlab IIR 4th-order IIR elliptic, 150 Hz lowpass, 0.2 dB - * passband ripple, -50 dB stopband ripple. + * passband ripple, -50 dB stopband ripple, phase delay 0.97 ms. */ data = (lpf[4] = lpf[3]) * 8.360961e-01; data += (lpf[3] = lpf[2]) * -3.481740e+00; @@ -1056,7 +1054,7 @@ wwv_rf( * tones and most of the noise and voice modulation components. * * Matlab 4th-order IIR elliptic, 800-1400 Hz bandpass, 0.2 dB - * passband ripple, -50 dB stopband ripple. + * passband ripple, -50 dB stopband ripple, phase delay 0.91 ms. */ syncx = (bpf[8] = bpf[7]) * 4.897278e-01; syncx += (bpf[7] = bpf[6]) * -2.765914e+00; @@ -1174,10 +1172,6 @@ wwv_rf( */ if (!wwv_newchan(peer)) up->watch = 0; -#ifdef ICOM - if (up->fd_icom > 0) - wwv_qsy(peer, up->dchan); -#endif /* ICOM */ } else { /* @@ -1212,7 +1206,8 @@ wwv_rf( wwv_epoch(peer); } else if (up->sptr != NULL) { sp = up->sptr; - if (sp->metric >= TTHR && epoch == sp->mepoch % SECOND) { + if (sp->metric >= TTHR && epoch == sp->mepoch % SECOND) + { up->rsec = (60 - sp->mepoch / SECOND) % 60; up->rphase = 0; up->status |= MSYNC; @@ -1232,18 +1227,14 @@ wwv_rf( * provides a resolution of one sample (125 us). The filters run * only if the station has been reliably determined. */ - if (up->status & SELV) { - pdelay = (int)(pp->fudgetime1 * SECOND); + if (up->status & SELV) mfsync = sqrt(csiamp * csiamp + csqamp * csqamp) / TCKCYC; - } else if (up->status & SELH) { - pdelay = (int)(pp->fudgetime2 * SECOND); + else if (up->status & SELH) mfsync = sqrt(hsiamp * hsiamp + hsqamp * hsqamp) / TCKCYC; - } else { - pdelay = 0; + else mfsync = 0; - } /* * Enhance the seconds sync pulse using a 1-s (8000-sample) comb @@ -1271,7 +1262,7 @@ wwv_rf( if (epoch == 0) { up->epomax = epomax; up->eposnr = wwv_snr(epomax, nxtmax); - epopos -= pdelay + TCKCYC * MS; + epopos -= TCKCYC * MS; if (epopos < 0) epopos += SECOND; wwv_endpoc(peer, epopos); @@ -1313,7 +1304,7 @@ wwv_qrz( { struct refclockproc *pp; struct wwvunit *up; - char tbuf[80]; /* monitor buffer */ + char tbuf[TBUF]; /* monitor buffer */ long epoch; pp = peer->procptr; @@ -1365,7 +1356,7 @@ wwv_qrz( sp->metric = wwv_metric(sp); if (pp->sloppyclockflag & CLK_FLAG4) { sprintf(tbuf, - "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %4ld %4ld", + "wwv8 %04x %3d %s %04x %.0f %.0f/%.1f %ld %ld", up->status, up->gain, sp->refid, sp->reach & 0xffff, sp->metric, sp->synmax, sp->synsnr, sp->pos % SECOND, epoch); @@ -1413,7 +1404,7 @@ wwv_endpoc( static int avgcnt; /* averaging interval counter */ static int avginc; /* averaging ratchet */ static int iniflg; /* initialization flag */ - char tbuf[80]; /* monitor buffer */ + char tbuf[TBUF]; /* monitor buffer */ double dtemp; int tmp2; @@ -1489,8 +1480,8 @@ wwv_endpoc( mepoch = xepoch; syncnt = 0; } - if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & MSYNC)) - { + if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & + MSYNC)) { sprintf(tbuf, "wwv1 %04x %3d %4d %5.0f %5.1f %5d %4d %4d %4d", up->status, up->gain, tepoch, up->epomax, @@ -1768,7 +1759,7 @@ wwv_rsec( struct wwvunit *up; struct chan *cp; struct sync *sp, *rp; - char tbuf[80]; /* monitor buffer */ + char tbuf[TBUF]; /* monitor buffer */ int sw, arg, nsec; pp = peer->procptr; @@ -1887,35 +1878,28 @@ wwv_rsec( up->errcnt = up->digcnt = up->alarm = 0; /* - * We now begin the minute scan. If not yet synchronized - * to a station, restart if the units digit has not been - * found within the DATA timeout (15 m) or if not - * synchronized within the SYNCH timeout (40 m). After - * synchronizing to a station, restart if no stations - * are found within the PANIC timeout (2 days). + * If synchronized to a station, restart if no stations + * have been heard within the PANIC timeout (2 days). If + * not and the minute digit has been found, restart if + * not synchronized withing the SYNCH timeout (40 m). If + * not, restart if the unit digit has not been found + * within the DATA timeout (15 m). */ if (up->status & INSYNC) { if (up->watch > PANIC) { wwv_newgame(peer); return; } - } else { - if (!(up->status & DSYNC)) { - if (up->watch > DATA) { - wwv_newgame(peer); - return; - } - } + } else if (up->status & DSYNC) { if (up->watch > SYNCH) { wwv_newgame(peer); return; } + } else if (up->watch > DATA) { + wwv_newgame(peer); + return; } wwv_newchan(peer); -#ifdef ICOM - if (up->fd_icom > 0) - wwv_qsy(peer, up->dchan); -#endif /* ICOM */ break; /* @@ -1987,7 +1971,7 @@ wwv_rsec( /* * Save the data channel gain, then QSY to the probe channel and - * dim the seconds comb filters. The newchan() routine will + * dim the seconds comb filters. The www_newchan() routine will * light them back up. */ case MSC21: /* 58 */ @@ -2107,7 +2091,7 @@ wwv_clock( pp->disp = 0; pp->lastref = up->timestamp; refclock_process_offset(pp, offset, - up->timestamp, PDELAY); + up->timestamp, PDELAY + up->pdelay); refclock_receive(peer); } } @@ -2122,12 +2106,12 @@ wwv_clock( /* - * wwv_corr4 - determine maximum likelihood digit + * wwv_corr4 - determine maximum-likelihood digit * * This routine correlates the received digit vector with the BCD * coefficient vectors corresponding to all valid digits at the given * position in the decoding matrix. The maximum value corresponds to the - * maximum likelihood digit, while the ratio of this value to the next + * maximum-likelihood digit, while the ratio of this value to the next * lower value determines the likelihood function. Note that, if the * digit is invalid, the likelihood vector is averaged toward a miss. */ @@ -2143,7 +2127,7 @@ wwv_corr4( struct wwvunit *up; double topmax, nxtmax; /* metrics */ double acc; /* accumulator */ - char tbuf[80]; /* monitor buffer */ + char tbuf[TBUF]; /* monitor buffer */ int mldigit; /* max likelihood digit */ int i, j; @@ -2176,9 +2160,9 @@ wwv_corr4( vp->digsnr = wwv_snr(topmax, nxtmax); /* - * The current maximum likelihood digit is compared to the last - * maximum likelihood digit. If different, the compare counter - * and maximum likelihood digit are reset. When the compare + * The current maximum-likelihood digit is compared to the last + * maximum-likelihood digit. If different, the compare counter + * and maximum-likelihood digit are reset. When the compare * counter reaches the BCMP threshold (3), the digit is assumed * correct. When the compare counter of all nine digits have * reached threshold, the clock is assumed correct. @@ -2188,26 +2172,23 @@ wwv_corr4( * not considered correct until all nine clock digits have * reached threshold. This is intended as eye candy, but avoids * mistakes when the signal is low and the SNR is very marginal. - * once correctly set, the maximum likelihood digit is ignored - * on the assumption the clock will always be correct unless for - * some reason it drifts to a different second. */ - vp->mldigit = mldigit; if (vp->digprb < BTHR || vp->digsnr < BSNR) { - vp->count = 0; up->status |= BGATE; } else { - up->status |= DSYNC; if (vp->digit != mldigit) { - vp->count = 0; up->alarm |= CMPERR; - if (!(up->status & INSYNC)) + if (vp->count > 0) + vp->count--; + if (vp->count == 0) vp->digit = mldigit; } else { if (vp->count < BCMP) vp->count++; - else + if (vp->count == BCMP) { + up->status |= DSYNC; up->digcnt++; + } } } if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & @@ -2215,7 +2196,7 @@ wwv_corr4( sprintf(tbuf, "wwv4 %2d %04x %3d %4d %5.0f %2d %d %d %d %5.0f %5.1f", up->rsec - 1, up->status, up->gain, up->yepoch, - up->epomax, vp->radix, vp->digit, vp->mldigit, + up->epomax, vp->radix, vp->digit, mldigit, vp->count, vp->digprb, vp->digsnr); record_clock_stats(&peer->srcadr, tbuf); #ifdef DEBUG @@ -2326,7 +2307,7 @@ wwv_tsec( * This routine rotates a likelihood vector one position and increments * the clock digit modulo the radix. It returns the new clock digit or * zero if a carry occurred. Once synchronized, the clock digit will - * match the maximum likelihood digit corresponding to that position. + * match the maximum-likelihood digit corresponding to that position. */ static int carry( @@ -2420,16 +2401,14 @@ wwv_newchan( struct wwvunit *up; struct sync *sp, *rp; double rank, dtemp; - int i, j; + int i, j, rval; pp = peer->procptr; up = (struct wwvunit *)pp->unitptr; /* * Search all five station pairs looking for the channel with - * maximum metric. If no station is found above thresholds, tune - * to WWV on 15 MHz, set the reference ID to NONE and wait for - * hotter ions. + * maximum metric. */ sp = NULL; j = 0; @@ -2453,39 +2432,57 @@ wwv_newchan( /* * If the strongest signal is less than the MTHR threshold (13), - * we are beneath the waves, so squelch the second sync. If the - * strongest signal is greater than the threshold, tune to that - * frequency and transmitter QTH. + * we are beneath the waves, so squelch the second sync and + * advance to the next station. This makes sure all stations are + * scanned when the ions grow dim. If the strongest signal is + * greater than the threshold, tune to that frequency and + * transmitter QTH. */ + up->status &= ~(SELV | SELH); if (rank < MTHR) { up->dchan = (up->dchan + 1) % NCHAN; - up->status &= ~(SELV | SELH); - return (FALSE); + if (up->status & METRIC) { + up->status &= ~METRIC; + refclock_report(peer, CEVNT_PROP); + } + rval = FALSE; + } else { + up->dchan = j; + up->sptr = sp; + memcpy(&pp->refid, sp->refid, 4); + peer->refid = pp->refid; + up->status |= METRIC; + if (sp->select & SELV) { + up->status |= SELV; + up->pdelay = pp->fudgetime1; + } else if (sp->select & SELH) { + up->status |= SELH; + up->pdelay = pp->fudgetime2; + } else { + up->pdelay = 0; + } + rval = TRUE; } - up->dchan = j; - up->status |= SELV | SELH; - up->sptr = sp; - memcpy(&pp->refid, sp->refid, 4); - peer->refid = pp->refid; - return (TRUE); +#ifdef ICOM + if (up->fd_icom > 0) + wwv_qsy(peer, up->dchan); +#endif /* ICOM */ + return (rval); } /* * wwv_newgame - reset and start over * - * There are four conditions resulting in a new game: + * There are three conditions resulting in a new game: * - * 1 During initial acquisition (MSYNC dark) going 6 minutes (ACQSN) - * without reliably finding the minute pulse (MSYNC lit). - * - * 2 After finding the minute pulse (MSYNC lit), going 15 minutes + * 1 After finding the minute pulse (MSYNC lit), going 15 minutes * (DATA) without finding the unit seconds digit. * - * 3 After finding good data (DATA lit), going more than 40 minutes + * 2 After finding good data (DSYNC lit), going more than 40 minutes * (SYNCH) without finding station sync (INSYNC lit). * - * 4 After finding station sync (INSYNC lit), going more than 2 days + * 3 After finding station sync (INSYNC lit), going more than 2 days * (PANIC) without finding any station. */ static void @@ -2505,6 +2502,8 @@ wwv_newgame( * Initialize strategic values. Note we set the leap bits * NOTINSYNC and the refid "NONE". */ + if (up->status) + up->errflg = CEVNT_TIMEOUT; peer->leap = LEAP_NOTINSYNC; up->watch = up->status = up->alarm = 0; up->avgint = MINAVG; @@ -2514,7 +2513,8 @@ wwv_newgame( /* * Initialize the station processes for audio gain, select bit, * station/frequency identifier and reference identifier. Start - * probing at the next channel after the data channel. + * probing at the strongest channel or the default channel if + * nothing heard. */ memset(up->mitig, 0, sizeof(up->mitig)); for (i = 0; i < NCHAN; i++) { @@ -2525,13 +2525,9 @@ wwv_newgame( cp->wwvh.select = SELH; sprintf(cp->wwvh.refid, "WH%.0f", floor(qsy[i])); } - up->dchan = (DCHAN + NCHAN - 1) % NCHAN;; + up->dchan = (DCHAN + NCHAN - 1) % NCHAN; wwv_newchan(peer); - up->achan = up->schan = up->dchan; -#ifdef ICOM - if (up->fd_icom > 0) - wwv_qsy(peer, up->dchan); -#endif /* ICOM */ + up->schan = up->dchan; } /* @@ -2668,7 +2664,8 @@ timecode( * there are no clips, the gain is bumped up; if there are more than * MAXCLP clips (100), it is bumped down. The decoder is relatively * insensitive to amplitude, so this crudity works just peachy. The - * input port is set and the error flag is cleared, mostly to be ornery. + * routine also jiggles the input port and selectively mutes the + * monitor. */ static void wwv_gain( |