diff options
Diffstat (limited to 'contrib/ntp/ntpd/refclock_chu.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_chu.c | 487 |
1 files changed, 296 insertions, 191 deletions
diff --git a/contrib/ntp/ntpd/refclock_chu.c b/contrib/ntp/ntpd/refclock_chu.c index b239ad6..e0c79e2 100644 --- a/contrib/ntp/ntpd/refclock_chu.c +++ b/contrib/ntp/ntpd/refclock_chu.c @@ -40,10 +40,10 @@ * change throughout the day and night. * * The driver receives, demodulates and decodes the radio signals when - * connected to the audio codec of a Sun workstation running SunOS or - * Solaris, 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. + * connected to the audio codec of a suported workstation hardware and + * operating system. These include Solaris, SunOS, FreeBSD, NetBSD and + * Linux. In this implementation, only one audio driver and codec can be + * supported on a single machine. * * The driver can be compiled to use a Bell 103 compatible modem or * modem chip to receive the radio signal and demodulate the data. @@ -105,8 +105,8 @@ * * The timecode format used for debugging and data recording includes * data helpful in diagnosing problems with the radio signal and serial - * connections. With debugging enabled (-d -d -d on the ntpd command - * line), the driver produces one line for each burst in two formats + * connections. With debugging enabled (-d on the ntpd command line), + * the driver produces one line for each burst in two formats * corresponding to format A and B. Following is format A: * * n b f s m code @@ -161,22 +161,22 @@ * * For accuracies better than the low millisceconds, fudge time1 can be * set to the radio propagation delay from CHU to the receiver. This can - * be done conviently using the minimuf program. When the modem driver - * is compiled, fudge flag3 enables the ppsclock line discipline. Fudge - * flag4 causes the dubugging output described above to be recorded in - * the clockstats file. + * be done conviently using the minimuf program. * - * When the audio driver is compiled, fudge flag2 selects the audio - * input port, where 0 is the mike port (default) and 1 is the line-in - * port. 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 speaker volume must be set before the driver is started. + * Fudge flag4 causes the dubugging output described above to be + * recorded in the clockstats file. When the audio driver is compiled, + * fudge flag2 selects the audio input port, where 0 is the mike port + * (default) and 1 is the line-in port. 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. * * The audio codec code is normally compiled in the driver if the - * architecture supports it (HAVE_AUDIO defined), but is used only if the - * link /dev/chu_audio is defined and valid. The serial port - * code is alwasy compiled in the driver, but is used only if the autdio - * codec is not available and the link /dev/chu%d is defined and valid. + * architecture supports it (HAVE_AUDIO defined), but is used only if + * the link /dev/chu_audio is defined and valid. The serial port code is + * always compiled in the driver, but is used only if the autdio codec + * is not available and the link /dev/chu%d is defined and valid. + * * The ICOM code is normally compiled in the driver if selected (ICOM * defined), but is used only if the link /dev/icom%d is defined and * valid and the mode keyword on the server configuration command @@ -194,11 +194,13 @@ #define DEVICE "/dev/chu%d" /* device name and unit */ #define SPEED232 B300 /* UART speed (300 baud) */ #ifdef ICOM -#define DWELL 5 /* minutes before qsy */ +#define TUNE .001 /* offset for narrow filter (kHz) */ +#define DWELL 5 /* minutes in a probe cycle */ #define NCHAN 3 /* number of channels */ +#define ISTAGE 3 /* number of integrator stages */ #endif /* ICOM */ -#ifdef HAVE_AUDIO +#ifdef HAVE_AUDIO /* * Audio demodulator definitions */ @@ -207,11 +209,13 @@ #define OFFSET 128 /* companded sample offset */ #define SIZE 256 /* decompanding table size */ #define MAXSIG 6000. /* maximum signal level */ +#define MAXCLP 100 /* max clips above reference per s */ #define LIMIT 1000. /* soft limiter threshold */ #define AGAIN 6. /* baseband gain */ #define LAG 10 /* discriminator lag */ #define DEVICE_AUDIO "/dev/chu_audio" /* device name */ #define DESCRIPTION "CHU Audio/Modem Receiver" /* WRU */ +#define AUDIO_BUFSIZ 240 /* audio buffer size (30 ms) */ #else #define DESCRIPTION "CHU Modem Receiver" /* WRU */ #endif /* HAVE_AUDIO */ @@ -224,16 +228,19 @@ #define BURST 11 /* max characters per burst */ #define MINCHAR 9 /* min characters per burst */ #define MINDIST 28 /* min burst distance (of 40) */ +#define MINBURST 4 /* min bursts in minute */ #define MINSYNC 8 /* min sync distance (of 16) */ #define MINSTAMP 20 /* min timestamps (of 60) */ -#define PANIC (4 * 1440) /* panic restart */ +#define METRIC 50. /* min channel metric */ +#define PANIC 1440 /* panic timeout (m) */ +#define HOLD 30 /* reach hold (m) */ /* * Hex extension codes (>= 16) */ -#define HEX_MISS 16 /* miss */ -#define HEX_SOFT 17 /* soft error */ -#define HEX_HARD 18 /* hard error */ +#define HEX_MISS 16 /* miss _ */ +#define HEX_SOFT 17 /* soft error * */ +#define HEX_HARD 18 /* hard error = */ /* * Status bits (status) @@ -246,8 +253,9 @@ #define AFORMAT 0x0020 /* invalid format A data */ #define DECODE 0x0040 /* invalid data decode */ #define STAMP 0x0080 /* too few timestamps */ -#define INYEAR 0x0100 /* valid B frame */ -#define INSYNC 0x0200 /* clock synchronized */ +#define AVALID 0x0100 /* valid A frame */ +#define BVALID 0x0200 /* valid B frame */ +#define INSYNC 0x0400 /* clock synchronized */ /* * Alarm status bits (alarm) @@ -264,6 +272,10 @@ #define TSPERR 0x08 /* insufficient data */ #ifdef HAVE_AUDIO +/* + * Maximum likelihood UART structure. There are eight of these + * corresponding to the number of phases. + */ struct surv { double shift[12]; /* mark register */ double es_max, es_min; /* max/min envelope signals */ @@ -272,6 +284,19 @@ struct surv { }; #endif /* HAVE_AUDIO */ +#ifdef ICOM +/* + * CHU station structure. There are three of these corresponding to the + * three frequencies. + */ +struct xmtr { + double integ[ISTAGE]; /* circular integrator */ + double metric; /* integrator sum */ + int iptr; /* integrator pointer */ + int probe; /* dwells since last probe */ +}; +#endif /* ICOM */ + /* * CHU unit control structure */ @@ -284,12 +309,13 @@ struct chuunit { l_fp charstamp; /* character time as a l_fp */ int errflg; /* error flags */ int status; /* status bits */ - int bufptr; /* buffer index pointer */ - char ident[10]; /* transmitter frequency */ + char ident[5]; /* station ID and channel */ #ifdef ICOM int fd_icom; /* ICOM file descriptor */ - int chan; /* frequency identifier */ - int dwell; /* dwell minutes at current frequency */ + int chan; /* data channel */ + int achan; /* active channel */ + int dwell; /* dwell cycle */ + struct xmtr xmtr[NCHAN]; /* station metric */ #endif /* ICOM */ /* @@ -300,7 +326,6 @@ struct chuunit { int ndx; /* buffer start index */ int prevsec; /* previous burst second */ int burdist; /* burst distance */ - int mindist; /* minimum distance */ int syndist; /* sync distance */ int burstcnt; /* format A bursts this minute */ @@ -320,7 +345,7 @@ struct chuunit { double comp[SIZE]; /* decompanding table */ int port; /* codec port */ int gain; /* codec gain */ - int bufcnt; /* samples in buffer */ + int mongain; /* codec monitor gain */ int clipcnt; /* sample clip count */ int seccnt; /* second interval counter */ @@ -362,21 +387,30 @@ static void chu_clear P((struct peer *)); static void chu_a P((struct peer *, int)); static void chu_b P((struct peer *, int)); static int chu_dist P((int, int)); -static int chu_major P((struct peer *)); +static double chu_major P((struct peer *)); #ifdef HAVE_AUDIO static void chu_uart P((struct surv *, double)); static void chu_rf P((struct peer *, double)); static void chu_gain P((struct peer *)); static void chu_audio_receive P((struct recvbuf *rbufp)); #endif /* HAVE_AUDIO */ +#ifdef ICOM +static int chu_newchan P((struct peer *, double)); +#endif /* ICOM */ static void chu_serial_receive P((struct recvbuf *rbufp)); /* * Global variables */ -static char hexchar[] = "0123456789abcdef_-="; +static char hexchar[] = "0123456789abcdef_*="; + #ifdef ICOM -static double qsy[NCHAN] = {3.33, 7.335, 14.67}; /* frequencies (MHz) */ +/* + * Note the tuned frequencies are 1 kHz higher than the carrier. CHU + * transmits on USB with carrier so we can use AM and the narrow SSB + * filter. + */ +static double qsy[NCHAN] = {3.330, 7.335, 14.670}; /* freq (MHz) */ #endif /* ICOM */ /* @@ -407,7 +441,6 @@ chu_start( char device[20]; /* device name */ int fd; /* file descriptor */ #ifdef ICOM - char tbuf[80]; /* trace buffer */ int temp; #endif /* ICOM */ #ifdef HAVE_AUDIO @@ -418,7 +451,7 @@ chu_start( /* * Open audio device. */ - fd_audio = audio_init(DEVICE_AUDIO); + fd_audio = audio_init(DEVICE_AUDIO, AUDIO_BUFSIZ, unit); #ifdef DEBUG if (fd_audio > 0 && debug) audio_show(); @@ -470,7 +503,8 @@ chu_start( */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; - memcpy((char *)&pp->refid, REFID, 4); + strcpy(up->ident, "CHU"); + memcpy(&peer->refid, up->ident, 4); DTOLFP(CHAR, &up->charstamp); #ifdef HAVE_AUDIO @@ -493,15 +527,14 @@ chu_start( } DTOLFP(1. / SECOND, &up->tick); #endif /* HAVE_AUDIO */ - strcpy(up->ident, "X"); #ifdef ICOM temp = 0; #ifdef DEBUG if (debug > 1) temp = P_TRACE; #endif - if (peer->ttlmax > 0) { - if (peer->ttlmax & 0x80) + if (peer->ttl > 0) { + if (peer->ttl & 0x80) up->fd_icom = icom_init("/dev/icom", B1200, temp); else @@ -509,22 +542,17 @@ chu_start( temp); } if (up->fd_icom > 0) { - if (icom_freq(up->fd_icom, peer->ttlmax & 0x7f, - qsy[up->chan]) < 0) { + if (chu_newchan(peer, 0) != 0) { NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) - msyslog(LOG_ERR, - "ICOM bus error; autotune disabled"); + msyslog(LOG_NOTICE, + "icom: radio not found"); up->errflg = CEVNT_FAULT; close(up->fd_icom); up->fd_icom = 0; } else { - sprintf(up->ident, "%.1f", qsy[up->chan]); - sprintf(tbuf, "chu: QSY to %s MHz", up->ident); - record_clock_stats(&peer->srcadr, tbuf); -#ifdef DEBUG - if (debug) - printf("%s\n", tbuf); -#endif + NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) + msyslog(LOG_NOTICE, + "icom: autotune enabled"); } } #endif /* ICOM */ @@ -548,12 +576,16 @@ chu_shutdown( up = (struct chuunit *)pp->unitptr; if (up == NULL) return; + io_closeclock(&pp->io); +#ifdef ICOM if (up->fd_icom > 0) close(up->fd_icom); +#endif /* ICOM */ free(up); } + /* * chu_receive - receive data from the audio or serial device */ @@ -588,8 +620,8 @@ chu_receive( #endif /* HAVE_AUDIO */ } -#ifdef HAVE_AUDIO +#ifdef HAVE_AUDIO /* * chu_audio_receive - receive data from the audio device */ @@ -604,10 +636,8 @@ chu_audio_receive( double sample; /* codec sample */ u_char *dpt; /* buffer pointer */ + int bufcnt; /* buffer counter */ l_fp ltemp; /* l_fp temp */ - int isneg; /* parity flag */ - double dtemp; - int i, j; peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; @@ -617,13 +647,12 @@ chu_audio_receive( * Main loop - read until there ain't no more. Note codec * samples are bit-inverted. */ + DTOLFP((double)rbufp->recv_length / SECOND, <emp); + L_SUB(&rbufp->recv_time, <emp); up->timestamp = rbufp->recv_time; - up->bufcnt = rbufp->recv_length; - DTOLFP(up->bufcnt * 1. / SECOND, <emp); - L_SUB(&up->timestamp, <emp); - dpt = (u_char *)&rbufp->recv_space; - for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) { - sample = up->comp[~*dpt & 0xff]; + dpt = rbufp->recv_buffer; + for (bufcnt = 0; bufcnt < rbufp->recv_length; bufcnt++) { + sample = up->comp[~*dpt++ & 0xff]; /* * Clip noise spikes greater than MAXSIG. If no clips, @@ -637,56 +666,30 @@ chu_audio_receive( sample = -MAXSIG; up->clipcnt++; } - up->seccnt = (up->seccnt + 1) % SECOND; - if (up->seccnt == 0) { - if (pp->sloppyclockflag & CLK_FLAG2) - up->port = 2; - else - up->port = 1; - chu_gain(peer); - } chu_rf(peer, sample); + L_ADD(&up->timestamp, &up->tick); /* - * During development, it is handy to have an audio - * monitor that can be switched to various signals. This - * code converts the linear signal left in up->monitor - * to codec format. If we can get the grass out of this - * thing and improve modem performance, this expensive - * code will be permanently nixed. + * Once each second ride gain. */ - isneg = 0; - dtemp = up->monitor; - if (sample < 0) { - isneg = 1; - dtemp-= dtemp; - } - i = 0; - j = OFFSET >> 1; - while (j != 0) { - if (dtemp > up->comp[i]) - i += j; - else if (dtemp < up->comp[i]) - i -= j; - else - break; - j >>= 1; + up->seccnt = (up->seccnt + 1) % SECOND; + if (up->seccnt == 0) { + pp->second = (pp->second + 1) % 60; + chu_gain(peer); } - if (isneg) - *dpt = ~(i + OFFSET); - else - *dpt = ~i; - dpt++; - L_ADD(&up->timestamp, &up->tick); } - + /* - * Squawk to the monitor speaker if enabled. + * Set the input port and monitor gain for the next buffer. */ + if (pp->sloppyclockflag & CLK_FLAG2) + up->port = 2; + else + up->port = 1; if (pp->sloppyclockflag & CLK_FLAG3) - if (write(pp->io.fd, (u_char *)&rbufp->recv_space, - (u_int)up->bufcnt) < 0) - perror("chu:"); + up->mongain = MONGAIN; + else + up->mongain = 0; } @@ -1068,13 +1071,13 @@ chu_b( * In a format B burst, a character is considered valid only if * the first occurrence matches the last occurrence. The burst * is considered valid only if all characters are valid; that - * is, only if the distance is 40. + * is, only if the distance is 40. Note that once a valid frame + * has been found errors are ignored. */ sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar, -up->burdist); for (i = 0; i < nchar; i++) - sprintf(&tbuf[strlen(tbuf)], "%02x", - up->cbuf[i]); + sprintf(&tbuf[strlen(tbuf)], "%02x", up->cbuf[i]); if (pp->sloppyclockflag & CLK_FLAG4) record_clock_stats(&peer->srcadr, tbuf); #ifdef DEBUG @@ -1085,7 +1088,7 @@ chu_b( up->status |= BFRAME; return; } - up->status |= INYEAR; + up->status |= BVALID; /* * Convert the burst data to internal format. If this succeeds, @@ -1201,6 +1204,7 @@ chu_a( * the previous burst to the current one. */ if (temp != 0) { + pp->second = 30 + temp; offset.l_ui = 30 + temp; offset.l_f = 0; i = 0; @@ -1236,6 +1240,7 @@ chu_a( up->decode[i][(up->cbuf[j] >> 4) & 0xf]++; i++; } + up->status |= AVALID; up->burstcnt++; } @@ -1251,54 +1256,48 @@ chu_poll( { struct refclockproc *pp; struct chuunit *up; + l_fp offset; char synchar, qual, leapchar; - int minset; - int temp; -#ifdef ICOM - char tbuf[80]; /* trace buffer */ -#endif /* ICOM */ + int minset, i; + double dtemp; + pp = peer->procptr; up = (struct chuunit *)pp->unitptr; if (pp->coderecv == pp->codeproc) up->errflg = CEVNT_TIMEOUT; else pp->polls++; + + /* + * If once in sync and the radio has not been heard for awhile + * (30 m), it is no longer reachable. If not heard in a long + * while (one day), turn out the lights and start from scratch. + */ minset = ((current_time - peer->update) + 30) / 60; if (up->status & INSYNC) { if (minset > PANIC) up->status = 0; - else + else if (minset <= HOLD) peer->reach |= 1; } /* * Process the last burst, if still in the burst buffer. - * Don't mess with anything if nothing has been heard. + * Don't mess with anything if nothing has been heard. If the + * minute contains a valid A frame and valid B frame, assume + * synchronized; however, believe the time only if within metric + * threshold. Note the quality indicator is only for + * diagnostics; the data are used only if in sync and above + * metric threshold. */ chu_burst(peer); + if (up->burstcnt == 0) { #ifdef ICOM - if (up->burstcnt > 2) { - up->dwell = 0; - } else if (up->dwell < DWELL) { - up->dwell++; - } else if (up->fd_icom > 0) { - up->dwell = 0; - up->chan = (up->chan + 1) % NCHAN; - icom_freq(up->fd_icom, peer->ttlmax & 0x7f, qsy[up->chan]); - sprintf(up->ident, "%.3f", qsy[up->chan]); - sprintf(tbuf, "chu: QSY to %s MHz", up->ident); - record_clock_stats(&peer->srcadr, tbuf); -#ifdef DEBUG - if (debug) - printf("%s\n", tbuf); -#endif - } + chu_newchan(peer, 0); #endif /* ICOM */ - if (up->burstcnt == 0) return; - temp = chu_major(peer); - if (up->status & INYEAR) - up->status |= INSYNC; + } + dtemp = chu_major(peer); qual = 0; if (up->status & (BFRAME | AFRAME)) qual |= SYNERR; @@ -1308,6 +1307,8 @@ chu_poll( qual |= DECERR; if (up->status & STAMP) qual |= TSPERR; + if (up->status & AVALID && up->status & BVALID) + up->status |= INSYNC; synchar = leapchar = ' '; if (!(up->status & INSYNC)) { pp->leap = LEAP_NOTINSYNC; @@ -1324,34 +1325,45 @@ chu_poll( #ifdef HAVE_AUDIO if (up->fd_audio) sprintf(pp->a_lastcode, - "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %d %s %d %d %d %d", + "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %d %s %.0f %d", synchar, qual, pp->year, pp->day, pp->hour, pp->minute, pp->second, leapchar, up->dst, up->dut, - minset, up->gain, up->ident, up->tai, up->burstcnt, - up->mindist, up->ntstamp); + minset, up->gain, up->ident, dtemp, up->ntstamp); else sprintf(pp->a_lastcode, - "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %s %d %d %d %d", + "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d", synchar, qual, pp->year, pp->day, pp->hour, pp->minute, pp->second, leapchar, up->dst, up->dut, - minset, up->ident, up->tai, up->burstcnt, - up->mindist, up->ntstamp); + minset, up->ident, dtemp, up->ntstamp); #else sprintf(pp->a_lastcode, - "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %s %d %d %d %d", + "%c%1X %04d %3d %02d:%02d:%02d %c%x %+d %d %s %.0f %d", synchar, qual, pp->year, pp->day, pp->hour, pp->minute, - pp->second, leapchar, up->dst, up->dut, minset, - up->ident, up->tai, up->burstcnt, up->mindist, up->ntstamp); + pp->second, leapchar, up->dst, up->dut, minset, up->ident, + dtemp, up->ntstamp); #endif /* HAVE_AUDIO */ pp->lencode = strlen(pp->a_lastcode); /* - * If timestamps have been stuffed, the timecode is ipso fatso - * correct and can be selected to discipline the clock. + * If in sync and the signal metric is above threshold, the + * timecode is ipso fatso valid and can be selected to + * discipline the clock. Be sure not to leave stray timestamps + * around if signals are too weak or the clock time is invalid. */ - if (temp > 0) { + if (up->status & INSYNC && dtemp > METRIC) { + if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, + up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { + up->errflg = CEVNT_BADTIME; + } else { + offset.l_uf = 0; + for (i = 0; i < up->ntstamp; i++) + refclock_process_offset(pp, offset, + up->tstamp[i], FUDGE + + pp->fudgetime1); + pp->lastref = up->timestamp; + refclock_receive(peer); + } record_clock_stats(&peer->srcadr, pp->a_lastcode); - refclock_receive(peer); } else if (pp->sloppyclockflag & CLK_FLAG4) { record_clock_stats(&peer->srcadr, pp->a_lastcode); } @@ -1360,6 +1372,9 @@ chu_poll( printf("chu: timecode %d %s\n", pp->lencode, pp->a_lastcode); #endif +#ifdef ICOM + chu_newchan(peer, dtemp); +#endif /* ICOM */ chu_clear(peer); if (up->errflg) refclock_report(peer, up->errflg); @@ -1370,7 +1385,7 @@ chu_poll( /* * chu_major - majority decoder */ -static int +static double chu_major( struct peer *peer /* peer structure pointer */ ) @@ -1379,10 +1394,9 @@ chu_major( struct chuunit *up; u_char code[11]; /* decoded timecode */ - l_fp toffset, offset; /* l_fp temps */ + int mindist; /* minimum distance */ int val1, val2; /* maximum distance */ int synchar; /* stray cat */ - double dtemp; int temp; int i, j, k; @@ -1392,20 +1406,20 @@ chu_major( /* * Majority decoder. Each burst encodes two replications at each * digit position in the timecode. Each row of the decoding - * matrix encodes the number of occurences of each digit found + * matrix encodes the number of occurrences of each digit found * at the corresponding position. The maximum over all - * occurences at each position is the distance for this position - * and the corresponding digit is the maximumn likelihood - * candidate. If the distance is zero, assume a miss '_'; if the - * distance is not more than half the total number of - * occurences, assume a soft error '-'; if two different digits - * with the same distance are found, assume a hard error '='. - * These will later cause a format error when the timecode is - * interpreted. The decoding distance is defined as the minimum - * distance over the first nine digits. The tenth digit varies - * over the seconds, so we don't count it. + * occurrences at each position is the distance for this + * position and the corresponding digit is the maximum + * likelihood candidate. If the distance is zero, assume a miss + * '_'; if the distance is not more than half the total number + * of occurrences, assume a soft error '*'; if two different + * digits with the same distance are found, assume a hard error + * '='. These will later cause a format error when the timecode + * is interpreted. The decoding distance is defined as the + * minimum distance over the first nine digits. The tenth digit + * varies over the seconds, so we don't count it. */ - up->mindist = 16; + mindist = 16; for (i = 0; i < 9; i++) { val1 = val2 = 0; k = 0; @@ -1425,19 +1439,18 @@ chu_major( code[i] = HEX_SOFT; else code[i] = k; - if (val1 < up->mindist) - up->mindist = val1; + if (val1 < mindist) + mindist = val1; code[i] = hexchar[code[i]]; } code[i] = 0; /* - * A valid timecode requires at least three bursts and a - * decoding distance greater than half the total number of - * occurences. A valid timecode also requires at least 20 valid - * timestamps. + * A valid timecode requires a minimum distance at least half + * the total number of occurrences. A valid timecode also + * requires at least 20 valid timestamps. */ - if (up->burstcnt < 3 || up->mindist <= up->burstcnt) + if (up->burstcnt < MINBURST || mindist < up->burstcnt) up->status |= DECODE; if (up->ntstamp < MINSTAMP) up->status |= STAMP; @@ -1458,20 +1471,7 @@ chu_major( up->errflg = CEVNT_BADREPLY; return (0); } - L_CLR(&offset); - if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, - up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { - up->errflg = CEVNT_BADTIME; - return (0); - } - pp->lastref = offset; - for (i = 0; i < up->ntstamp; i++) { - toffset = offset; - L_SUB(&toffset, &up->tstamp[i]); - LFPTOD(&toffset, dtemp); - SAMPLE(dtemp + FUDGE + pp->fudgetime1); - } - return (i); + return (mindist * 100. / (2. * up->burstcnt)); } @@ -1494,15 +1494,120 @@ chu_clear( * Clear stuff for the minute. */ up->ndx = up->prevsec = 0; - up->burstcnt = up->mindist = up->ntstamp = 0; - up->status &= INSYNC | INYEAR; - up->burstcnt = 0; + up->burstcnt = up->ntstamp = 0; + up->status &= INSYNC; for (i = 0; i < 20; i++) { for (j = 0; j < 16; j++) up->decode[i][j] = 0; } } +#ifdef ICOM +/* + * chu_newchan - called once per minute to find the best channel; + * returns zero on success, nonzero if ICOM error. + */ +static int +chu_newchan( + struct peer *peer, + double met + ) +{ + struct chuunit *up; + struct refclockproc *pp; + struct xmtr *sp; + char tbuf[80]; /* trace buffer */ + int rval; + double metric; + int i, j; + + pp = peer->procptr; + up = (struct chuunit *)pp->unitptr; + + /* + * The radio can be tuned to three channels: 0 (3330 kHz), 1 + * (7335 kHz) and 2 (14670 kHz). There are five one-minute + * dwells in each cycle. During the first dwell the radio is + * tuned to one of three probe channels; during the remaining + * four dwells the radio is tuned to the data channel. The probe + * channel is selects as the least recently used. At the end of + * each dwell the channel metrics are measured and the highest + * one is selected as the data channel. + */ + if (up->fd_icom <= 0) + return (0); + + sp = &up->xmtr[up->achan]; + sp->metric -= sp->integ[sp->iptr]; + sp->integ[sp->iptr] = met; + sp->metric += sp->integ[sp->iptr]; + sp->iptr = (sp->iptr + 1) % ISTAGE; + metric = 0; + j = 0; + for (i = 0; i < NCHAN; i++) { + up->xmtr[i].probe++; + if (i == up->achan) + up->xmtr[i].probe = 0; + if (up->xmtr[i].metric < metric) + continue; + metric = up->xmtr[i].metric; + j = i; + } + if (j != up->chan && metric > 0) { + up->chan = j; + sprintf(tbuf, "chu: QSY to %.3f MHz metric %.0f", + qsy[up->chan], metric); + if (pp->sloppyclockflag & CLK_FLAG4) + record_clock_stats(&peer->srcadr, tbuf); +#ifdef DEBUG + if (debug) + printf("%s\n", tbuf); +#endif + } + + /* + * Start the next dwell. We speed up the initial sync a little. + * If not in sync and no bursts were heard the previous dwell, + * restart the probe. + */ + rval = 0; + if (up->burstcnt == 0 && !(up->status & INSYNC)) + up->dwell = 0; +#ifdef DEBUG + if (debug) + printf( + "chu: at %ld dwell %d achan %d metric %.0f chan %d\n", + current_time, up->dwell, up->achan, sp->metric, + up->chan); +#endif + if (up->dwell == 0) { + rval = 0; + for (i = 0; i < NCHAN; i++) { + if (up->xmtr[i].probe < rval) + continue; + rval = up->xmtr[i].probe; + up->achan = i; + } + rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, + qsy[up->achan] + TUNE); +#ifdef DEBUG + if (debug) + printf("chu: at %ld probe channel %d\n", + current_time, up->achan); +#endif + } else { + if (up->achan != up->chan) { + rval = icom_freq(up->fd_icom, peer->ttl & 0x7f, + qsy[up->chan] + TUNE); + up->achan = up->chan; + } + } + sprintf(up->ident, "CHU%d", up->achan); + memcpy(&peer->refid, up->ident, 4); + up->dwell = (up->dwell + 1) % DWELL; + return (rval); +} +#endif /* ICOM */ /* * chu_dist - determine the distance of two octet arguments @@ -1564,14 +1669,14 @@ chu_gain( */ if (up->clipcnt == 0) { up->gain += 4; - if (up->gain > 255) - up->gain = 255; - } else if (up->clipcnt > SECOND / 100) { + if (up->gain > MAXGAIN) + up->gain = MAXGAIN; + } else if (up->clipcnt > MAXCLP) { up->gain -= 4; if (up->gain < 0) up->gain = 0; } - audio_gain(up->gain, up->port); + audio_gain(up->gain, up->mongain, up->port); up->clipcnt = 0; } #endif /* HAVE_AUDIO */ |