1*eabc0478Schristos /* $NetBSD: refclock_arbiter.c,v 1.6 2024/08/18 20:47:18 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite 5abb0f93cSkardel * Controlled Clock 6abb0f93cSkardel */ 7abb0f93cSkardel 8abb0f93cSkardel #ifdef HAVE_CONFIG_H 9abb0f93cSkardel #include <config.h> 10abb0f93cSkardel #endif 11abb0f93cSkardel 12abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_ARBITER) 13abb0f93cSkardel 14abb0f93cSkardel #include "ntpd.h" 15abb0f93cSkardel #include "ntp_io.h" 16abb0f93cSkardel #include "ntp_refclock.h" 17abb0f93cSkardel #include "ntp_stdlib.h" 18abb0f93cSkardel 19abb0f93cSkardel #include <stdio.h> 20abb0f93cSkardel #include <ctype.h> 21abb0f93cSkardel 22abb0f93cSkardel /* 23abb0f93cSkardel * This driver supports the Arbiter 1088A/B Satellite Controlled Clock. 24abb0f93cSkardel * The claimed accuracy of this clock is 100 ns relative to the PPS 25abb0f93cSkardel * output when receiving four or more satellites. 26abb0f93cSkardel * 27abb0f93cSkardel * The receiver should be configured before starting the NTP daemon, in 28abb0f93cSkardel * order to establish reliable position and operating conditions. It 29abb0f93cSkardel * does not initiate surveying or hold mode. For use with NTP, the 30abb0f93cSkardel * daylight savings time feature should be disables (D0 command) and the 31abb0f93cSkardel * broadcast mode set to operate in UTC (BU command). 32abb0f93cSkardel * 33abb0f93cSkardel * The timecode format supported by this driver is selected by the poll 34abb0f93cSkardel * sequence "B5", which initiates a line in the following format to be 35abb0f93cSkardel * repeated once per second until turned off by the "B0" poll sequence. 36abb0f93cSkardel * 37abb0f93cSkardel * Format B5 (24 ASCII printing characters): 38abb0f93cSkardel * 39abb0f93cSkardel * <cr><lf>i yy ddd hh:mm:ss.000bbb 40abb0f93cSkardel * 41abb0f93cSkardel * on-time = <cr> 42abb0f93cSkardel * i = synchronization flag (' ' = locked, '?' = unlocked) 43abb0f93cSkardel * yy = year of century 44abb0f93cSkardel * ddd = day of year 45abb0f93cSkardel * hh:mm:ss = hours, minutes, seconds 46abb0f93cSkardel * .000 = fraction of second (not used) 47abb0f93cSkardel * bbb = tailing spaces for fill 48abb0f93cSkardel * 49abb0f93cSkardel * The alarm condition is indicated by a '?' at i, which indicates the 50abb0f93cSkardel * receiver is not synchronized. In normal operation, a line consisting 51abb0f93cSkardel * of the timecode followed by the time quality character (TQ) followed 52abb0f93cSkardel * by the receiver status string (SR) is written to the clockstats file. 53abb0f93cSkardel * The time quality character is encoded in IEEE P1344 standard: 54abb0f93cSkardel * 55abb0f93cSkardel * Format TQ (IEEE P1344 estimated worst-case time quality) 56abb0f93cSkardel * 57abb0f93cSkardel * 0 clock locked, maximum accuracy 58abb0f93cSkardel * F clock failure, time not reliable 59abb0f93cSkardel * 4 clock unlocked, accuracy < 1 us 60abb0f93cSkardel * 5 clock unlocked, accuracy < 10 us 61abb0f93cSkardel * 6 clock unlocked, accuracy < 100 us 62abb0f93cSkardel * 7 clock unlocked, accuracy < 1 ms 63abb0f93cSkardel * 8 clock unlocked, accuracy < 10 ms 64abb0f93cSkardel * 9 clock unlocked, accuracy < 100 ms 65abb0f93cSkardel * A clock unlocked, accuracy < 1 s 66abb0f93cSkardel * B clock unlocked, accuracy < 10 s 67abb0f93cSkardel * 68abb0f93cSkardel * The status string is encoded as follows: 69abb0f93cSkardel * 70abb0f93cSkardel * Format SR (25 ASCII printing characters) 71abb0f93cSkardel * 72abb0f93cSkardel * V=vv S=ss T=t P=pdop E=ee 73abb0f93cSkardel * 74abb0f93cSkardel * vv = satellites visible 75abb0f93cSkardel * ss = relative signal strength 76abb0f93cSkardel * t = satellites tracked 77abb0f93cSkardel * pdop = position dilution of precision (meters) 78abb0f93cSkardel * ee = hardware errors 79abb0f93cSkardel * 80abb0f93cSkardel * If flag4 is set, an additional line consisting of the receiver 81abb0f93cSkardel * latitude (LA), longitude (LO), elevation (LH) (meters), and data 82abb0f93cSkardel * buffer (DB) is written to this file. If channel B is enabled for 83abb0f93cSkardel * deviation mode and connected to a 1-PPS signal, the last two numbers 84abb0f93cSkardel * on the line are the deviation and standard deviation averaged over 85abb0f93cSkardel * the last 15 seconds. 86abb0f93cSkardel * 87abb0f93cSkardel * PPS calibration fudge time1 .001240 88abb0f93cSkardel */ 89abb0f93cSkardel 90abb0f93cSkardel /* 91abb0f93cSkardel * Interface definitions 92abb0f93cSkardel */ 93abb0f93cSkardel #define DEVICE "/dev/gps%d" /* device name and unit */ 94abb0f93cSkardel #define SPEED232 B9600 /* uart speed (9600 baud) */ 95abb0f93cSkardel #define PRECISION (-20) /* precision assumed (about 1 us) */ 96abb0f93cSkardel #define REFID "GPS " /* reference ID */ 97abb0f93cSkardel #define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */ 98abb0f93cSkardel #define LENARB 24 /* format B5 timecode length */ 99abb0f93cSkardel #define MAXSTA 40 /* max length of status string */ 100abb0f93cSkardel #define MAXPOS 80 /* max length of position string */ 101abb0f93cSkardel 102f003fb54Skardel #ifdef PRE_NTP420 103f003fb54Skardel #define MODE ttlmax 104f003fb54Skardel #else 105f003fb54Skardel #define MODE ttl 106f003fb54Skardel #endif 107f003fb54Skardel 108f003fb54Skardel #define COMMAND_HALT_BCAST ( (peer->MODE % 2) ? "O0" : "B0" ) 109f003fb54Skardel #define COMMAND_START_BCAST ( (peer->MODE % 2) ? "O5" : "B5" ) 110f003fb54Skardel 111abb0f93cSkardel /* 112abb0f93cSkardel * ARB unit control structure 113abb0f93cSkardel */ 114abb0f93cSkardel struct arbunit { 115abb0f93cSkardel l_fp laststamp; /* last receive timestamp */ 116abb0f93cSkardel int tcswitch; /* timecode switch/counter */ 117abb0f93cSkardel char qualchar; /* IEEE P1344 quality (TQ command) */ 118abb0f93cSkardel char status[MAXSTA]; /* receiver status (SR command) */ 119abb0f93cSkardel char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */ 120abb0f93cSkardel }; 121abb0f93cSkardel 122abb0f93cSkardel /* 123abb0f93cSkardel * Function prototypes 124abb0f93cSkardel */ 125abb0f93cSkardel static int arb_start (int, struct peer *); 126abb0f93cSkardel static void arb_shutdown (int, struct peer *); 127abb0f93cSkardel static void arb_receive (struct recvbuf *); 128abb0f93cSkardel static void arb_poll (int, struct peer *); 129abb0f93cSkardel 130abb0f93cSkardel /* 131abb0f93cSkardel * Transfer vector 132abb0f93cSkardel */ 133abb0f93cSkardel struct refclock refclock_arbiter = { 134abb0f93cSkardel arb_start, /* start up driver */ 135abb0f93cSkardel arb_shutdown, /* shut down driver */ 136abb0f93cSkardel arb_poll, /* transmit poll message */ 137abb0f93cSkardel noentry, /* not used (old arb_control) */ 138abb0f93cSkardel noentry, /* initialize driver (not used) */ 139abb0f93cSkardel noentry, /* not used (old arb_buginfo) */ 140abb0f93cSkardel NOFLAGS /* not used */ 141abb0f93cSkardel }; 142abb0f93cSkardel 143abb0f93cSkardel 144abb0f93cSkardel /* 145abb0f93cSkardel * arb_start - open the devices and initialize data for processing 146abb0f93cSkardel */ 147abb0f93cSkardel static int 148abb0f93cSkardel arb_start( 149abb0f93cSkardel int unit, 150abb0f93cSkardel struct peer *peer 151abb0f93cSkardel ) 152abb0f93cSkardel { 153abb0f93cSkardel register struct arbunit *up; 154abb0f93cSkardel struct refclockproc *pp; 155abb0f93cSkardel int fd; 156abb0f93cSkardel char device[20]; 157abb0f93cSkardel 158abb0f93cSkardel /* 159abb0f93cSkardel * Open serial port. Use CLK line discipline, if available. 160abb0f93cSkardel */ 161f003fb54Skardel snprintf(device, sizeof(device), DEVICE, unit); 162*eabc0478Schristos fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK); 1638585484eSchristos if (fd <= 0) 164abb0f93cSkardel return (0); 165abb0f93cSkardel 166abb0f93cSkardel /* 167abb0f93cSkardel * Allocate and initialize unit structure 168abb0f93cSkardel */ 1698585484eSchristos up = emalloc_zero(sizeof(*up)); 170abb0f93cSkardel pp = peer->procptr; 171abb0f93cSkardel pp->io.clock_recv = arb_receive; 1728585484eSchristos pp->io.srcclock = peer; 173abb0f93cSkardel pp->io.datalen = 0; 174abb0f93cSkardel pp->io.fd = fd; 175abb0f93cSkardel if (!io_addclock(&pp->io)) { 176f003fb54Skardel close(fd); 177f003fb54Skardel pp->io.fd = -1; 178abb0f93cSkardel free(up); 179abb0f93cSkardel return (0); 180abb0f93cSkardel } 1818585484eSchristos pp->unitptr = up; 182abb0f93cSkardel 183abb0f93cSkardel /* 184abb0f93cSkardel * Initialize miscellaneous variables 185abb0f93cSkardel */ 186abb0f93cSkardel peer->precision = PRECISION; 187abb0f93cSkardel pp->clockdesc = DESCRIPTION; 188abb0f93cSkardel memcpy((char *)&pp->refid, REFID, 4); 189f003fb54Skardel if (peer->MODE > 1) { 190f003fb54Skardel msyslog(LOG_NOTICE, "ARBITER: Invalid mode %d", peer->MODE); 191f003fb54Skardel close(fd); 192f003fb54Skardel pp->io.fd = -1; 193f003fb54Skardel free(up); 194f003fb54Skardel return (0); 195f003fb54Skardel } 196f003fb54Skardel #ifdef DEBUG 197f003fb54Skardel if(debug) { printf("arbiter: mode = %d.\n", peer->MODE); } 198f003fb54Skardel #endif 199*eabc0478Schristos refclock_write(peer, COMMAND_HALT_BCAST, 2, "HALT_BCAST"); 200abb0f93cSkardel return (1); 201abb0f93cSkardel } 202abb0f93cSkardel 203abb0f93cSkardel 204abb0f93cSkardel /* 205abb0f93cSkardel * arb_shutdown - shut down the clock 206abb0f93cSkardel */ 207abb0f93cSkardel static void 208abb0f93cSkardel arb_shutdown( 209abb0f93cSkardel int unit, 210abb0f93cSkardel struct peer *peer 211abb0f93cSkardel ) 212abb0f93cSkardel { 213abb0f93cSkardel register struct arbunit *up; 214abb0f93cSkardel struct refclockproc *pp; 215abb0f93cSkardel 216abb0f93cSkardel pp = peer->procptr; 2178585484eSchristos up = pp->unitptr; 218f003fb54Skardel if (-1 != pp->io.fd) 219abb0f93cSkardel io_closeclock(&pp->io); 220f003fb54Skardel if (NULL != up) 221abb0f93cSkardel free(up); 222abb0f93cSkardel } 223abb0f93cSkardel 224abb0f93cSkardel 225abb0f93cSkardel /* 226abb0f93cSkardel * arb_receive - receive data from the serial interface 227abb0f93cSkardel */ 228abb0f93cSkardel static void 229abb0f93cSkardel arb_receive( 230abb0f93cSkardel struct recvbuf *rbufp 231abb0f93cSkardel ) 232abb0f93cSkardel { 233abb0f93cSkardel register struct arbunit *up; 234abb0f93cSkardel struct refclockproc *pp; 235abb0f93cSkardel struct peer *peer; 236abb0f93cSkardel l_fp trtmp; 237abb0f93cSkardel int temp; 238abb0f93cSkardel u_char syncchar; /* synch indicator */ 239abb0f93cSkardel char tbuf[BMAX]; /* temp buffer */ 240abb0f93cSkardel 241abb0f93cSkardel /* 242abb0f93cSkardel * Initialize pointers and read the timecode and timestamp 243abb0f93cSkardel */ 2448585484eSchristos peer = rbufp->recv_peer; 245abb0f93cSkardel pp = peer->procptr; 2468585484eSchristos up = pp->unitptr; 2478585484eSchristos temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp); 248abb0f93cSkardel 249abb0f93cSkardel /* 250abb0f93cSkardel * Note we get a buffer and timestamp for both a <cr> and <lf>, 251abb0f93cSkardel * but only the <cr> timestamp is retained. The program first 252abb0f93cSkardel * sends a TQ and expects the echo followed by the time quality 253abb0f93cSkardel * character. It then sends a B5 starting the timecode broadcast 254abb0f93cSkardel * and expects the echo followed some time later by the on-time 255abb0f93cSkardel * character <cr> and then the <lf> beginning the timecode 256abb0f93cSkardel * itself. Finally, at the <cr> beginning the next timecode at 257abb0f93cSkardel * the next second, the program sends a B0 shutting down the 258abb0f93cSkardel * timecode broadcast. 259abb0f93cSkardel * 260abb0f93cSkardel * If flag4 is set, the program snatches the latitude, longitude 261abb0f93cSkardel * and elevation and writes it to the clockstats file. 262abb0f93cSkardel */ 263abb0f93cSkardel if (temp == 0) 264abb0f93cSkardel return; 265abb0f93cSkardel 266abb0f93cSkardel pp->lastrec = up->laststamp; 267abb0f93cSkardel up->laststamp = trtmp; 268abb0f93cSkardel if (temp < 3) 269abb0f93cSkardel return; 270abb0f93cSkardel 271abb0f93cSkardel if (up->tcswitch == 0) { 272abb0f93cSkardel 273abb0f93cSkardel /* 274abb0f93cSkardel * Collect statistics. If nothing is recogized, just 275abb0f93cSkardel * ignore; sometimes the clock doesn't stop spewing 276abb0f93cSkardel * timecodes for awhile after the B0 command. 277abb0f93cSkardel * 278abb0f93cSkardel * If flag4 is not set, send TQ, SR, B5. If flag4 is 279abb0f93cSkardel * sset, send TQ, SR, LA, LO, LH, DB, B5. When the 280abb0f93cSkardel * median filter is full, send B0. 281abb0f93cSkardel */ 282abb0f93cSkardel if (!strncmp(tbuf, "TQ", 2)) { 283abb0f93cSkardel up->qualchar = tbuf[2]; 284*eabc0478Schristos refclock_write(peer, "SR", 2, "SR"); 285abb0f93cSkardel return; 286abb0f93cSkardel 287abb0f93cSkardel } else if (!strncmp(tbuf, "SR", 2)) { 2888585484eSchristos strlcpy(up->status, tbuf + 2, 2898585484eSchristos sizeof(up->status)); 290abb0f93cSkardel if (pp->sloppyclockflag & CLK_FLAG4) 291*eabc0478Schristos refclock_write(peer, "LA", 2, "LA"); 292abb0f93cSkardel else 293*eabc0478Schristos refclock_write(peer, COMMAND_START_BCAST, 2, 294*eabc0478Schristos COMMAND_START_BCAST); 295abb0f93cSkardel return; 296abb0f93cSkardel 297abb0f93cSkardel } else if (!strncmp(tbuf, "LA", 2)) { 2988585484eSchristos strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon)); 299*eabc0478Schristos refclock_write(peer, "LO", 2, "LO"); 300abb0f93cSkardel return; 301abb0f93cSkardel 302abb0f93cSkardel } else if (!strncmp(tbuf, "LO", 2)) { 3038585484eSchristos strlcat(up->latlon, " ", sizeof(up->latlon)); 3048585484eSchristos strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 305*eabc0478Schristos refclock_write(peer, "LH", 2, "LH"); 306abb0f93cSkardel return; 307abb0f93cSkardel 308abb0f93cSkardel } else if (!strncmp(tbuf, "LH", 2)) { 3098585484eSchristos strlcat(up->latlon, " ", sizeof(up->latlon)); 3108585484eSchristos strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 311*eabc0478Schristos refclock_write(peer, "DB", 2, "DB"); 312abb0f93cSkardel return; 313abb0f93cSkardel 314abb0f93cSkardel } else if (!strncmp(tbuf, "DB", 2)) { 3158585484eSchristos strlcat(up->latlon, " ", sizeof(up->latlon)); 3168585484eSchristos strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 317abb0f93cSkardel record_clock_stats(&peer->srcadr, up->latlon); 318abb0f93cSkardel #ifdef DEBUG 319abb0f93cSkardel if (debug) 320abb0f93cSkardel printf("arbiter: %s\n", up->latlon); 321abb0f93cSkardel #endif 322*eabc0478Schristos refclock_write(peer, COMMAND_START_BCAST, 2, 323*eabc0478Schristos COMMAND_START_BCAST); 324abb0f93cSkardel } 325abb0f93cSkardel } 326abb0f93cSkardel 327abb0f93cSkardel /* 328abb0f93cSkardel * We get down to business, check the timecode format and decode 329abb0f93cSkardel * its contents. If the timecode has valid length, but not in 330abb0f93cSkardel * proper format, we declare bad format and exit. If the 331abb0f93cSkardel * timecode has invalid length, which sometimes occurs when the 332abb0f93cSkardel * B0 amputates the broadcast, we just quietly steal away. Note 333abb0f93cSkardel * that the time quality character and receiver status string is 334abb0f93cSkardel * tacked on the end for clockstats display. 335abb0f93cSkardel */ 336abb0f93cSkardel up->tcswitch++; 337abb0f93cSkardel if (up->tcswitch <= 1 || temp < LENARB) 338abb0f93cSkardel return; 339abb0f93cSkardel 340abb0f93cSkardel /* 341abb0f93cSkardel * Timecode format B5: "i yy ddd hh:mm:ss.000 " 342abb0f93cSkardel */ 3438585484eSchristos strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode)); 344abb0f93cSkardel pp->a_lastcode[LENARB - 2] = up->qualchar; 3458585484eSchristos strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode)); 346abb0f93cSkardel pp->lencode = strlen(pp->a_lastcode); 347abb0f93cSkardel syncchar = ' '; 348abb0f93cSkardel if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d", 349abb0f93cSkardel &syncchar, &pp->year, &pp->day, &pp->hour, 350abb0f93cSkardel &pp->minute, &pp->second) != 6) { 351abb0f93cSkardel refclock_report(peer, CEVNT_BADREPLY); 352*eabc0478Schristos refclock_write(peer, COMMAND_HALT_BCAST, 2, COMMAND_HALT_BCAST); 353abb0f93cSkardel return; 354abb0f93cSkardel } 355abb0f93cSkardel 356abb0f93cSkardel /* 357abb0f93cSkardel * We decode the clock dispersion from the time quality 358abb0f93cSkardel * character. 359abb0f93cSkardel */ 360abb0f93cSkardel switch (up->qualchar) { 361abb0f93cSkardel 362abb0f93cSkardel case '0': /* locked, max accuracy */ 363abb0f93cSkardel pp->disp = 1e-7; 364abb0f93cSkardel pp->lastref = pp->lastrec; 365abb0f93cSkardel break; 366abb0f93cSkardel 367abb0f93cSkardel case '4': /* unlock accuracy < 1 us */ 368abb0f93cSkardel pp->disp = 1e-6; 369abb0f93cSkardel break; 370abb0f93cSkardel 371abb0f93cSkardel case '5': /* unlock accuracy < 10 us */ 372abb0f93cSkardel pp->disp = 1e-5; 373abb0f93cSkardel break; 374abb0f93cSkardel 375abb0f93cSkardel case '6': /* unlock accuracy < 100 us */ 376abb0f93cSkardel pp->disp = 1e-4; 377abb0f93cSkardel break; 378abb0f93cSkardel 379abb0f93cSkardel case '7': /* unlock accuracy < 1 ms */ 380abb0f93cSkardel pp->disp = .001; 381abb0f93cSkardel break; 382abb0f93cSkardel 383abb0f93cSkardel case '8': /* unlock accuracy < 10 ms */ 384abb0f93cSkardel pp->disp = .01; 385abb0f93cSkardel break; 386abb0f93cSkardel 387abb0f93cSkardel case '9': /* unlock accuracy < 100 ms */ 388abb0f93cSkardel pp->disp = .1; 389abb0f93cSkardel break; 390abb0f93cSkardel 391abb0f93cSkardel case 'A': /* unlock accuracy < 1 s */ 392abb0f93cSkardel pp->disp = 1; 393abb0f93cSkardel break; 394abb0f93cSkardel 395abb0f93cSkardel case 'B': /* unlock accuracy < 10 s */ 396abb0f93cSkardel pp->disp = 10; 397abb0f93cSkardel break; 398abb0f93cSkardel 399abb0f93cSkardel case 'F': /* clock failure */ 400abb0f93cSkardel pp->disp = MAXDISPERSE; 401abb0f93cSkardel refclock_report(peer, CEVNT_FAULT); 402*eabc0478Schristos refclock_write(peer, COMMAND_HALT_BCAST, 2, 403*eabc0478Schristos COMMAND_HALT_BCAST); 404abb0f93cSkardel return; 405abb0f93cSkardel 406abb0f93cSkardel default: 407abb0f93cSkardel pp->disp = MAXDISPERSE; 408abb0f93cSkardel refclock_report(peer, CEVNT_BADREPLY); 409*eabc0478Schristos refclock_write(peer, COMMAND_HALT_BCAST, 2, 410*eabc0478Schristos COMMAND_HALT_BCAST); 411abb0f93cSkardel return; 412abb0f93cSkardel } 413abb0f93cSkardel if (syncchar != ' ') 414abb0f93cSkardel pp->leap = LEAP_NOTINSYNC; 415abb0f93cSkardel else 416abb0f93cSkardel pp->leap = LEAP_NOWARNING; 417abb0f93cSkardel 418abb0f93cSkardel /* 419abb0f93cSkardel * Process the new sample in the median filter and determine the 420abb0f93cSkardel * timecode timestamp. 421abb0f93cSkardel */ 422abb0f93cSkardel if (!refclock_process(pp)) 423abb0f93cSkardel refclock_report(peer, CEVNT_BADTIME); 424abb0f93cSkardel else if (peer->disp > MAXDISTANCE) 425abb0f93cSkardel refclock_receive(peer); 426abb0f93cSkardel 427f003fb54Skardel /* if (up->tcswitch >= MAXSTAGE) { */ 428*eabc0478Schristos refclock_write(peer, COMMAND_HALT_BCAST, 2, COMMAND_HALT_BCAST); 429f003fb54Skardel /* } */ 430abb0f93cSkardel } 431abb0f93cSkardel 432abb0f93cSkardel 433abb0f93cSkardel /* 434abb0f93cSkardel * arb_poll - called by the transmit procedure 435abb0f93cSkardel */ 436abb0f93cSkardel static void 437abb0f93cSkardel arb_poll( 438abb0f93cSkardel int unit, 439abb0f93cSkardel struct peer *peer 440abb0f93cSkardel ) 441abb0f93cSkardel { 442abb0f93cSkardel register struct arbunit *up; 443abb0f93cSkardel struct refclockproc *pp; 444abb0f93cSkardel 445abb0f93cSkardel /* 446abb0f93cSkardel * Time to poll the clock. The Arbiter clock responds to a "B5" 447abb0f93cSkardel * by returning a timecode in the format specified above. 448abb0f93cSkardel * Transmission occurs once per second, unless turned off by a 449abb0f93cSkardel * "B0". Note there is no checking on state, since this may not 450abb0f93cSkardel * be the only customer reading the clock. Only one customer 451abb0f93cSkardel * need poll the clock; all others just listen in. 452abb0f93cSkardel */ 453abb0f93cSkardel pp = peer->procptr; 4548585484eSchristos up = pp->unitptr; 455abb0f93cSkardel pp->polls++; 456abb0f93cSkardel up->tcswitch = 0; 457*eabc0478Schristos if (refclock_write(peer, "TQ", 2, "TQ") != 2) 458abb0f93cSkardel refclock_report(peer, CEVNT_FAULT); 459abb0f93cSkardel 460abb0f93cSkardel /* 461abb0f93cSkardel * Process median filter samples. If none received, declare a 462abb0f93cSkardel * timeout and keep going. 463abb0f93cSkardel */ 464abb0f93cSkardel if (pp->coderecv == pp->codeproc) { 465abb0f93cSkardel refclock_report(peer, CEVNT_TIMEOUT); 466abb0f93cSkardel return; 467abb0f93cSkardel } 468abb0f93cSkardel refclock_receive(peer); 469abb0f93cSkardel record_clock_stats(&peer->srcadr, pp->a_lastcode); 470abb0f93cSkardel #ifdef DEBUG 471abb0f93cSkardel if (debug) 472abb0f93cSkardel printf("arbiter: timecode %d %s\n", 473abb0f93cSkardel pp->lencode, pp->a_lastcode); 474abb0f93cSkardel #endif 475abb0f93cSkardel } 476abb0f93cSkardel 477abb0f93cSkardel #else 478*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 479abb0f93cSkardel #endif /* REFCLOCK */ 480