refclock_chu.c revision 56746
199461Sobrien/* 2218822Sdim * refclock_chu - clock driver for Canadian CHU time/frequency station 3218822Sdim */ 499461Sobrien#ifdef HAVE_CONFIG_H 599461Sobrien#include <config.h> 699461Sobrien#endif 799461Sobrien 899461Sobrien#if defined(REFCLOCK) && defined(CLOCK_CHU) 999461Sobrien 1099461Sobrien#include <stdio.h> 1199461Sobrien#include <ctype.h> 1299461Sobrien#include <sys/time.h> 1399461Sobrien#include <time.h> 1499461Sobrien#include <math.h> 1599461Sobrien 1699461Sobrien#include "ntpd.h" 1799461Sobrien#include "ntp_io.h" 1899461Sobrien#include "ntp_refclock.h" 1999461Sobrien#include "ntp_calendar.h" 20218822Sdim#include "ntp_stdlib.h" 21218822Sdim#ifdef AUDIO_CHU 2299461Sobrien#include "audio.h" 23218822Sdim#endif /* AUDIO_CHU */ 2499461Sobrien 2599461Sobrien#define ICOM 1 /* undefine to suppress ICOM code */ 2699461Sobrien 2799461Sobrien#ifdef ICOM 2899461Sobrien#include "icom.h" 2999461Sobrien#endif /* ICOM */ 3099461Sobrien 3199461Sobrien/* 3299461Sobrien * Audio CHU demodulator/decoder 33130561Sobrien * 3499461Sobrien * This driver synchronizes the computer time using data encoded in 3599461Sobrien * radio transmissions from Canadian time/frequency station CHU in 3699461Sobrien * Ottawa, Ontario. Transmissions are made continuously on 3330 kHz, 3799461Sobrien * 7335 kHz and 14670 kHz in upper sideband, compatible AM mode. An 3899461Sobrien * ordinary shortwave receiver can be tuned manually to one of these 39130561Sobrien * frequencies or, in the case of ICOM receivers, the receiver can be 4099461Sobrien * tuned automatically using this program as propagation conditions 41130561Sobrien * change throughout the day and night. 4299461Sobrien * 4399461Sobrien * The driver receives, demodulates and decodes the radio signals when 44218822Sdim * connected to the audio codec of a Sun workstation running SunOS or 45107492Sobrien * Solaris, and with a little help, other workstations with similar 46130561Sobrien * codecs or sound cards. In this implementation, only one audio driver 4799461Sobrien * and codec can be supported on a single machine. 4899461Sobrien * 49130561Sobrien * The driver can be compiled to use a Bell 103 compatible modem or 50130561Sobrien * modem chip to receive the radio signal and demodulate the data. 51130561Sobrien * Alternatively, the driver can be compiled to use the audio codec of 52130561Sobrien * the Sun workstation or another with compatible audio drivers. In the 5399461Sobrien * latter case, the driver implements the modem using DSP routines, so 54130561Sobrien * the radio can be connected directly to either the microphone on line 5599461Sobrien * input port. In either case, the driver decodes the data using a 56130561Sobrien * maximum likelihood technique which exploits the considerable degree 5799461Sobrien * of redundancy available to maximize accuracy and minimize errors. 58130561Sobrien * 5999461Sobrien * The CHU time broadcast includes an audio signal compatible with the 60130561Sobrien * Bell 103 modem standard (mark = 2225 Hz, space = 2025 Hz). It consist 6199461Sobrien * of nine, ten-character bursts transmitted at 300 bps and beginning 6299461Sobrien * each second from second 31 to second 39 of the minute. Each character 63130561Sobrien * consists of eight data bits plus one start bit and two stop bits to 6499461Sobrien * encode two hex digits. The burst data consist of five characters (ten 6599461Sobrien * hex digits) followed by a repeat of these characters. In format A, 6699461Sobrien * the characters are repeated in the same polarity; in format B, the 6799461Sobrien * characters are repeated in the opposite polarity. 68130561Sobrien * 6999461Sobrien * Format A bursts are sent at seconds 32 through 39 of the minute in 70130561Sobrien * hex digits 71130561Sobrien * 72130561Sobrien * 6dddhhmmss6dddhhmmss 73130561Sobrien * 74130561Sobrien * The first ten digits encode a frame marker (6) followed by the day 75130561Sobrien * (ddd), hour (hh in UTC), minute (mm) and the second (ss). Since 76130561Sobrien * format A bursts are sent during the third decade of seconds the tens 77130561Sobrien * digit of ss is always 3. The driver uses this to determine correct 78130561Sobrien * burst synchronization. These digits are then repeated with the same 79130561Sobrien * polarity. 80130561Sobrien * 81130561Sobrien * Format B bursts are sent at second 31 of the minute in hex digits 82130561Sobrien * 83130561Sobrien * xdyyyyttaaxdyyyyttaa 84130561Sobrien * 85130561Sobrien * The first ten digits encode a code (x described below) followed by 8699461Sobrien * the DUT1 (d in deciseconds), Gregorian year (yyyy), difference TAI - 8799461Sobrien * UTC (tt) and daylight time indicator (aa) peculiar to Canada. These 8899461Sobrien * digits are then repeated with inverted polarity. 8999461Sobrien * 9099461Sobrien * The x is coded 9199461Sobrien * 9299461Sobrien * 1 Sign of DUT (0 = +) 9399461Sobrien * 2 Leap second warning. One second will be added. 9499461Sobrien * 4 Leap second warning. One second will be subtracted. 9599461Sobrien * 8 Even parity bit for this nibble. 9699461Sobrien * 97130561Sobrien * By design, the last stop bit of the last character in the burst 9899461Sobrien * coincides with 0.5 second. Since characters have 11 bits and are 9999461Sobrien * transmitted at 300 bps, the last stop bit of the first character 10099461Sobrien * coincides with 0.5 - 10 * 11/300 = 0.133 second. Depending on the 10199461Sobrien * UART, character interrupts can vary somewhere between the beginning 102130561Sobrien * of bit 9 and end of bit 11. These eccentricities can be corrected 10399461Sobrien * along with the radio propagation delay using fudge time 1. 10499461Sobrien * 105130561Sobrien * Debugging aids 10699461Sobrien * 107130561Sobrien * The timecode format used for debugging and data recording includes 108130561Sobrien * data helpful in diagnosing problems with the radio signal and serial 109130561Sobrien * connections. With debugging enabled (-d -d -d on the ntpd command 110130561Sobrien * line), the driver produces one line for each burst in two formats 111130561Sobrien * corresponding to format A and B. Following is format A: 112130561Sobrien * 113130561Sobrien * n b f s m code 114130561Sobrien * 115130561Sobrien * where n is the number of characters in the burst (0-11), b the burst 116130561Sobrien * distance (0-40), f the field alignment (-1, 0, 1), s the 117130561Sobrien * synchronization distance (0-16), m the burst number (2-9) and code 118130561Sobrien * the burst characters as received. Note that the hex digits in each 119130561Sobrien * character are reversed, so the burst 120130561Sobrien * 121130561Sobrien * 10 38 0 16 9 06851292930685129293 122130561Sobrien * 123130561Sobrien * is interpreted as containing 11 characters with burst distance 38, 124130561Sobrien * field alignment 0, synchronization distance 16 and burst number 9. 125130561Sobrien * The nibble-swapped timecode shows day 58, hour 21, minute 29 and 126130561Sobrien * second 39. 127130561Sobrien * 128130561Sobrien * When the audio driver is compiled, format A is preceded by 129130561Sobrien * the current gain (0-255) and relative signal level (0-9999). The 130130561Sobrien * receiver folume control should be set so that the gain is somewhere 131130561Sobrien * near the middle of the range 0-255, which results in a signal level 132130561Sobrien * near 1000. 133130561Sobrien * 134130561Sobrien * Following is format B: 135130561Sobrien * 136130561Sobrien * n b s code 137130561Sobrien * 138130561Sobrien * where n is the number of characters in the burst (0-11), b the burst 139130561Sobrien * distance (0-40), s the synchronization distance (0-40) and code the 140130561Sobrien * burst characters as received. Note that the hex digits in each 141130561Sobrien * character are reversed and the last ten digits inverted, so the burst 142130561Sobrien * 143130561Sobrien * 11 40 1091891300ef6e76ecff 144130561Sobrien * 145130561Sobrien * is interpreted as containing 11 characters with burst distance 40. 146130561Sobrien * The nibble-swapped timecode shows DUT1 +0.1 second, year 1998 and TAI 147130561Sobrien * - UTC 31 seconds. 148130561Sobrien * 149130561Sobrien * In addition to the above, the reference timecode is updated and 150130561Sobrien * written to the clockstats file and debug score after the last burst 151130561Sobrien * received in the minute. The format is 152130561Sobrien * 153130561Sobrien * qq yyyy ddd hh:mm:ss nn dd tt 154130561Sobrien * 155130561Sobrien * where qq are the error flags, as described below, yyyy is the year, 156130561Sobrien * ddd the day, hh:mm:ss the time of day, nn the number of format A 157130561Sobrien * bursts received during the previous minute, dd the decoding distance 158130561Sobrien * and tt the number of timestamps. The error flags are cleared after 159130561Sobrien * every update. 160130561Sobrien * 161130561Sobrien * Fudge factors 162130561Sobrien * 163130561Sobrien * For accuracies better than the low millisceconds, fudge time1 can be 164130561Sobrien * set to the radio propagation delay from CHU to the receiver. This can 165130561Sobrien * be done conviently using the minimuf program. When the modem driver 166130561Sobrien * is compiled, fudge flag3 enables the ppsclock line discipline. Fudge 167130561Sobrien * flag4 causes the dubugging output described above to be recorded in 168130561Sobrien * the clockstats file. 169130561Sobrien * 170130561Sobrien * When the audio driver is compiled, fudge flag2 selects the audio 171130561Sobrien * input port, where 0 is the mike port (default) and 1 is the line-in 172130561Sobrien * port. It does not seem useful to select the compact disc player port. 173130561Sobrien * Fudge flag3 enables audio monitoring of the input signal. For this 174130561Sobrien * purpose, the speaker volume must be set before the driver is started. 175130561Sobrien * 176130561Sobrien * The ICOM code is normally compiled in the driver. It isn't used, 177130561Sobrien * unless the mode keyword on the server configuration command specifies 178130561Sobrien * a nonzero ICOM ID select code. The C-IV trace is turned on if the 179130561Sobrien * debug level is greater than one. 180130561Sobrien */ 181130561Sobrien/* 182130561Sobrien * Interface definitions 183130561Sobrien */ 184130561Sobrien#define SPEED232 B300 /* uart speed (300 baud) */ 185130561Sobrien#define PRECISION (-10) /* precision assumed (about 1 ms) */ 186130561Sobrien#define REFID "CHU" /* reference ID */ 187130561Sobrien#ifdef ICOM 188130561Sobrien#define DWELL 5 /* minutes before qsy */ 189130561Sobrien#define NCHAN 3 /* number of channels */ 190130561Sobrien#endif /* ICOM */ 191130561Sobrien#ifdef AUDIO_CHU 192130561Sobrien#define DESCRIPTION "CHU Modem Receiver" /* WRU */ 193130561Sobrien 194130561Sobrien/* 195130561Sobrien * Audio demodulator definitions 196130561Sobrien */ 197130561Sobrien#define SECOND 8000 /* nominal sample rate (Hz) */ 198130561Sobrien#define BAUD 300 /* modulation rate (bps) */ 199130561Sobrien#define OFFSET 128 /* companded sample offset */ 200130561Sobrien#define SIZE 256 /* decompanding table size */ 201130561Sobrien#define MAXSIG 6000. /* maximum signal level */ 202130561Sobrien#define LIMIT 1000. /* soft limiter threshold */ 203130561Sobrien#define AGAIN 6. /* baseband gain */ 204130561Sobrien#define LAG 10 /* discriminator lag */ 205130561Sobrien#else 206130561Sobrien#define DEVICE "/dev/chu%d" /* device name and unit */ 207130561Sobrien#define SPEED232 B300 /* UART speed (300 baud) */ 208130561Sobrien#define DESCRIPTION "CHU Audio Receiver" /* WRU */ 209130561Sobrien#endif /* AUDIO_CHU */ 210130561Sobrien 211130561Sobrien/* 212130561Sobrien * Decoder definitions 213130561Sobrien */ 21499461Sobrien#define CHAR (11. / 300.) /* character time (s) */ 21599461Sobrien#define FUDGE .185 /* offset to first stop bit (s) */ 21699461Sobrien#define BURST 11 /* max characters per burst */ 21799461Sobrien#define MINCHAR 9 /* min characters per burst */ 218130561Sobrien#define MINDIST 28 /* min burst distance (of 40) */ 21999461Sobrien#define MINSYNC 8 /* min sync distance (of 16) */ 220130561Sobrien#define MINSTAMP 20 /* min timestamps (of 60) */ 22199461Sobrien#define PANIC (4 * 1440) /* panic restart */ 22299461Sobrien 22399461Sobrien/* 22499461Sobrien * Hex extension codes (>= 16) 22599461Sobrien */ 22699461Sobrien#define HEX_MISS 16 /* miss */ 22799461Sobrien#define HEX_SOFT 17 /* soft error */ 22899461Sobrien#define HEX_HARD 18 /* hard error */ 22999461Sobrien 23099461Sobrien/* 23199461Sobrien * Status bits (status) 23299461Sobrien */ 23399461Sobrien#define RUNT 0x0001 /* runt burst */ 23499461Sobrien#define NOISE 0x0002 /* noise burst */ 23599461Sobrien#define BFRAME 0x0004 /* invalid format B frame sync */ 23699461Sobrien#define BFORMAT 0x0008 /* invalid format B data */ 23799461Sobrien#define AFRAME 0x0010 /* invalid format A frame sync */ 23899461Sobrien#define AFORMAT 0x0020 /* invalid format A data */ 23999461Sobrien#define DECODE 0x0040 /* invalid data decode */ 24099461Sobrien#define STAMP 0x0080 /* too few timestamps */ 24199461Sobrien#define INYEAR 0x0100 /* valid B frame */ 24299461Sobrien#define INSYNC 0x0200 /* clock synchronized */ 24399461Sobrien 24499461Sobrien/* 24599461Sobrien * Alarm status bits (alarm) 24699461Sobrien * 24799461Sobrien * These alarms are set at the end of a minute in which at least one 24899461Sobrien * burst was received. SYNERR is raised if the AFRAME or BFRAME status 24999461Sobrien * bits are set during the minute, FMTERR is raised if the AFORMAT or 25099461Sobrien * BFORMAT status bits are set, DECERR is raised if the DECODE status 25199461Sobrien * bit is set and TSPERR is raised if the STAMP status bit is set. 25299461Sobrien */ 25399461Sobrien#define SYNERR 0x01 /* frame sync error */ 25499461Sobrien#define FMTERR 0x02 /* data format error */ 25599461Sobrien#define DECERR 0x04 /* data decoding error */ 25699461Sobrien#define TSPERR 0x08 /* insufficient data */ 25799461Sobrien 258130561Sobrien#ifdef AUDIO_CHU 25999461Sobrienstruct surv { 26099461Sobrien double shift[12]; /* mark register */ 26199461Sobrien double es_max, es_min; /* max/min envelope signals */ 26299461Sobrien double dist; /* sample distance */ 26399461Sobrien int uart; /* decoded character */ 26499461Sobrien}; 26599461Sobrien#endif /* AUDIO_CHU */ 26699461Sobrien 26799461Sobrien/* 26899461Sobrien * CHU unit control structure 26999461Sobrien */ 27099461Sobrienstruct chuunit { 27199461Sobrien u_char decode[20][16]; /* maximum likelihood decoding matrix */ 27299461Sobrien l_fp cstamp[BURST]; /* character timestamps */ 27399461Sobrien l_fp tstamp[MAXSTAGE]; /* timestamp samples */ 27499461Sobrien l_fp timestamp; /* current buffer timestamp */ 27599461Sobrien l_fp laststamp; /* last buffer timestamp */ 27699461Sobrien l_fp charstamp; /* character time as a l_fp */ 277130561Sobrien int errflg; /* error flags */ 278130561Sobrien int status; /* status bits */ 279130561Sobrien int bufptr; /* buffer index pointer */ 280130561Sobrien char ident[10]; /* transmitter frequency */ 281130561Sobrien#ifdef ICOM 282130561Sobrien int chan; /* frequency identifier */ 283130561Sobrien int dwell; /* dwell minutes at current frequency */ 284130561Sobrien int fd_icom; /* ICOM file descriptor */ 285130561Sobrien#endif /* ICOM */ 286130561Sobrien 287130561Sobrien /* 288130561Sobrien * Character burst variables 289130561Sobrien */ 290130561Sobrien int cbuf[BURST]; /* character buffer */ 291130561Sobrien int ntstamp; /* number of timestamp samples */ 292130561Sobrien int ndx; /* buffer start index */ 293130561Sobrien int prevsec; /* previous burst second */ 294130561Sobrien int burdist; /* burst distance */ 295130561Sobrien int mindist; /* minimum distance */ 296130561Sobrien int syndist; /* sync distance */ 297130561Sobrien int burstcnt; /* format A bursts this minute */ 298130561Sobrien 299130561Sobrien /* 300130561Sobrien * Format particulars 301130561Sobrien */ 302130561Sobrien int leap; /* leap/dut code */ 303130561Sobrien int dut; /* UTC1 correction */ 304130561Sobrien int tai; /* TAI - UTC correction */ 305130561Sobrien int dst; /* Canadian DST code */ 306130561Sobrien 307130561Sobrien#ifdef AUDIO_CHU 308130561Sobrien /* 309130561Sobrien * Audio codec variables 310130561Sobrien */ 311130561Sobrien double comp[SIZE]; /* decompanding table */ 312130561Sobrien int port; /* codec port */ 313130561Sobrien int gain; /* codec gain */ 314130561Sobrien int bufcnt; /* samples in buffer */ 315130561Sobrien int clipcnt; /* sample clip count */ 316130561Sobrien int seccnt; /* second interval counter */ 317130561Sobrien 318130561Sobrien /* 319130561Sobrien * Modem variables 320130561Sobrien */ 321130561Sobrien l_fp tick; /* audio sample increment */ 322130561Sobrien double bpf[9]; /* IIR bandpass filter */ 323130561Sobrien double disc[LAG]; /* discriminator shift register */ 324130561Sobrien double lpf[27]; /* FIR lowpass filter */ 325130561Sobrien double monitor; /* audio monitor */ 326130561Sobrien double maxsignal; /* signal level */ 32799461Sobrien int discptr; /* discriminator pointer */ 32899461Sobrien 32999461Sobrien /* 33099461Sobrien * Maximum likelihood UART variables 33199461Sobrien */ 33299461Sobrien double baud; /* baud interval */ 33399461Sobrien struct surv surv[8]; /* UART survivor structures */ 33499461Sobrien int decptr; /* decode pointer */ 33599461Sobrien int dbrk; /* holdoff counter */ 33699461Sobrien#endif /* AUDIO_CHU */ 337218822Sdim}; 338218822Sdim 339218822Sdim/* 340218822Sdim * Function prototypes 341218822Sdim */ 342218822Sdimstatic int chu_start P((int, struct peer *)); 343218822Sdimstatic void chu_shutdown P((int, struct peer *)); 344218822Sdimstatic void chu_receive P((struct recvbuf *)); 345218822Sdimstatic void chu_poll P((int, struct peer *)); 346218822Sdim 347218822Sdim/* 348218822Sdim * More function prototypes 349218822Sdim */ 350218822Sdimstatic void chu_decode P((struct peer *, int)); 351218822Sdimstatic void chu_burst P((struct peer *)); 352218822Sdimstatic void chu_clear P((struct peer *)); 353218822Sdimstatic void chu_a P((struct peer *, int)); 354218822Sdimstatic void chu_b P((struct peer *, int)); 355218822Sdimstatic int chu_dist P((int, int)); 35699461Sobrienstatic int chu_major P((struct peer *)); 35799461Sobrien#ifdef AUDIO_CHU 35899461Sobrienstatic void chu_uart P((struct surv *, double)); 35999461Sobrienstatic void chu_rf P((struct peer *, double)); 36099461Sobrienstatic void chu_gain P((struct peer *)); 36199461Sobrien#endif /* AUDIO_CHU */ 36299461Sobrien 36399461Sobrien/* 36499461Sobrien * Global variables 365218822Sdim */ 366218822Sdimstatic char hexchar[] = "0123456789abcdef_-="; 36799461Sobrien#ifdef ICOM 36899461Sobrienstatic double qsy[NCHAN] = {3.33, 7.335, 14.67}; /* frequencies (MHz) */ 36999461Sobrien#endif /* ICOM */ 37099461Sobrien 37199461Sobrien/* 37299461Sobrien * Transfer vector 37399461Sobrien */ 37499461Sobrienstruct refclock refclock_chu = { 37599461Sobrien chu_start, /* start up driver */ 37699461Sobrien chu_shutdown, /* shut down driver */ 377218822Sdim chu_poll, /* transmit poll message */ 378218822Sdim noentry, /* not used (old chu_control) */ 379218822Sdim noentry, /* initialize driver (not used) */ 380218822Sdim noentry, /* not used (old chu_buginfo) */ 381218822Sdim NOFLAGS /* not used */ 382218822Sdim}; 383218822Sdim 38499461Sobrien 38599461Sobrien/* 38699461Sobrien * chu_start - open the devices and initialize data for processing 387130561Sobrien */ 388130561Sobrienstatic int 389130561Sobrienchu_start( 390130561Sobrien int unit, /* instance number (not used) */ 391130561Sobrien struct peer *peer /* peer structure pointer */ 392130561Sobrien ) 393130561Sobrien{ 394130561Sobrien struct chuunit *up; 395130561Sobrien struct refclockproc *pp; 396130561Sobrien int fd; /* file descriptor */ 397130561Sobrien#ifdef ICOM 398130561Sobrien char tbuf[80]; /* trace buffer */ 399130561Sobrien int temp; 400130561Sobrien#endif /* ICOM */ 401130561Sobrien#ifdef AUDIO_CHU 402130561Sobrien int i; /* index */ 403130561Sobrien double step; /* codec adjustment */ 404130561Sobrien 405130561Sobrien /* 406130561Sobrien * Open audio device 407130561Sobrien */ 408130561Sobrien fd = audio_init(); 409130561Sobrien if (fd < 0) 410130561Sobrien return (0); 411130561Sobrien#ifdef DEBUG 412130561Sobrien if (debug) 413130561Sobrien audio_show(); 414130561Sobrien#endif 415130561Sobrien#else 416130561Sobrien char device[20]; /* device name */ 417130561Sobrien 418130561Sobrien /* 419130561Sobrien * Open serial port in raw mode. 420130561Sobrien */ 421130561Sobrien (void)sprintf(device, DEVICE, unit); 422130561Sobrien if (!(fd = refclock_open(device, SPEED232, LDISC_RAW))) { 423130561Sobrien return (0); 424130561Sobrien } 425130561Sobrien#endif /* AUDIO_CHU */ 426130561Sobrien 427130561Sobrien /* 428130561Sobrien * Allocate and initialize unit structure 429130561Sobrien */ 430130561Sobrien if (!(up = (struct chuunit *) 431130561Sobrien emalloc(sizeof(struct chuunit)))) { 432218822Sdim (void) close(fd); 433130561Sobrien return (0); 434130561Sobrien } 435130561Sobrien memset((char *)up, 0, sizeof(struct chuunit)); 436130561Sobrien pp = peer->procptr; 437130561Sobrien pp->unitptr = (caddr_t)up; 438130561Sobrien pp->io.clock_recv = chu_receive; 439130561Sobrien pp->io.srcclock = (caddr_t)peer; 440130561Sobrien pp->io.datalen = 0; 441130561Sobrien pp->io.fd = fd; 442130561Sobrien if (!io_addclock(&pp->io)) { 443130561Sobrien (void)close(fd); 444130561Sobrien free(up); 445130561Sobrien return (0); 446130561Sobrien } 447130561Sobrien 448130561Sobrien /* 449130561Sobrien * Initialize miscellaneous variables 450130561Sobrien */ 451130561Sobrien peer->precision = PRECISION; 452130561Sobrien pp->clockdesc = DESCRIPTION; 453130561Sobrien memcpy((char *)&pp->refid, REFID, 4); 454130561Sobrien DTOLFP(CHAR, &up->charstamp); 455130561Sobrien#ifdef AUDIO_CHU 456130561Sobrien up->gain = 127; 457130561Sobrien 45899461Sobrien /* 45999461Sobrien * The companded samples are encoded sign-magnitude. The table 46099461Sobrien * contains all the 256 values in the interest of speed. 46199461Sobrien */ 46299461Sobrien up->comp[0] = up->comp[OFFSET] = 0.; 463130561Sobrien up->comp[1] = 1; up->comp[OFFSET + 1] = -1.; 46499461Sobrien up->comp[2] = 3; up->comp[OFFSET + 2] = -3.; 46599461Sobrien step = 2.; 46699461Sobrien for (i = 3; i < OFFSET; i++) { 46799461Sobrien up->comp[i] = up->comp[i - 1] + step; 46899461Sobrien up->comp[OFFSET + i] = -up->comp[i]; 46999461Sobrien if (i % 16 == 0) 47099461Sobrien step *= 2.; 47199461Sobrien } 47299461Sobrien DTOLFP(1. / SECOND, &up->tick); 47399461Sobrien#endif /* AUDIO_CHU */ 47499461Sobrien strcpy(up->ident, "X"); 475130561Sobrien#ifdef ICOM 476130561Sobrien temp = 0; 477130561Sobrien#ifdef DEBUG 478130561Sobrien if (debug > 1) 479130561Sobrien temp = P_TRACE; 480130561Sobrien#endif 48199461Sobrien if (peer->ttl > 0) { 48299461Sobrien if (peer->ttl & 0x80) 48399461Sobrien up->fd_icom = icom_init("/dev/icom", B1200, 48499461Sobrien temp); 48599461Sobrien else 48699461Sobrien up->fd_icom = icom_init("/dev/icom", B9600, 48799461Sobrien temp); 48899461Sobrien } 48999461Sobrien if (up->fd_icom > 0) { 49099461Sobrien if (icom_freq(up->fd_icom, peer->ttl & 0x7f, 49199461Sobrien qsy[up->chan]) < 0) { 49299461Sobrien NLOG(NLOG_SYNCEVENT | NLOG_SYSEVENT) 49399461Sobrien msyslog(LOG_ERR, 49499461Sobrien "ICOM bus error; autotune disabled"); 49599461Sobrien up->errflg = CEVNT_FAULT; 49699461Sobrien close(up->fd_icom); 49799461Sobrien up->fd_icom = 0; 49899461Sobrien } else { 49999461Sobrien sprintf(up->ident, "%.1f", qsy[up->chan]); 50099461Sobrien sprintf(tbuf, "chu: QSY to %s MHz", up->ident); 50199461Sobrien record_clock_stats(&peer->srcadr, tbuf); 50299461Sobrien#ifdef DEBUG 50399461Sobrien if (debug) 50499461Sobrien printf("%s\n", tbuf); 505130561Sobrien#endif 50699461Sobrien } 50799461Sobrien } 50899461Sobrien#endif /* ICOM */ 50999461Sobrien return (1); 51099461Sobrien} 51199461Sobrien 51299461Sobrien 51399461Sobrien/* 51499461Sobrien * chu_shutdown - shut down the clock 51599461Sobrien */ 51699461Sobrienstatic void 51799461Sobrienchu_shutdown( 51899461Sobrien int unit, /* instance number (not used) */ 51999461Sobrien struct peer *peer /* peer structure pointer */ 52099461Sobrien ) 52199461Sobrien{ 52299461Sobrien struct chuunit *up; 52399461Sobrien struct refclockproc *pp; 52499461Sobrien 52599461Sobrien pp = peer->procptr; 52699461Sobrien up = (struct chuunit *)pp->unitptr; 52799461Sobrien io_closeclock(&pp->io); 52899461Sobrien if (up->fd_icom > 0) 52999461Sobrien close(up->fd_icom); 53099461Sobrien free(up); 53199461Sobrien} 53299461Sobrien 53399461Sobrien#ifdef AUDIO_CHU 53499461Sobrien 53599461Sobrien/* 53699461Sobrien * chu_receive - receive data from the audio device 53799461Sobrien */ 53899461Sobrienstatic void 53999461Sobrienchu_receive( 54099461Sobrien struct recvbuf *rbufp /* receive buffer structure pointer */ 54199461Sobrien ) 54299461Sobrien{ 54399461Sobrien struct chuunit *up; 54499461Sobrien struct refclockproc *pp; 54599461Sobrien struct peer *peer; 54699461Sobrien 54799461Sobrien double sample; /* codec sample */ 54899461Sobrien u_char *dpt; /* buffer pointer */ 54999461Sobrien l_fp ltemp; /* l_fp temp */ 55099461Sobrien int isneg; /* parity flag */ 55199461Sobrien double dtemp; 55299461Sobrien int i, j; 55399461Sobrien 55499461Sobrien peer = (struct peer *)rbufp->recv_srcclock; 55599461Sobrien pp = peer->procptr; 55699461Sobrien up = (struct chuunit *)pp->unitptr; 55799461Sobrien 55899461Sobrien /* 55999461Sobrien * Main loop - read until there ain't no more. Note codec 56099461Sobrien * samples are bit-inverted. 56199461Sobrien */ 56299461Sobrien up->timestamp = rbufp->recv_time; 56399461Sobrien up->bufcnt = rbufp->recv_length; 56499461Sobrien DTOLFP(up->bufcnt * 1. / SECOND, <emp); 56599461Sobrien L_SUB(&up->timestamp, <emp); 56699461Sobrien dpt = (u_char *)&rbufp->recv_space; 56799461Sobrien for (up->bufptr = 0; up->bufptr < up->bufcnt; up->bufptr++) { 56899461Sobrien sample = up->comp[~*dpt & 0xff]; 56999461Sobrien 57099461Sobrien /* 57199461Sobrien * Clip noise spikes greater than MAXSIG. If no clips, 57299461Sobrien * increase the gain a tad; if the clips are too high, 57399461Sobrien * decrease a tad. 57499461Sobrien */ 57599461Sobrien if (sample > MAXSIG) { 57699461Sobrien sample = MAXSIG; 57799461Sobrien up->clipcnt++; 57899461Sobrien } else if (sample < -MAXSIG) { 57999461Sobrien sample = -MAXSIG; 58099461Sobrien up->clipcnt++; 58199461Sobrien } 58299461Sobrien up->seccnt = (up->seccnt + 1) % SECOND; 58399461Sobrien if (up->seccnt == 0) { 58499461Sobrien if (pp->sloppyclockflag & CLK_FLAG2) 58599461Sobrien up->port = 2; 58699461Sobrien else 58799461Sobrien up->port = 1; 58899461Sobrien chu_gain(peer); 58999461Sobrien } 59099461Sobrien chu_rf(peer, sample); 59199461Sobrien 59299461Sobrien /* 59399461Sobrien * During development, it is handy to have an audio 59499461Sobrien * monitor that can be switched to various signals. This 59599461Sobrien * code converts the linear signal left in up->monitor 59699461Sobrien * to codec format. If we can get the grass out of this 59799461Sobrien * thing and improve modem performance, this expensive 59899461Sobrien * code will be permanently nixed. 59999461Sobrien */ 60099461Sobrien isneg = 0; 60199461Sobrien dtemp = up->monitor; 60299461Sobrien if (sample < 0) { 60399461Sobrien isneg = 1; 60499461Sobrien dtemp-= dtemp; 60599461Sobrien } 60699461Sobrien i = 0; 60799461Sobrien j = OFFSET >> 1; 60899461Sobrien while (j != 0) { 60999461Sobrien if (dtemp > up->comp[i]) 61099461Sobrien i += j; 61199461Sobrien else if (dtemp < up->comp[i]) 61299461Sobrien i -= j; 61399461Sobrien else 61499461Sobrien break; 61599461Sobrien j >>= 1; 61699461Sobrien } 61799461Sobrien if (isneg) 61899461Sobrien *dpt = ~(i + OFFSET); 61999461Sobrien else 62099461Sobrien *dpt = ~i; 62199461Sobrien dpt++; 62299461Sobrien L_ADD(&up->timestamp, &up->tick); 62399461Sobrien } 62499461Sobrien 62599461Sobrien /* 62699461Sobrien * Squawk to the monitor speaker if enabled. 62799461Sobrien */ 62899461Sobrien if (pp->sloppyclockflag & CLK_FLAG3) 62999461Sobrien if (write(pp->io.fd, (u_char *)&rbufp->recv_space, 63099461Sobrien (u_int)up->bufcnt) < 0) 63199461Sobrien perror("chu:"); 63299461Sobrien} 63399461Sobrien 63499461Sobrien 63599461Sobrien/* 63699461Sobrien * chu_rf - filter and demodulate the FSK signal 63799461Sobrien * 63899461Sobrien * This routine implements a 300-baud Bell 103 modem with mark 2225 Hz 63999461Sobrien * and space 2025 Hz. It uses a bandpass filter followed by a soft 64099461Sobrien * limiter, FM discriminator and lowpass filter. A maximum likelihood 64199461Sobrien * decoder samples the baseband signal at eight times the baud rate and 64299461Sobrien * detects the start bit of each character. 64399461Sobrien * 64499461Sobrien * The filters are built for speed, which explains the rather clumsy 64599461Sobrien * code. Hopefully, the compiler will efficiently implement the move- 64699461Sobrien * and-muiltiply-and-add operations. 64799461Sobrien */ 64899461Sobrienstatic void 64999461Sobrienchu_rf( 65099461Sobrien struct peer *peer, /* peer structure pointer */ 65199461Sobrien double sample /* analog sample */ 65299461Sobrien ) 65399461Sobrien{ 65499461Sobrien struct refclockproc *pp; 65599461Sobrien struct chuunit *up; 65699461Sobrien struct surv *sp; 65799461Sobrien 65899461Sobrien /* 65999461Sobrien * Local variables 66099461Sobrien */ 66199461Sobrien double signal; /* bandpass signal */ 662130561Sobrien double limit; /* limiter signal */ 663130561Sobrien double disc; /* discriminator signal */ 664130561Sobrien double lpf; /* lowpass signal */ 665130561Sobrien double span; /* UART signal span */ 666130561Sobrien double dist; /* UART signal distance */ 667130561Sobrien int i, j; 668130561Sobrien 669130561Sobrien pp = peer->procptr; 670130561Sobrien up = (struct chuunit *)pp->unitptr; 671130561Sobrien 67299461Sobrien /* 67399461Sobrien * Bandpass filter. 4th-order elliptic, 500-Hz bandpass centered 674130561Sobrien * at 2125 Hz. Passband ripple 0.3 dB, stopband ripple 50 dB. 675130561Sobrien */ 676130561Sobrien signal = (up->bpf[8] = up->bpf[7]) * 5.844676e-01; 677130561Sobrien signal += (up->bpf[7] = up->bpf[6]) * 4.884860e-01; 678130561Sobrien signal += (up->bpf[6] = up->bpf[5]) * 2.704384e+00; 679130561Sobrien signal += (up->bpf[5] = up->bpf[4]) * 1.645032e+00; 680130561Sobrien signal += (up->bpf[4] = up->bpf[3]) * 4.644557e+00; 681130561Sobrien signal += (up->bpf[3] = up->bpf[2]) * 1.879165e+00; 682130561Sobrien signal += (up->bpf[2] = up->bpf[1]) * 3.522634e+00; 683130561Sobrien signal += (up->bpf[1] = up->bpf[0]) * 7.315738e-01; 684130561Sobrien up->bpf[0] = sample - signal; 685130561Sobrien signal = up->bpf[0] * 6.176213e-03 686130561Sobrien + up->bpf[1] * 3.156599e-03 687130561Sobrien + up->bpf[2] * 7.567487e-03 688130561Sobrien + up->bpf[3] * 4.344580e-03 689130561Sobrien + up->bpf[4] * 1.190128e-02 690130561Sobrien + up->bpf[5] * 4.344580e-03 691130561Sobrien + up->bpf[6] * 7.567487e-03 692218822Sdim + up->bpf[7] * 3.156599e-03 693130561Sobrien + up->bpf[8] * 6.176213e-03; 694130561Sobrien 695218822Sdim up->monitor = signal / 4.; /* note monitor after filter */ 696218822Sdim 697218822Sdim /* 698218822Sdim * Soft limiter/discriminator. The 11-sample discriminator lag 699218822Sdim * interval corresponds to three cycles of 2125 Hz, which 700218822Sdim * requires the sample frequency to be 2125 * 11 / 3 = 7791.7 701218822Sdim * Hz. The discriminator output varies +-0.5 interval for input 702130561Sobrien * frequency 2025-2225 Hz. However, we don't get to sample at 703130561Sobrien * this frequency, so the discriminator output is biased. Life 704130561Sobrien * at 8000 Hz sucks. 705130561Sobrien */ 706130561Sobrien limit = signal; 707130561Sobrien if (limit > LIMIT) 708130561Sobrien limit = LIMIT; 709130561Sobrien else if (limit < -LIMIT) 710130561Sobrien limit = -LIMIT; 711130561Sobrien disc = up->disc[up->discptr] * -limit; 71299461Sobrien up->disc[up->discptr] = limit; 71399461Sobrien up->discptr = (up->discptr + 1 ) % LAG; 71499461Sobrien if (disc >= 0) 71599461Sobrien disc = sqrt(disc); 71699461Sobrien else 71799461Sobrien disc = -sqrt(-disc); 71899461Sobrien 71999461Sobrien /* 72099461Sobrien * Lowpass filter. Raised cosine, Ts = 1 / 300, beta = 0.1. 72199461Sobrien */ 72299461Sobrien lpf = (up->lpf[26] = up->lpf[25]) * 2.538771e-02; 72399461Sobrien lpf += (up->lpf[25] = up->lpf[24]) * 1.084671e-01; 72499461Sobrien lpf += (up->lpf[24] = up->lpf[23]) * 2.003159e-01; 72599461Sobrien lpf += (up->lpf[23] = up->lpf[22]) * 2.985303e-01; 72699461Sobrien lpf += (up->lpf[22] = up->lpf[21]) * 4.003697e-01; 727130561Sobrien lpf += (up->lpf[21] = up->lpf[20]) * 5.028552e-01; 728130561Sobrien lpf += (up->lpf[20] = up->lpf[19]) * 6.028795e-01; 729130561Sobrien lpf += (up->lpf[19] = up->lpf[18]) * 6.973249e-01; 730130561Sobrien lpf += (up->lpf[18] = up->lpf[17]) * 7.831828e-01; 731130561Sobrien lpf += (up->lpf[17] = up->lpf[16]) * 8.576717e-01; 73299461Sobrien lpf += (up->lpf[16] = up->lpf[15]) * 9.183463e-01; 73399461Sobrien lpf += (up->lpf[15] = up->lpf[14]) * 9.631951e-01; 73499461Sobrien lpf += (up->lpf[14] = up->lpf[13]) * 9.907208e-01; 73599461Sobrien lpf += (up->lpf[13] = up->lpf[12]) * 1.000000e+00; 73699461Sobrien lpf += (up->lpf[12] = up->lpf[11]) * 9.907208e-01; 73799461Sobrien lpf += (up->lpf[11] = up->lpf[10]) * 9.631951e-01; 73899461Sobrien lpf += (up->lpf[10] = up->lpf[9]) * 9.183463e-01; 73999461Sobrien lpf += (up->lpf[9] = up->lpf[8]) * 8.576717e-01; 74099461Sobrien lpf += (up->lpf[8] = up->lpf[7]) * 7.831828e-01; 74199461Sobrien lpf += (up->lpf[7] = up->lpf[6]) * 6.973249e-01; 74299461Sobrien lpf += (up->lpf[6] = up->lpf[5]) * 6.028795e-01; 74399461Sobrien lpf += (up->lpf[5] = up->lpf[4]) * 5.028552e-01; 74499461Sobrien lpf += (up->lpf[4] = up->lpf[3]) * 4.003697e-01; 74599461Sobrien lpf += (up->lpf[3] = up->lpf[2]) * 2.985303e-01; 74699461Sobrien lpf += (up->lpf[2] = up->lpf[1]) * 2.003159e-01; 74799461Sobrien lpf += (up->lpf[1] = up->lpf[0]) * 1.084671e-01; 74899461Sobrien lpf += up->lpf[0] = disc * 2.538771e-02; 74999461Sobrien 75099461Sobrien /* 75199461Sobrien * Maximum likelihood decoder. The UART updates each of the 75299461Sobrien * eight survivors and determines the span, slice level and 75399461Sobrien * tentative decoded character. Valid 11-bit characters are 75499461Sobrien * framed so that bit 1 and bit 11 (stop bits) are mark and bit 75599461Sobrien * 2 (start bit) is space. When a valid character is found, the 75699461Sobrien * survivor with maximum distance determines the final decoded 75799461Sobrien * character. 75899461Sobrien */ 75999461Sobrien up->baud += 1. / SECOND; 76099461Sobrien if (up->baud > 1. / (BAUD * 8.)) { 76199461Sobrien up->baud -= 1. / (BAUD * 8.); 76299461Sobrien sp = &up->surv[up->decptr]; 76399461Sobrien span = sp->es_max - sp->es_min; 76499461Sobrien up->maxsignal += (span - up->maxsignal) / 80.; 76599461Sobrien if (up->dbrk > 0) { 76699461Sobrien up->dbrk--; 767130561Sobrien } else if ((sp->uart & 0x403) == 0x401 && span > 1000.) 768130561Sobrien { 76999461Sobrien dist = 0; 77099461Sobrien j = 0; 77199461Sobrien for (i = 0; i < 8; i++) { 77299461Sobrien if (up->surv[i].dist > dist) { 77399461Sobrien dist = up->surv[i].dist; 77499461Sobrien j = i; 77599461Sobrien } 77699461Sobrien } 77799461Sobrien chu_decode(peer, (up->surv[j].uart >> 2) & 77899461Sobrien 0xff); 77999461Sobrien up->dbrk = 80; 78099461Sobrien } 78199461Sobrien up->decptr = (up->decptr + 1) % 8; 78299461Sobrien chu_uart(sp, -lpf * AGAIN); 783107492Sobrien } 78499461Sobrien} 78599461Sobrien 78699461Sobrien 787218822Sdim/* 788218822Sdim * chu_uart - maximum likelihood UART 78999461Sobrien * 790107492Sobrien * This routine updates a shift register holding the last 11 envelope 79199461Sobrien * samples. It then computes the slice level and span over these samples 79299461Sobrien * and determines the tentative data bits and distance. The calling 79399461Sobrien * program selects over the last eight survivors the one with maximum 79499461Sobrien * distance to determine the decoded character. 79599461Sobrien */ 79699461Sobrienstatic void 79799461Sobrienchu_uart( 79899461Sobrien struct surv *sp, /* survivor structure pointer */ 79999461Sobrien double sample /* baseband signal */ 80099461Sobrien ) 801130561Sobrien{ 80299461Sobrien double es_max, es_min; /* max/min envelope */ 80399461Sobrien double slice; /* slice level */ 80499461Sobrien double dist; /* distance */ 80599461Sobrien double dtemp; 80699461Sobrien int i; 80799461Sobrien 80899461Sobrien /* 80999461Sobrien * Save the sample and shift right. At the same time, measure 810130561Sobrien * the maximum and minimum over all eleven samples. 81199461Sobrien */ 81299461Sobrien es_max = -1e6; 81399461Sobrien es_min = 1e6; 81499461Sobrien sp->shift[0] = sample; 81599461Sobrien for (i = 11; i > 0; i--) { 81699461Sobrien sp->shift[i] = sp->shift[i - 1]; 81799461Sobrien if (sp->shift[i] > es_max) 818130561Sobrien es_max = sp->shift[i]; 81999461Sobrien if (sp->shift[i] < es_min) 82099461Sobrien es_min = sp->shift[i]; 82199461Sobrien } 82299461Sobrien 82399461Sobrien /* 82499461Sobrien * Determine the slice level midway beteen the maximum and 82599461Sobrien * minimum and the span as the maximum less the minimum. Compute 826218822Sdim * the distance on the assumption the first and last bits must 827218822Sdim * be mark, the second space and the rest either mark or space. 828218822Sdim */ 829218822Sdim slice = (es_max + es_min) / 2.; 830218822Sdim dist = 0; 831218822Sdim sp->uart = 0; 83299461Sobrien for (i = 1; i < 12; i++) { 83399461Sobrien sp->uart <<= 1; 834130561Sobrien dtemp = sp->shift[i]; 835130561Sobrien if (dtemp > slice) 83699461Sobrien sp->uart |= 0x1; 83799461Sobrien if (i == 1 || i == 11) { 83899461Sobrien dist += dtemp - es_min; 83999461Sobrien } else if (i == 10) { 84099461Sobrien dist += es_max - dtemp; 84199461Sobrien } else { 842130561Sobrien if (dtemp > slice) 84399461Sobrien dist += dtemp - es_min; 84499461Sobrien else 84599461Sobrien dist += es_max - dtemp; 84699461Sobrien } 84799461Sobrien } 84899461Sobrien sp->es_max = es_max; 84999461Sobrien sp->es_min = es_min; 85099461Sobrien sp->dist = dist / (11 * (es_max - es_min)); 851130561Sobrien} 85299461Sobrien 85399461Sobrien 854130561Sobrien#else /* AUDIO_CHU */ 85599461Sobrien/* 85699461Sobrien * chu_receive - receive data from the serial interface 85799461Sobrien */ 85899461Sobrienstatic void 85999461Sobrienchu_receive( 86099461Sobrien struct recvbuf *rbufp /* receive buffer structure pointer */ 86199461Sobrien ) 86299461Sobrien{ 86399461Sobrien struct chuunit *up; 86499461Sobrien struct refclockproc *pp; 86599461Sobrien struct peer *peer; 866130561Sobrien 86799461Sobrien u_char *dpt; /* receive buffer pointer */ 86899461Sobrien 86999461Sobrien peer = (struct peer *)rbufp->recv_srcclock; 87099461Sobrien pp = peer->procptr; 87199461Sobrien up = (struct chuunit *)pp->unitptr; 872218822Sdim 873218822Sdim /* 87499461Sobrien * Initialize pointers and read the timecode and timestamp. 87599461Sobrien */ 87699461Sobrien up->timestamp = rbufp->recv_time; 87799461Sobrien dpt = (u_char *)&rbufp->recv_space; 87899461Sobrien chu_decode(peer, *dpt); 87999461Sobrien} 88099461Sobrien#endif /* AUDIO_CHU */ 88199461Sobrien 88299461Sobrien 88399461Sobrien/* 88499461Sobrien * chu_decode - decode the data 88599461Sobrien */ 88699461Sobrienstatic void 88799461Sobrienchu_decode( 888218822Sdim struct peer *peer, /* peer structure pointer */ 88999461Sobrien int hexhex /* data character */ 89099461Sobrien ) 89199461Sobrien{ 89299461Sobrien struct refclockproc *pp; 89399461Sobrien struct chuunit *up; 89499461Sobrien 89599461Sobrien l_fp tstmp; /* timestamp temp */ 89699461Sobrien double dtemp; 89799461Sobrien 89899461Sobrien pp = peer->procptr; 89999461Sobrien up = (struct chuunit *)pp->unitptr; 90099461Sobrien 90199461Sobrien /* 90299461Sobrien * If the interval since the last character is greater than the 90399461Sobrien * longest burst, process the last burst and start a new one. If 90499461Sobrien * the interval is less than this but greater than two 90599461Sobrien * characters, consider this a noise burst and reject it. 90699461Sobrien */ 90799461Sobrien tstmp = up->timestamp; 90899461Sobrien if (L_ISZERO(&up->laststamp)) 90999461Sobrien up->laststamp = up->timestamp; 91099461Sobrien L_SUB(&tstmp, &up->laststamp); 91199461Sobrien up->laststamp = up->timestamp; 912130561Sobrien LFPTOD(&tstmp, dtemp); 913130561Sobrien if (dtemp > BURST * CHAR) { 914130561Sobrien chu_burst(peer); 915130561Sobrien up->ndx = 0; 916130561Sobrien } else if (dtemp > 2.5 * CHAR) { 917130561Sobrien up->ndx = 0; 918130561Sobrien } 919130561Sobrien 920130561Sobrien /* 921218822Sdim * Append the character to the current burst and append the 922218822Sdim * timestamp to the timestamp list. 923218822Sdim */ 924218822Sdim if (up->ndx < BURST) { 925218822Sdim up->cbuf[up->ndx] = hexhex & 0xff; 926218822Sdim up->cstamp[up->ndx] = up->timestamp; 927218822Sdim up->ndx++; 928218822Sdim 929218822Sdim } 930218822Sdim} 931130561Sobrien 932218822Sdim 93399461Sobrien/* 93499461Sobrien * chu_burst - search for valid burst format 935130561Sobrien */ 936130561Sobrienstatic void 937130561Sobrienchu_burst( 938130561Sobrien struct peer *peer 939130561Sobrien ) 940130561Sobrien{ 941130561Sobrien struct chuunit *up; 942130561Sobrien struct refclockproc *pp; 943130561Sobrien 944130561Sobrien int i; 945130561Sobrien 946130561Sobrien pp = peer->procptr; 947130561Sobrien up = (struct chuunit *)pp->unitptr; 948130561Sobrien 949130561Sobrien /* 950130561Sobrien * Correlate a block of five characters with the next block of 951130561Sobrien * five characters. The burst distance is defined as the number 952130561Sobrien * of bits that match in the two blocks for format A and that 953130561Sobrien * match the inverse for format B. 954130561Sobrien */ 955130561Sobrien if (up->ndx < MINCHAR) { 956130561Sobrien up->status |= RUNT; 957130561Sobrien return; 958130561Sobrien } 959130561Sobrien up->burdist = 0; 960130561Sobrien for (i = 0; i < 5 && i < up->ndx - 5; i++) 961130561Sobrien up->burdist += chu_dist(up->cbuf[i], up->cbuf[i + 5]); 96299461Sobrien 96399461Sobrien /* 96499461Sobrien * If the burst distance is at least MINDIST, this must be a 96599461Sobrien * format A burst; if the value is not greater than -MINDIST, it 966130561Sobrien * must be a format B burst. If the B burst is perfect, we 96799461Sobrien * believe it; otherwise, it is a noise burst and of no use to 96899461Sobrien * anybody. 96999461Sobrien */ 97099461Sobrien if (up->burdist >= MINDIST) { 97199461Sobrien chu_a(peer, up->ndx); 97299461Sobrien } else if (up->burdist <= -MINDIST) { 97399461Sobrien chu_b(peer, up->ndx); 97499461Sobrien } else { 97599461Sobrien up->status |= NOISE; 97699461Sobrien return; 97799461Sobrien } 97899461Sobrien 979130561Sobrien /* 980130561Sobrien * If this is a valid burst, wait a guard time of ten seconds to 98199461Sobrien * allow for more bursts, then arm the poll update routine to 982130561Sobrien * process the minute. Don't do this if this is called from the 983130561Sobrien * timer interrupt routine. 98499461Sobrien */ 98599461Sobrien if (peer->outdate != current_time) 98699461Sobrien peer->nextdate = current_time + 10; 98799461Sobrien} 988130561Sobrien 98999461Sobrien 99099461Sobrien/* 99199461Sobrien * chu_b - decode format B burst 99299461Sobrien */ 99399461Sobrienstatic void 99499461Sobrienchu_b( 995130561Sobrien struct peer *peer, 99699461Sobrien int nchar 99799461Sobrien ) 99899461Sobrien{ 99999461Sobrien struct refclockproc *pp; 100099461Sobrien struct chuunit *up; 100199461Sobrien 100299461Sobrien u_char code[11]; /* decoded timecode */ 1003218822Sdim char tbuf[80]; /* trace buffer */ 1004218822Sdim l_fp offset; /* timestamp offset */ 1005130561Sobrien int i; 100699461Sobrien 100799461Sobrien pp = peer->procptr; 100899461Sobrien up = (struct chuunit *)pp->unitptr; 100999461Sobrien 101099461Sobrien /* 1011218822Sdim * In a format B burst, a character is considered valid only if 1012218822Sdim * the first occurrence matches the last occurrence. The burst 1013218822Sdim * is considered valid only if all characters are valid; that 1014218822Sdim * is, only if the distance is 40. 1015218822Sdim */ 1016218822Sdim sprintf(tbuf, "chuB %04x %2d %2d ", up->status, nchar, 101799461Sobrien -up->burdist); 1018130561Sobrien for (i = 0; i < nchar; i++) 1019130561Sobrien sprintf(&tbuf[strlen(tbuf)], "%02x", 1020130561Sobrien up->cbuf[i]); 1021130561Sobrien if (pp->sloppyclockflag & CLK_FLAG4) 1022130561Sobrien record_clock_stats(&peer->srcadr, tbuf); 1023130561Sobrien#ifdef DEBUG 102499461Sobrien if (debug) 102599461Sobrien printf("%s\n", tbuf); 1026130561Sobrien#endif 1027130561Sobrien if (up->burdist > -40) { 102899461Sobrien up->status |= BFRAME; 102999461Sobrien return; 1030130561Sobrien } 1031130561Sobrien up->status |= INYEAR; 1032130561Sobrien 1033130561Sobrien /* 1034130561Sobrien * Convert the burst data to internal format. If this succeeds, 1035130561Sobrien * save the timestamps for later. 1036130561Sobrien */ 1037130561Sobrien for (i = 0; i < 5; i++) { 1038130561Sobrien code[2 * i] = hexchar[up->cbuf[i] & 0xf]; 1039130561Sobrien code[2 * i + 1] = hexchar[(up->cbuf[i] >> 1040130561Sobrien 4) & 0xf]; 1041130561Sobrien } 1042130561Sobrien if (sscanf((char *)code, "%1x%1d%4d%2d%2x", &up->leap, &up->dut, 1043130561Sobrien &pp->year, &up->tai, &up->dst) != 5) { 104499461Sobrien up->status |= BFORMAT; 1045130561Sobrien return; 104699461Sobrien } 1047130561Sobrien if (up->leap & 0x8) 1048130561Sobrien up->dut = -up->dut; 1049130561Sobrien offset.l_ui = 31; 1050130561Sobrien offset.l_f = 0; 105199461Sobrien for (i = 0; i < nchar && i < 10; i++) { 1052130561Sobrien up->tstamp[up->ntstamp] = up->cstamp[i]; 1053130561Sobrien L_SUB(&up->tstamp[up->ntstamp], &offset); 1054130561Sobrien L_ADD(&offset, &up->charstamp); 1055130561Sobrien if (up->ntstamp < MAXSTAGE) 105699461Sobrien up->ntstamp++; 1057130561Sobrien } 1058130561Sobrien} 1059130561Sobrien 106099461Sobrien 106199461Sobrien/* 106299461Sobrien * chu_a - decode format A burst 106399461Sobrien */ 106499461Sobrienstatic void 106599461Sobrienchu_a( 106699461Sobrien struct peer *peer, 1067130561Sobrien int nchar 106899461Sobrien ) 1069130561Sobrien{ 1070130561Sobrien struct refclockproc *pp; 1071130561Sobrien struct chuunit *up; 1072130561Sobrien 1073130561Sobrien char tbuf[80]; /* trace buffer */ 1074130561Sobrien l_fp offset; /* timestamp offset */ 1075130561Sobrien int val; /* distance */ 1076130561Sobrien int temp; 1077130561Sobrien int i, j, k; 107899461Sobrien 107999461Sobrien pp = peer->procptr; 1080130561Sobrien up = (struct chuunit *)pp->unitptr; 1081130561Sobrien 108299461Sobrien /* 1083130561Sobrien * Determine correct burst phase. There are three cases 1084130561Sobrien * corresponding to in-phase, one character early or one 108599461Sobrien * character late. These cases are distinguished by the position 1086130561Sobrien * of the framing digits x6 at positions 0 and 5 and x3 at 1087130561Sobrien * positions 4 and 9. The correct phase is when the distance 1088130561Sobrien * relative to the framing digits is maximum. The burst is valid 1089130561Sobrien * only if the maximum distance is at least MINSYNC. 1090130561Sobrien */ 109199461Sobrien up->syndist = k = 0; 109299461Sobrien val = -16; 1093130561Sobrien for (i = -1; i < 2; i++) { 1094130561Sobrien temp = up->cbuf[i + 4] & 0xf; 1095130561Sobrien if (i >= 0) 1096218822Sdim temp |= (up->cbuf[i] & 0xf) << 4; 1097130561Sobrien val = chu_dist(temp, 0x63); 1098130561Sobrien temp = (up->cbuf[i + 5] & 0xf) << 4; 1099130561Sobrien if (i + 9 < nchar) 110099461Sobrien temp |= up->cbuf[i + 9] & 0xf; 1101130561Sobrien val += chu_dist(temp, 0x63); 1102130561Sobrien if (val > up->syndist) { 1103130561Sobrien up->syndist = val; 1104130561Sobrien k = i; 1105130561Sobrien } 1106130561Sobrien } 1107130561Sobrien temp = (up->cbuf[k + 4] >> 4) & 0xf; 1108130561Sobrien if (temp > 9 || k + 9 >= nchar || temp != ((up->cbuf[k + 9] >> 1109130561Sobrien 4) & 0xf)) 1110130561Sobrien temp = 0; 1111130561Sobrien#ifdef AUDIO_CHU 1112130561Sobrien sprintf(tbuf, "chuA %04x %4.0f %2d %2d %2d %2d %1d ", 1113130561Sobrien up->status, up->maxsignal, nchar, up->burdist, k, 1114130561Sobrien up->syndist, temp); 1115130561Sobrien#else 1116130561Sobrien sprintf(tbuf, "chuA %04x %2d %2d %2d %2d %1d ", up->status, 1117218822Sdim nchar, up->burdist, k, up->syndist, temp); 1118130561Sobrien#endif /* AUDIO_CHU */ 1119130561Sobrien for (i = 0; i < nchar; i++) 1120130561Sobrien sprintf(&tbuf[strlen(tbuf)], "%02x", 1121130561Sobrien up->cbuf[i]); 112299461Sobrien if (pp->sloppyclockflag & CLK_FLAG4) 112399461Sobrien record_clock_stats(&peer->srcadr, tbuf); 1124130561Sobrien#ifdef DEBUG 1125130561Sobrien if (debug) 1126130561Sobrien printf("%s\n", tbuf); 1127130561Sobrien#endif 1128130561Sobrien if (up->syndist < MINSYNC) { 1129130561Sobrien up->status |= AFRAME; 1130130561Sobrien return; 1131130561Sobrien } 1132130561Sobrien 1133130561Sobrien /* 1134130561Sobrien * A valid burst requires the first seconds number to match the 1135130561Sobrien * last seconds number. If so, the burst timestamps are 1136130561Sobrien * corrected to the current minute and saved for later 1137130561Sobrien * processing. In addition, the seconds decode is advanced from 1138130561Sobrien * the previous burst to the current one. 1139130561Sobrien */ 1140130561Sobrien if (temp != 0) { 1141130561Sobrien offset.l_ui = 30 + temp; 1142130561Sobrien offset.l_f = 0; 1143130561Sobrien i = 0; 1144130561Sobrien if (k < 0) 1145130561Sobrien offset = up->charstamp; 1146130561Sobrien else if (k > 0) 1147130561Sobrien i = 1; 1148130561Sobrien for (; i < nchar && i < k + 10; i++) { 1149130561Sobrien up->tstamp[up->ntstamp] = up->cstamp[i]; 1150130561Sobrien L_SUB(&up->tstamp[up->ntstamp], &offset); 1151130561Sobrien L_ADD(&offset, &up->charstamp); 1152130561Sobrien if (up->ntstamp < MAXSTAGE) 1153130561Sobrien up->ntstamp++; 1154130561Sobrien } 1155130561Sobrien while (temp > up->prevsec) { 1156130561Sobrien for (j = 15; j > 0; j--) { 1157130561Sobrien up->decode[9][j] = up->decode[9][j - 1]; 1158130561Sobrien up->decode[19][j] = 1159130561Sobrien up->decode[19][j - 1]; 1160130561Sobrien } 1161130561Sobrien up->decode[9][j] = up->decode[19][j] = 0; 1162130561Sobrien up->prevsec++; 1163130561Sobrien } 1164130561Sobrien } 1165130561Sobrien i = -(2 * k); 1166130561Sobrien for (j = 0; j < nchar; j++) { 1167130561Sobrien if (i < 0 || i > 19) { 1168130561Sobrien i += 2; 1169130561Sobrien continue; 1170130561Sobrien } 1171130561Sobrien up->decode[i][up->cbuf[j] & 0xf]++; 1172130561Sobrien i++; 1173130561Sobrien up->decode[i][(up->cbuf[j] >> 4) & 0xf]++; 1174130561Sobrien i++; 1175130561Sobrien } 1176130561Sobrien up->burstcnt++; 1177130561Sobrien} 1178130561Sobrien 1179130561Sobrien 1180130561Sobrien/* 1181130561Sobrien * chu_poll - called by the transmit procedure 1182130561Sobrien */ 1183130561Sobrienstatic void 1184130561Sobrienchu_poll( 1185218822Sdim int unit, 1186218822Sdim struct peer *peer /* peer structure pointer */ 1187130561Sobrien ) 1188130561Sobrien{ 1189130561Sobrien struct refclockproc *pp; 1190130561Sobrien struct chuunit *up; 1191130561Sobrien char synchar, qual, leapchar; 1192130561Sobrien int minset; 1193130561Sobrien int temp; 1194130561Sobrien#ifdef ICOM 1195130561Sobrien char tbuf[80]; /* trace buffer */ 1196130561Sobrien#endif /* ICOM */ 1197130561Sobrien pp = peer->procptr; 1198130561Sobrien up = (struct chuunit *)pp->unitptr; 1199130561Sobrien if (pp->coderecv == pp->codeproc) 1200130561Sobrien up->errflg = CEVNT_TIMEOUT; 1201130561Sobrien else 1202130561Sobrien pp->polls++; 1203130561Sobrien minset = ((current_time - peer->update) + 30) / 60; 1204130561Sobrien if (up->status & INSYNC) { 1205130561Sobrien if (minset > PANIC) 1206130561Sobrien up->status = 0; 1207130561Sobrien else 1208130561Sobrien peer->reach |= 1; 1209130561Sobrien } 1210130561Sobrien 1211130561Sobrien /* 1212130561Sobrien * Process the last burst, if still in the burst buffer. 121399461Sobrien * Don't mess with anything if nothing has been heard. 1214130561Sobrien */ 1215130561Sobrien chu_burst(peer); 121699461Sobrien#ifdef ICOM 121799461Sobrien if (up->burstcnt > 2) { 121899461Sobrien up->dwell = 0; 121999461Sobrien } else if (up->dwell < DWELL) { 122099461Sobrien up->dwell++; 122199461Sobrien } else if (up->fd_icom > 0) { 122299461Sobrien up->dwell = 0; 122399461Sobrien up->chan = (up->chan + 1) % NCHAN; 122499461Sobrien icom_freq(up->fd_icom, peer->ttl & 0x7f, qsy[up->chan]); 122599461Sobrien sprintf(up->ident, "%.3f", qsy[up->chan]); 1226218822Sdim sprintf(tbuf, "chu: QSY to %s MHz", up->ident); 122799461Sobrien record_clock_stats(&peer->srcadr, tbuf); 122899461Sobrien#ifdef DEBUG 122999461Sobrien if (debug) 123099461Sobrien printf("%s\n", tbuf); 123199461Sobrien#endif 123299461Sobrien } 123399461Sobrien#endif /* ICOM */ 123499461Sobrien if (up->burstcnt == 0) 123599461Sobrien return; 123699461Sobrien temp = chu_major(peer); 123799461Sobrien if (up->status & INYEAR) 123899461Sobrien up->status |= INSYNC; 123999461Sobrien qual = 0; 124099461Sobrien if (up->status & (BFRAME | AFRAME)) 124199461Sobrien qual |= SYNERR; 124299461Sobrien if (up->status & (BFORMAT | AFORMAT)) 124399461Sobrien qual |= FMTERR; 124499461Sobrien if (up->status & DECODE) 124599461Sobrien qual |= DECERR; 124699461Sobrien if (up->status & STAMP) 124799461Sobrien qual |= TSPERR; 124899461Sobrien synchar = leapchar = ' '; 124999461Sobrien if (!(up->status & INSYNC)) { 125099461Sobrien pp->leap = LEAP_NOTINSYNC; 125199461Sobrien synchar = '?'; 125299461Sobrien } else if (up->leap & 0x2) { 125399461Sobrien pp->leap = LEAP_ADDSECOND; 125499461Sobrien leapchar = 'L'; 125599461Sobrien } else { 125699461Sobrien pp->leap = LEAP_NOWARNING; 125799461Sobrien } 125899461Sobrien#ifdef AUDIO_CHU 125999461Sobrien sprintf(pp->a_lastcode, 126099461Sobrien "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %d %s %d %d %d %d", 126199461Sobrien synchar, qual, pp->year, pp->day, pp->hour, pp->minute, 126299461Sobrien pp->second, leapchar, up->dst, up->dut, minset, up->gain, 1263218822Sdim up->ident, up->tai, up->burstcnt, up->mindist, up->ntstamp); 1264130561Sobrien#else 1265130561Sobrien sprintf(pp->a_lastcode, 126699461Sobrien "%c%1X %4d %3d %02d:%02d:%02d.000 %c%x %+d %d %s %d %d %d %d", 126799461Sobrien synchar, qual, pp->year, pp->day, pp->hour, pp->minute, 126899461Sobrien pp->second, leapchar, up->dst, up->dut, minset, 1269218822Sdim up->ident, up->tai, up->burstcnt, up->mindist, up->ntstamp); 127099461Sobrien#endif /* AUDIO_CHU */ 127199461Sobrien pp->lencode = strlen(pp->a_lastcode); 127299461Sobrien 127399461Sobrien /* 127499461Sobrien * If timestamps have been stuffed, the timecode is ipso fatso 127599461Sobrien * correct and can be selected to discipline the clock. 127699461Sobrien */ 127799461Sobrien if (temp > 0) { 127899461Sobrien record_clock_stats(&peer->srcadr, pp->a_lastcode); 127999461Sobrien refclock_receive(peer); 128099461Sobrien } else if (pp->sloppyclockflag & CLK_FLAG4) { 128199461Sobrien record_clock_stats(&peer->srcadr, pp->a_lastcode); 128299461Sobrien } 128399461Sobrien#ifdef DEBUG 128499461Sobrien if (debug) 128599461Sobrien printf("chu: timecode %d %s\n", pp->lencode, 128699461Sobrien pp->a_lastcode); 1287130561Sobrien#endif 128899461Sobrien chu_clear(peer); 1289218822Sdim if (up->errflg) 129099461Sobrien refclock_report(peer, up->errflg); 129199461Sobrien up->errflg = 0; 129299461Sobrien} 129399461Sobrien 1294218822Sdim 1295218822Sdim/* 129699461Sobrien * chu_major - majority decoder 129799461Sobrien */ 129899461Sobrienstatic int 129999461Sobrienchu_major( 130099461Sobrien struct peer *peer /* peer structure pointer */ 130199461Sobrien ) 130299461Sobrien{ 130399461Sobrien struct refclockproc *pp; 130499461Sobrien struct chuunit *up; 130599461Sobrien 130699461Sobrien u_char code[11]; /* decoded timecode */ 130799461Sobrien l_fp toffset, offset; /* l_fp temps */ 130899461Sobrien int val1, val2; /* maximum distance */ 130999461Sobrien int synchar; /* stray cat */ 131099461Sobrien double dtemp; 1311218822Sdim int temp; 1312218822Sdim int i, j, k; 1313218822Sdim 131499461Sobrien pp = peer->procptr; 131599461Sobrien up = (struct chuunit *)pp->unitptr; 1316130561Sobrien 131799461Sobrien /* 131899461Sobrien * Majority decoder. Each burst encodes two replications at each 131999461Sobrien * digit position in the timecode. Each row of the decoding 132099461Sobrien * matrix encodes the number of occurences of each digit found 132199461Sobrien * at the corresponding position. The maximum over all 132299461Sobrien * occurences at each position is the distance for this position 132399461Sobrien * and the corresponding digit is the maximumn likelihood 132499461Sobrien * candidate. If the distance is zero, assume a miss '_'; if the 132599461Sobrien * distance is not more than half the total number of 132699461Sobrien * occurences, assume a soft error '-'; if two different digits 132799461Sobrien * with the same distance are found, assume a hard error '='. 132899461Sobrien * These will later cause a format error when the timecode is 132999461Sobrien * interpreted. The decoding distance is defined as the minimum 133099461Sobrien * distance over the first nine digits. The tenth digit varies 133199461Sobrien * over the seconds, so we don't count it. 1332130561Sobrien */ 1333218822Sdim up->mindist = 16; 133499461Sobrien for (i = 0; i < 9; i++) { 133599461Sobrien val1 = val2 = 0; 133699461Sobrien k = 0; 133799461Sobrien for (j = 0; j < 16; j++) { 1338130561Sobrien temp = up->decode[i][j] + up->decode[i + 10][j]; 133999461Sobrien if (temp > val1) { 1340218822Sdim val2 = val1; 1341218822Sdim val1 = temp; 134299461Sobrien k = j; 134399461Sobrien } 134499461Sobrien } 134599461Sobrien if (val1 == 0) 134699461Sobrien code[i] = HEX_MISS; 134799461Sobrien else if (val1 == val2) 1348130561Sobrien code[i] = HEX_HARD; 134999461Sobrien else if (val1 <= up->burstcnt) 135099461Sobrien code[i] = HEX_SOFT; 135199461Sobrien else 1352130561Sobrien code[i] = k; 135399461Sobrien if (val1 < up->mindist) 135499461Sobrien up->mindist = val1; 135599461Sobrien code[i] = hexchar[code[i]]; 135699461Sobrien } 135799461Sobrien code[i] = 0; 135899461Sobrien 135999461Sobrien /* 136099461Sobrien * A valid timecode requires at least three bursts and a 136199461Sobrien * decoding distance greater than half the total number of 136299461Sobrien * occurences. A valid timecode also requires at least 20 valid 136399461Sobrien * timestamps. 136499461Sobrien */ 136599461Sobrien if (up->burstcnt < 3 || up->mindist <= up->burstcnt) 136699461Sobrien up->status |= DECODE; 136799461Sobrien if (up->ntstamp < MINSTAMP) 136899461Sobrien up->status |= STAMP; 136999461Sobrien 137099461Sobrien /* 1371130561Sobrien * Compute the timecode timestamp from the days, hours and 1372130561Sobrien * minutes of the timecode. Use clocktime() for the aggregate 1373130561Sobrien * minutes and the minute offset computed from the burst 1374130561Sobrien * seconds. Note that this code relies on the filesystem time 137599461Sobrien * for the years and does not use the years of the timecode. 137699461Sobrien */ 137799461Sobrien if (sscanf((char *)code, "%1x%3d%2d%2d", &synchar, &pp->day, 1378130561Sobrien &pp->hour, &pp->minute) != 4) { 1379130561Sobrien up->status |= AFORMAT; 1380130561Sobrien return (0); 1381130561Sobrien } 138299461Sobrien if (up->status & (DECODE | STAMP)) { 138399461Sobrien up->errflg = CEVNT_BADREPLY; 138499461Sobrien return (0); 138599461Sobrien } 138699461Sobrien L_CLR(&offset); 138799461Sobrien if (!clocktime(pp->day, pp->hour, pp->minute, 0, GMT, 1388130561Sobrien up->tstamp[0].l_ui, &pp->yearstart, &offset.l_ui)) { 138999461Sobrien up->errflg = CEVNT_BADTIME; 139099461Sobrien return (0); 139199461Sobrien } 139299461Sobrien pp->lastref = offset; 139399461Sobrien pp->variance = 0; 139499461Sobrien for (i = 0; i < up->ntstamp; i++) { 1395218822Sdim toffset = offset; 1396218822Sdim L_SUB(&toffset, &up->tstamp[i]); 1397218822Sdim LFPTOD(&toffset, dtemp); 1398218822Sdim SAMPLE(dtemp + FUDGE + pp->fudgetime1); 1399218822Sdim } 140099461Sobrien return (i); 140199461Sobrien} 1402218822Sdim 1403218822Sdim 1404218822Sdim/* 1405218822Sdim * chu_clear - clear decoding matrix 1406218822Sdim */ 1407218822Sdimstatic void 1408218822Sdimchu_clear( 140999461Sobrien struct peer *peer /* peer structure pointer */ 141099461Sobrien ) 141199461Sobrien{ 141299461Sobrien struct refclockproc *pp; 141399461Sobrien struct chuunit *up; 1414130561Sobrien int i, j; 1415218822Sdim 1416218822Sdim pp = peer->procptr; 1417218822Sdim up = (struct chuunit *)pp->unitptr; 1418218822Sdim 141999461Sobrien /* 142099461Sobrien * Clear stuff for the minute. 142199461Sobrien */ 142299461Sobrien up->ndx = up->prevsec = 0; 142399461Sobrien up->burstcnt = up->mindist = up->ntstamp = 0; 142499461Sobrien up->status &= INSYNC | INYEAR; 142599461Sobrien up->burstcnt = 0; 142699461Sobrien for (i = 0; i < 20; i++) { 142799461Sobrien for (j = 0; j < 16; j++) 142899461Sobrien up->decode[i][j] = 0; 142999461Sobrien } 143099461Sobrien} 143199461Sobrien 143299461Sobrien 1433130561Sobrien/* 1434130561Sobrien * chu_dist - determine the distance of two octet arguments 1435130561Sobrien */ 1436130561Sobrienstatic int 143799461Sobrienchu_dist( 1438130561Sobrien int x, /* an octet of bits */ 1439130561Sobrien int y /* another octet of bits */ 1440130561Sobrien ) 1441130561Sobrien{ 1442130561Sobrien int val; /* bit count */ 1443130561Sobrien int temp; 144499461Sobrien int i; 1445130561Sobrien 1446218822Sdim /* 1447218822Sdim * The distance is determined as the weight of the exclusive OR 1448218822Sdim * of the two arguments. The weight is determined by the number 1449130561Sobrien * of one bits in the result. Each one bit increases the weight, 145099461Sobrien * while each zero bit decreases it. 1451130561Sobrien */ 1452130561Sobrien temp = x ^ y; 1453130561Sobrien val = 0; 1454130561Sobrien for (i = 0; i < 8; i++) { 1455130561Sobrien if ((temp & 0x1) == 0) 1456130561Sobrien val++; 1457130561Sobrien else 1458130561Sobrien val--; 145999461Sobrien temp >>= 1; 1460130561Sobrien } 1461130561Sobrien return (val); 1462130561Sobrien} 1463130561Sobrien 1464130561Sobrien 1465130561Sobrien#ifdef AUDIO_CHU 1466130561Sobrien/* 1467130561Sobrien * chu_gain - adjust codec gain 146899461Sobrien * 1469130561Sobrien * This routine is called once each second. If the signal envelope 1470130561Sobrien * amplitude is too low, the codec gain is bumped up by four units; if 1471130561Sobrien * too high, it is bumped down. The decoder is relatively insensitive to 1472130561Sobrien * amplitude, so this crudity works just fine. The input port is set and 1473130561Sobrien * the error flag is cleared, mostly to be ornery. 1474130561Sobrien */ 1475130561Sobrienstatic void 1476130561Sobrienchu_gain( 1477130561Sobrien struct peer *peer /* peer structure pointer */ 1478130561Sobrien ) 1479130561Sobrien{ 1480130561Sobrien struct refclockproc *pp; 1481130561Sobrien struct chuunit *up; 1482130561Sobrien 1483130561Sobrien pp = peer->procptr; 1484130561Sobrien up = (struct chuunit *)pp->unitptr; 1485130561Sobrien 1486130561Sobrien /* 1487130561Sobrien * Apparently, the codec uses only the high order bits of the 1488130561Sobrien * gain control field. Thus, it may take awhile for changes to 1489130561Sobrien * wiggle the hardware bits. 1490130561Sobrien */ 1491130561Sobrien if (up->clipcnt == 0) { 1492130561Sobrien up->gain += 4; 1493130561Sobrien if (up->gain > 255) 1494130561Sobrien up->gain = 255; 1495130561Sobrien } else if (up->clipcnt > SECOND / 100) { 1496130561Sobrien up->gain -= 4; 1497130561Sobrien if (up->gain < 0) 1498130561Sobrien up->gain = 0; 1499130561Sobrien } 1500130561Sobrien audio_gain(up->gain, up->port); 1501130561Sobrien up->clipcnt = 0; 1502130561Sobrien} 1503130561Sobrien#endif /* AUDIO_CHU */ 1504130561Sobrien 1505130561Sobrien 1506130561Sobrien#else 1507130561Sobrienint refclock_chu_bs; 1508130561Sobrien#endif /* REFCLOCK */ 1509130561Sobrien