📄 refclock_wwv.c
字号:
* but as early as possible if the second epoch is not exact. */ if (up->rphase == 15 * MS) sigmin = sigzer = sigone = up->irig; /* * The zero bit is defined as the maximum data signal over the * interval 190-210 ms. Keep this around until the end of the * second. */ else if (up->rphase >= 190 * MS && up->rphase < 210 * MS && up->irig > sigzer) sigzer = up->irig; /* * The one bit is defined as the maximum data signal over the * interval 490-510 ms. Keep this around until the end of the * second. */ else if (up->rphase >= 490 * MS && up->rphase < 510 * MS && up->irig > sigone) sigone = up->irig; /* * At the end of the second crank the clock state machine and * adjust the codec gain. Note the epoch is buffered from the * center of the second in order to avoid jitter while the * seconds synch is diddling the epoch. Then, determine the true * offset and update the median filter in the driver interface. * * Use the energy as the noise to compute the SNR for the pulse. * This gives a better measurement than the beginning of the * second, especially when returning from the probe channel. * This gives a guard time of 30 ms from the decay of the * longest pulse to the rise of the next pulse. */ up->rphase++; if (up->mphase % SECOND == up->repoch) { up->status &= ~(DGATE | BGATE); engmin = sqrt(up->irig * up->irig + up->qrig * up->qrig); up->datsig = engmax; up->datsnr = wwv_snr(engmax, engmin); /* * If the amplitude or SNR is below threshold, average a * 0 in the the integrators; otherwise, average the * bipolar signal. This is done to avoid noise polution. */ if (engmax < DTHR || up->datsnr < DSNR) { up->status |= DGATE; wwv_rsec(peer, 0); } else { sigzer -= sigone; sigone -= sigmin; wwv_rsec(peer, sigone - sigzer); } if (up->status & (DGATE | BGATE)) up->errcnt++; if (up->errcnt > MAXERR) up->alarm |= LOWERR; wwv_gain(peer); up->rphase = 0; }}/* * wwv_rsec - process receiver second * * This routine is called at the end of each receiver second to * implement the per-second state machine. The machine assembles BCD * digit bits, decodes miscellaneous bits and dances the leap seconds. * * Normally, the minute has 60 seconds numbered 0-59. If the leap * warning bit is set, the last minute (1439) of 30 June (day 181 or 182 * for leap years) or 31 December (day 365 or 366 for leap years) is * augmented by one second numbered 60. This is accomplished by * extending the minute interval by one second and teaching the state * machine to ignore it. */static voidwwv_rsec( struct peer *peer, /* peer structure pointer */ double bit ){ static int iniflg; /* initialization flag */ static double bcddld[4]; /* BCD data bits */ static double bitvec[61]; /* bit integrator for misc bits */ struct refclockproc *pp; struct wwvunit *up; struct chan *cp; struct sync *sp, *rp; char tbuf[80]; /* monitor buffer */ int sw, arg, nsec; pp = peer->procptr; up = (struct wwvunit *)pp->unitptr; if (!iniflg) { iniflg = 1; memset((char *)bitvec, 0, sizeof(bitvec)); } /* * The bit represents the probability of a hit on zero (negative * values), a hit on one (positive values) or a miss (zero * value). The likelihood vector is the exponential average of * these probabilities. Only the bits of this vector * corresponding to the miscellaneous bits of the timecode are * used, but it's easier to do them all. After that, crank the * seconds state machine. */ nsec = up->rsec; up->rsec++; bitvec[nsec] += (bit - bitvec[nsec]) / TCONST; sw = progx[nsec].sw; arg = progx[nsec].arg; /* * The minute state machine. Fly off to a particular section as * directed by the transition matrix and second number. */ switch (sw) { /* * Ignore this second. */ case IDLE: /* 9, 45-49 */ break; /* * Probe channel stuff * * The WWV/H format contains data pulses in second 59 (position * identifier) and second 1, but not in second 0. The minute * sync pulse is contained in second 0. second 0. At the end of * second 58 QSY to the probe channel, which rotates in turn * over all WWV/H frequencies. At the end of second 0 measure * the minute sync pulse. At the end of second 1 measure the * data pulse and QSY back to the data channel. Note that the * seconds numbering here applies to the end of the second, so * appears one second earlier than the clock displayed on the * radio itself. * * At the end of second 0 save the minute sync pulse peak * amplitude previously latched at 800 ms. */ case SYNC2: /* 0 */ cp = &up->mitig[up->achan]; cp->wwv.synmax = cp->wwv.syneng; cp->wwvh.synmax = cp->wwvh.syneng; break; /* * At the end of second 1 measure the minute sync pulse * minimum amplitude and calculate the SNR. If the minute sync * pulse and SNR are above threshold and the data pulse * amplitude and SNR are above thresold and, shift a 1 into the * station reachability register; otherwise, shift a 0. The * number of 1 bits in the last six intervals is a component of * the channel metric computed by the mitigation routine. * Finally, QSY back to the data channel. */ case SYNC3: /* 1 */ cp = &up->mitig[up->achan]; /* * WWV station */ sp = &cp->wwv; sp->synsnr = wwv_snr(sp->synmax, sp->syneng); sp->reach <<= 1; if (sp->reach & (1 << AMAX)) sp->count--; if (sp->synmax >= QTHR && sp->synsnr >= QSNR && ~(up->status & (DGATE | BGATE))) { sp->reach |= 1; sp->count++; } sp->metric = wwv_metric(sp); /* * WWVH station */ rp = &cp->wwvh; rp->synsnr = wwv_snr(rp->synmax, rp->syneng); rp->reach <<= 1; if (rp->reach & (1 << AMAX)) rp->count--; if (rp->synmax >= QTHR && rp->synsnr >= QSNR && ~(up->status & (DGATE | BGATE))) { rp->reach |= 1; rp->count++; } rp->metric = wwv_metric(rp); if (pp->sloppyclockflag & CLK_FLAG4) { sprintf(tbuf, "wwv5 %04x %3d %4d %.0f/%.1f %.0f/%.1f %s %04x %.0f %.0f/%.1f %s %04x %.0f %.0f/%.1f", up->status, up->gain, up->yepoch, up->epomax, up->eposnr, up->datsig, up->datsnr, sp->refid, sp->reach & 0xffff, sp->metric, sp->synmax, sp->synsnr, rp->refid, rp->reach & 0xffff, rp->metric, rp->synmax, rp->synsnr); record_clock_stats(&peer->srcadr, tbuf);#ifdef DEBUG if (debug) printf("%s\n", tbuf);#endif /* DEBUG */ } up->errcnt = up->digcnt = up->alarm = 0; /* * We now begin the minute scan. Before first * synchronizing to a station, reset and step to the * next channel immediately if no station has been * heard. Reset and step after the DATA timeout (4 min) * if a station has been heard, but too few good data * bits have been found. In any case, reset and step * after the SYNCH timeout (30 min). * * After synchronizing to a station, reset and step * after the PANIC timeout (2 days). */ if (!(up->status & INSYNC)) { if (!wwv_newchan(peer)) { wwv_newgame(peer, DCHAN); return; } if (up->watch > DATA && !(up->status & DSYNC)) { wwv_newgame(peer, DCHAN); return; } if (up->watch > SYNCH) { wwv_newgame(peer, DCHAN); return; } } else { wwv_newchan(peer); if (up->watch > PANIC) { wwv_newgame(peer, DCHAN); return; } }#ifdef ICOM if (up->fd_icom > 0) wwv_qsy(peer, up->dchan);#endif /* ICOM */ break; /* * Save the bit probability in the BCD data vector at the index * given by the argument. Bits not used in the digit are forced * to zero. */ case COEF1: /* 4-7 */ bcddld[arg] = bit; break; case COEF: /* 10-13, 15-17, 20-23, 25-26, 30-33, 35-38, 40-41, 51-54 */ if (up->status & DSYNC) bcddld[arg] = bit; else bcddld[arg] = 0; break; case COEF2: /* 18, 27-28, 42-43 */ bcddld[arg] = 0; break; /* * Correlate coefficient vector with each valid digit vector and * save in decoding matrix. We step through the decoding matrix * digits correlating each with the coefficients and saving the * greatest and the next lower for later SNR calculation. */ case DECIM2: /* 29 */ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd2); break; case DECIM3: /* 44 */ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd3); break; case DECIM6: /* 19 */ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd6); break; case DECIM9: /* 8, 14, 24, 34, 39 */ wwv_corr4(peer, &up->decvec[arg], bcddld, bcd9); break; /* * Miscellaneous bits. If above the positive threshold, declare * 1; if below the negative threshold, declare 0; otherwise * raise the BGATE bit. The design is intended to avoid * integrating noise under low SNR conditions. */ case MSC20: /* 55 */ wwv_corr4(peer, &up->decvec[YR + 1], bcddld, bcd9); /* fall through */ case MSCBIT: /* 2-3, 50, 56-57 */ if (bitvec[nsec] > BTHR) up->misc |= arg; else if (bitvec[nsec] < -BTHR) up->misc &= ~arg; else up->status |= BGATE; break; /* * Save the data channel gain, then QSY to the probe channel and * dim the seconds comb filters. The newchan() routine will * light them back up. */ case MSC21: /* 58 */ if (bitvec[nsec] > BTHR) up->misc |= arg; else if (bitvec[nsec] < -BTHR) up->misc &= ~arg; else up->status |= BGATE; up->status &= ~(SELV | SELH);#ifdef ICOM if (up->fd_icom > 0) { up->schan = (up->schan + 1) % NCHAN; wwv_qsy(peer, up->schan); }#endif /* ICOM */ break; /* * The endgames * * During second 59 the receiver and codec AGC are settling * down, so the data pulse is unusable as quality metric. If * LEPSEC is set on the last minute of 30 June or 31 December, * the transmitter and receiver insert an extra second (60) in * the timescale and the minute sync repeats the second. Once we * tested this wrinkle at intervals of about 18 months, but * strangely enough a leap has not been declared since the end * of 1998. */ case MIN1: /* 59 */ if (up->status & LEPSEC) break; /* fall through */ case MIN2: /* 60 */ up->status &= ~LEPSEC; wwv_tsec(peer); up->rsec = 0; wwv_clock(peer); break; } if ((pp->sloppyclockflag & CLK_FLAG4) && !(up->status & DSYNC)) { sprintf(tbuf, "wwv3 %2d %04x %3d %4d %5.0f %5.1f %5.0f %5.1f %5.0f", nsec, up->status, up->gain, up->yepoch, up->epomax, up->eposnr, up->datsig, up->datsnr, bit); record_clock_stats(&peer->srcadr, tbuf);#ifdef DEBUG if (debug) printf("%s\n", tbuf);#endif /* DEBUG */ } pp->disp += AUDIO_PHI;}/* * If victory has been declared and seconds sync is lit, strike * a timestamp. It should not be a surprise, especially if the * radio is not tunable, that sometimes no stations are above * the noise and the reference ID set to NONE. */static voidwwv_clock( struct peer *peer /* peer unit pointer */ ){ struct refclockproc *pp; struct wwvunit *up; l_fp offset; /* offset in NTP seconds */ pp = peer->procptr; up = (struct wwvunit *)pp->unitptr; if (!(up->status & SSYNC)) up->alarm |= SYNERR; if (up->digcnt < 9) up->alarm |= NINERR; if (!(up->alarm)) { up->status |= INSYNC; if (up->misc & SECWAR) pp->leap = LEAP_ADDSECOND; else pp->leap = LEAP_NOWARNING; pp->second = up->rsec; pp->minute = up->decvec[MN].digit + up->decvec[MN + 1].digit * 10; pp->hour = up->decvec[HR].digit + up->decvec[HR + 1].digit * 10; pp->day = up->decvec[DA].digit + up->decvec[DA + 1].digit * 10 + up->decvec[DA + 2].digit * 100; pp->year = up->decvec[YR].digit + up->decvec[YR + 1].digit * 10; pp->year += 2000; L_CLR(&offset); if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT, up->timestamp.l_ui, &pp->yearstart, &offset.l_ui)) { up->errflg = CEVNT_BADTIME; } else { up->watch = 0; pp->disp = 0; refclock_process_offset(pp, offset, up->timestamp, PDELAY); refclock_receive(peer); } } pp->lencode = timecode(up, pp->a_lastcode); record_clock_stats(
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -