1232950Stheraven/* 2232950Stheraven * refclock_arbiter - clock driver for Arbiter 1088A/B Satellite 3232950Stheraven * Controlled Clock 4232950Stheraven */ 5232950Stheraven 6232950Stheraven#ifdef HAVE_CONFIG_H 7232950Stheraven#include <config.h> 8232950Stheraven#endif 9232950Stheraven 10232950Stheraven#if defined(REFCLOCK) && defined(CLOCK_ARBITER) 11232950Stheraven 12232950Stheraven#include "ntpd.h" 13232950Stheraven#include "ntp_io.h" 14232950Stheraven#include "ntp_refclock.h" 15232950Stheraven#include "ntp_stdlib.h" 16232950Stheraven 17232950Stheraven#include <stdio.h> 18232950Stheraven#include <ctype.h> 19232950Stheraven 20232950Stheraven#ifdef SYS_WINNT 21232950Stheravenextern int async_write(int, const void *, unsigned int); 22232950Stheraven#undef write 23232950Stheraven#define write(fd, data, octets) async_write(fd, data, octets) 24232950Stheraven#endif 25232950Stheraven 26227825Stheraven/* 27227825Stheraven * This driver supports the Arbiter 1088A/B Satellite Controlled Clock. 28227825Stheraven * The claimed accuracy of this clock is 100 ns relative to the PPS 29227825Stheraven * output when receiving four or more satellites. 30227825Stheraven * 31227825Stheraven * The receiver should be configured before starting the NTP daemon, in 32227825Stheraven * order to establish reliable position and operating conditions. It 33227825Stheraven * does not initiate surveying or hold mode. For use with NTP, the 34227825Stheraven * daylight savings time feature should be disables (D0 command) and the 35227825Stheraven * broadcast mode set to operate in UTC (BU command). 36227825Stheraven * 37227825Stheraven * The timecode format supported by this driver is selected by the poll 38227825Stheraven * sequence "B5", which initiates a line in the following format to be 39227825Stheraven * repeated once per second until turned off by the "B0" poll sequence. 40227825Stheraven * 41227825Stheraven * Format B5 (24 ASCII printing characters): 42227825Stheraven * 43227825Stheraven * <cr><lf>i yy ddd hh:mm:ss.000bbb 44227972Stheraven * 45227825Stheraven * on-time = <cr> 46227825Stheraven * i = synchronization flag (' ' = locked, '?' = unlocked) 47227972Stheraven * yy = year of century 48227825Stheraven * ddd = day of year 49227825Stheraven * hh:mm:ss = hours, minutes, seconds 50227825Stheraven * .000 = fraction of second (not used) 51227825Stheraven * bbb = tailing spaces for fill 52227825Stheraven * 53227825Stheraven * The alarm condition is indicated by a '?' at i, which indicates the 54227825Stheraven * receiver is not synchronized. In normal operation, a line consisting 55227825Stheraven * of the timecode followed by the time quality character (TQ) followed 56227825Stheraven * by the receiver status string (SR) is written to the clockstats file. 57227825Stheraven * The time quality character is encoded in IEEE P1344 standard: 58227825Stheraven * 59227825Stheraven * Format TQ (IEEE P1344 estimated worst-case time quality) 60253159Stheraven * 61253159Stheraven * 0 clock locked, maximum accuracy 62227825Stheraven * F clock failure, time not reliable 63227825Stheraven * 4 clock unlocked, accuracy < 1 us 64227825Stheraven * 5 clock unlocked, accuracy < 10 us 65227825Stheraven * 6 clock unlocked, accuracy < 100 us 66227825Stheraven * 7 clock unlocked, accuracy < 1 ms 67227825Stheraven * 8 clock unlocked, accuracy < 10 ms 68227825Stheraven * 9 clock unlocked, accuracy < 100 ms 69227825Stheraven * A clock unlocked, accuracy < 1 s 70227825Stheraven * B clock unlocked, accuracy < 10 s 71227825Stheraven * 72227825Stheraven * The status string is encoded as follows: 73227825Stheraven * 74227825Stheraven * Format SR (25 ASCII printing characters) 75227825Stheraven * 76227825Stheraven * V=vv S=ss T=t P=pdop E=ee 77227825Stheraven * 78227825Stheraven * vv = satellites visible 79227825Stheraven * ss = relative signal strength 80227825Stheraven * t = satellites tracked 81227825Stheraven * pdop = position dilution of precision (meters) 82227825Stheraven * ee = hardware errors 83227825Stheraven * 84227825Stheraven * If flag4 is set, an additional line consisting of the receiver 85227825Stheraven * latitude (LA), longitude (LO), elevation (LH) (meters), and data 86278724Sdim * buffer (DB) is written to this file. If channel B is enabled for 87227825Stheraven * deviation mode and connected to a 1-PPS signal, the last two numbers 88227825Stheraven * on the line are the deviation and standard deviation averaged over 89227825Stheraven * the last 15 seconds. 90227825Stheraven * 91227825Stheraven * PPS calibration fudge time1 .001240 92227825Stheraven */ 93227825Stheraven 94227825Stheraven/* 95227825Stheraven * Interface definitions 96227825Stheraven */ 97227825Stheraven#define DEVICE "/dev/gps%d" /* device name and unit */ 98227825Stheraven#define SPEED232 B9600 /* uart speed (9600 baud) */ 99227825Stheraven#define PRECISION (-20) /* precision assumed (about 1 us) */ 100227825Stheraven#define REFID "GPS " /* reference ID */ 101227825Stheraven#define DESCRIPTION "Arbiter 1088A/B GPS Receiver" /* WRU */ 102227825Stheraven#define LENARB 24 /* format B5 timecode length */ 103227825Stheraven#define MAXSTA 40 /* max length of status string */ 104227825Stheraven#define MAXPOS 80 /* max length of position string */ 105227825Stheraven 106227825Stheraven#ifdef PRE_NTP420 107227825Stheraven#define MODE ttlmax 108227825Stheraven#else 109227825Stheraven#define MODE ttl 110227825Stheraven#endif 111227825Stheraven 112227825Stheraven#define COMMAND_HALT_BCAST ( (peer->MODE % 2) ? "O0" : "B0" ) 113227825Stheraven#define COMMAND_START_BCAST ( (peer->MODE % 2) ? "O5" : "B5" ) 114227825Stheraven 115227825Stheraven/* 116227825Stheraven * ARB unit control structure 117227825Stheraven */ 118278724Sdimstruct arbunit { 119227825Stheraven l_fp laststamp; /* last receive timestamp */ 120227825Stheraven int tcswitch; /* timecode switch/counter */ 121227825Stheraven char qualchar; /* IEEE P1344 quality (TQ command) */ 122227825Stheraven char status[MAXSTA]; /* receiver status (SR command) */ 123227825Stheraven char latlon[MAXPOS]; /* receiver position (lat/lon/alt) */ 124227825Stheraven}; 125227825Stheraven 126227825Stheraven/* 127227825Stheraven * Function prototypes 128227825Stheraven */ 129227825Stheravenstatic int arb_start (int, struct peer *); 130227825Stheravenstatic void arb_shutdown (int, struct peer *); 131227825Stheravenstatic void arb_receive (struct recvbuf *); 132227825Stheravenstatic void arb_poll (int, struct peer *); 133227825Stheraven 134227825Stheraven/* 135227825Stheraven * Transfer vector 136227825Stheraven */ 137227825Stheravenstruct refclock refclock_arbiter = { 138227825Stheraven arb_start, /* start up driver */ 139227825Stheraven arb_shutdown, /* shut down driver */ 140227825Stheraven arb_poll, /* transmit poll message */ 141227825Stheraven noentry, /* not used (old arb_control) */ 142227825Stheraven noentry, /* initialize driver (not used) */ 143227825Stheraven noentry, /* not used (old arb_buginfo) */ 144227825Stheraven NOFLAGS /* not used */ 145227825Stheraven}; 146227825Stheraven 147227825Stheraven 148227825Stheraven/* 149227825Stheraven * arb_start - open the devices and initialize data for processing 150227825Stheraven */ 151227825Stheravenstatic int 152227825Stheravenarb_start( 153227825Stheraven int unit, 154227825Stheraven struct peer *peer 155227825Stheraven ) 156227825Stheraven{ 157227825Stheraven register struct arbunit *up; 158227825Stheraven struct refclockproc *pp; 159227825Stheraven int fd; 160227825Stheraven char device[20]; 161227825Stheraven 162227825Stheraven /* 163227825Stheraven * Open serial port. Use CLK line discipline, if available. 164227825Stheraven */ 165227825Stheraven snprintf(device, sizeof(device), DEVICE, unit); 166227825Stheraven fd = refclock_open(device, SPEED232, LDISC_CLK); 167227825Stheraven if (fd <= 0) 168227825Stheraven return (0); 169227825Stheraven 170227825Stheraven /* 171227825Stheraven * Allocate and initialize unit structure 172227825Stheraven */ 173227825Stheraven up = emalloc_zero(sizeof(*up)); 174227825Stheraven pp = peer->procptr; 175227825Stheraven pp->io.clock_recv = arb_receive; 176227825Stheraven pp->io.srcclock = peer; 177227825Stheraven pp->io.datalen = 0; 178227825Stheraven pp->io.fd = fd; 179227825Stheraven if (!io_addclock(&pp->io)) { 180227825Stheraven close(fd); 181227825Stheraven pp->io.fd = -1; 182227825Stheraven free(up); 183227825Stheraven return (0); 184227825Stheraven } 185227825Stheraven pp->unitptr = up; 186227825Stheraven 187227825Stheraven /* 188227825Stheraven * Initialize miscellaneous variables 189227825Stheraven */ 190227825Stheraven peer->precision = PRECISION; 191227825Stheraven pp->clockdesc = DESCRIPTION; 192227825Stheraven memcpy((char *)&pp->refid, REFID, 4); 193227825Stheraven if (peer->MODE > 1) { 194227825Stheraven msyslog(LOG_NOTICE, "ARBITER: Invalid mode %d", peer->MODE); 195227825Stheraven close(fd); 196227825Stheraven pp->io.fd = -1; 197227825Stheraven free(up); 198227825Stheraven return (0); 199227825Stheraven } 200227825Stheraven#ifdef DEBUG 201227825Stheraven if(debug) { printf("arbiter: mode = %d.\n", peer->MODE); } 202227825Stheraven#endif 203227825Stheraven write(pp->io.fd, COMMAND_HALT_BCAST, 2); 204227825Stheraven return (1); 205227825Stheraven} 206227825Stheraven 207227825Stheraven 208227825Stheraven/* 209278724Sdim * arb_shutdown - shut down the clock 210227825Stheraven */ 211278724Sdimstatic void 212227825Stheravenarb_shutdown( 213227825Stheraven int unit, 214227825Stheraven struct peer *peer 215227825Stheraven ) 216227825Stheraven{ 217227825Stheraven register struct arbunit *up; 218227825Stheraven struct refclockproc *pp; 219227825Stheraven 220227825Stheraven pp = peer->procptr; 221227825Stheraven up = pp->unitptr; 222227825Stheraven if (-1 != pp->io.fd) 223227825Stheraven io_closeclock(&pp->io); 224227825Stheraven if (NULL != up) 225227825Stheraven free(up); 226279456Sdim} 227279456Sdim 228279456Sdim 229279456Sdim/* 230279456Sdim * arb_receive - receive data from the serial interface 231279456Sdim */ 232227825Stheravenstatic void 233227825Stheravenarb_receive( 234227825Stheraven struct recvbuf *rbufp 235227825Stheraven ) 236227825Stheraven{ 237227825Stheraven register struct arbunit *up; 238227825Stheraven struct refclockproc *pp; 239227825Stheraven struct peer *peer; 240227825Stheraven l_fp trtmp; 241227825Stheraven int temp; 242279456Sdim u_char syncchar; /* synch indicator */ 243227825Stheraven char tbuf[BMAX]; /* temp buffer */ 244279456Sdim 245227825Stheraven /* 246227825Stheraven * Initialize pointers and read the timecode and timestamp 247227825Stheraven */ 248227825Stheraven peer = rbufp->recv_peer; 249227825Stheraven pp = peer->procptr; 250227825Stheraven up = pp->unitptr; 251227825Stheraven temp = refclock_gtlin(rbufp, tbuf, sizeof(tbuf), &trtmp); 252227825Stheraven 253227825Stheraven /* 254227825Stheraven * Note we get a buffer and timestamp for both a <cr> and <lf>, 255227825Stheraven * but only the <cr> timestamp is retained. The program first 256227825Stheraven * sends a TQ and expects the echo followed by the time quality 257227825Stheraven * character. It then sends a B5 starting the timecode broadcast 258227825Stheraven * and expects the echo followed some time later by the on-time 259227825Stheraven * character <cr> and then the <lf> beginning the timecode 260227825Stheraven * itself. Finally, at the <cr> beginning the next timecode at 261227825Stheraven * the next second, the program sends a B0 shutting down the 262227825Stheraven * timecode broadcast. 263227825Stheraven * 264278724Sdim * If flag4 is set, the program snatches the latitude, longitude 265227825Stheraven * and elevation and writes it to the clockstats file. 266227825Stheraven */ 267278724Sdim if (temp == 0) 268227825Stheraven return; 269227825Stheraven 270278724Sdim pp->lastrec = up->laststamp; 271227825Stheraven up->laststamp = trtmp; 272227825Stheraven if (temp < 3) 273278724Sdim return; 274227825Stheraven 275227825Stheraven if (up->tcswitch == 0) { 276227825Stheraven 277227825Stheraven /* 278227825Stheraven * Collect statistics. If nothing is recogized, just 279227825Stheraven * ignore; sometimes the clock doesn't stop spewing 280227825Stheraven * timecodes for awhile after the B0 command. 281227825Stheraven * 282227825Stheraven * If flag4 is not set, send TQ, SR, B5. If flag4 is 283278724Sdim * sset, send TQ, SR, LA, LO, LH, DB, B5. When the 284227825Stheraven * median filter is full, send B0. 285227825Stheraven */ 286227825Stheraven if (!strncmp(tbuf, "TQ", 2)) { 287227825Stheraven up->qualchar = tbuf[2]; 288227825Stheraven write(pp->io.fd, "SR", 2); 289227825Stheraven return; 290227825Stheraven 291227825Stheraven } else if (!strncmp(tbuf, "SR", 2)) { 292227825Stheraven strlcpy(up->status, tbuf + 2, 293227825Stheraven sizeof(up->status)); 294227825Stheraven if (pp->sloppyclockflag & CLK_FLAG4) 295227825Stheraven write(pp->io.fd, "LA", 2); 296227825Stheraven else 297227825Stheraven write(pp->io.fd, COMMAND_START_BCAST, 2); 298227825Stheraven return; 299227825Stheraven 300227825Stheraven } else if (!strncmp(tbuf, "LA", 2)) { 301227825Stheraven strlcpy(up->latlon, tbuf + 2, sizeof(up->latlon)); 302227825Stheraven write(pp->io.fd, "LO", 2); 303227825Stheraven return; 304227825Stheraven 305227825Stheraven } else if (!strncmp(tbuf, "LO", 2)) { 306227825Stheraven strlcat(up->latlon, " ", sizeof(up->latlon)); 307227825Stheraven strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 308227825Stheraven write(pp->io.fd, "LH", 2); 309227825Stheraven return; 310227825Stheraven 311227825Stheraven } else if (!strncmp(tbuf, "LH", 2)) { 312227825Stheraven strlcat(up->latlon, " ", sizeof(up->latlon)); 313227825Stheraven strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 314227825Stheraven write(pp->io.fd, "DB", 2); 315227825Stheraven return; 316227825Stheraven 317227825Stheraven } else if (!strncmp(tbuf, "DB", 2)) { 318227825Stheraven strlcat(up->latlon, " ", sizeof(up->latlon)); 319227825Stheraven strlcat(up->latlon, tbuf + 2, sizeof(up->latlon)); 320227825Stheraven record_clock_stats(&peer->srcadr, up->latlon); 321227825Stheraven#ifdef DEBUG 322227825Stheraven if (debug) 323227825Stheraven printf("arbiter: %s\n", up->latlon); 324227825Stheraven#endif 325227825Stheraven write(pp->io.fd, COMMAND_START_BCAST, 2); 326227825Stheraven } 327227825Stheraven } 328227825Stheraven 329227825Stheraven /* 330227825Stheraven * We get down to business, check the timecode format and decode 331227825Stheraven * its contents. If the timecode has valid length, but not in 332227825Stheraven * proper format, we declare bad format and exit. If the 333227825Stheraven * timecode has invalid length, which sometimes occurs when the 334227825Stheraven * B0 amputates the broadcast, we just quietly steal away. Note 335227825Stheraven * that the time quality character and receiver status string is 336227825Stheraven * tacked on the end for clockstats display. 337227825Stheraven */ 338227825Stheraven up->tcswitch++; 339227825Stheraven if (up->tcswitch <= 1 || temp < LENARB) 340227825Stheraven return; 341227825Stheraven 342227825Stheraven /* 343278724Sdim * Timecode format B5: "i yy ddd hh:mm:ss.000 " 344227825Stheraven */ 345227825Stheraven strlcpy(pp->a_lastcode, tbuf, sizeof(pp->a_lastcode)); 346227825Stheraven pp->a_lastcode[LENARB - 2] = up->qualchar; 347227825Stheraven strlcat(pp->a_lastcode, up->status, sizeof(pp->a_lastcode)); 348278724Sdim pp->lencode = strlen(pp->a_lastcode); 349227825Stheraven syncchar = ' '; 350278724Sdim if (sscanf(pp->a_lastcode, "%c%2d %3d %2d:%2d:%2d", 351227825Stheraven &syncchar, &pp->year, &pp->day, &pp->hour, 352227825Stheraven &pp->minute, &pp->second) != 6) { 353227825Stheraven refclock_report(peer, CEVNT_BADREPLY); 354227825Stheraven write(pp->io.fd, COMMAND_HALT_BCAST, 2); 355227825Stheraven return; 356227825Stheraven } 357227825Stheraven 358227825Stheraven /* 359227825Stheraven * We decode the clock dispersion from the time quality 360227825Stheraven * character. 361227825Stheraven */ 362227825Stheraven switch (up->qualchar) { 363227825Stheraven 364227825Stheraven case '0': /* locked, max accuracy */ 365227825Stheraven pp->disp = 1e-7; 366278724Sdim pp->lastref = pp->lastrec; 367227972Stheraven break; 368227972Stheraven 369227825Stheraven case '4': /* unlock accuracy < 1 us */ 370278724Sdim pp->disp = 1e-6; 371227825Stheraven break; 372227825Stheraven 373227825Stheraven case '5': /* unlock accuracy < 10 us */ 374278724Sdim pp->disp = 1e-5; 375227825Stheraven break; 376227825Stheraven 377278724Sdim case '6': /* unlock accuracy < 100 us */ 378227825Stheraven pp->disp = 1e-4; 379227825Stheraven break; 380227825Stheraven 381227825Stheraven case '7': /* unlock accuracy < 1 ms */ 382227825Stheraven pp->disp = .001; 383227825Stheraven break; 384227825Stheraven 385227825Stheraven case '8': /* unlock accuracy < 10 ms */ 386227825Stheraven pp->disp = .01; 387227825Stheraven break; 388227825Stheraven 389227825Stheraven case '9': /* unlock accuracy < 100 ms */ 390227825Stheraven pp->disp = .1; 391227825Stheraven break; 392227825Stheraven 393227825Stheraven case 'A': /* unlock accuracy < 1 s */ 394227825Stheraven pp->disp = 1; 395227825Stheraven break; 396227825Stheraven 397227825Stheraven case 'B': /* unlock accuracy < 10 s */ 398227825Stheraven pp->disp = 10; 399227825Stheraven break; 400227825Stheraven 401227825Stheraven case 'F': /* clock failure */ 402227825Stheraven pp->disp = MAXDISPERSE; 403227825Stheraven refclock_report(peer, CEVNT_FAULT); 404227825Stheraven write(pp->io.fd, COMMAND_HALT_BCAST, 2); 405227825Stheraven return; 406227825Stheraven 407227825Stheraven default: 408227825Stheraven pp->disp = MAXDISPERSE; 409227825Stheraven refclock_report(peer, CEVNT_BADREPLY); 410227825Stheraven write(pp->io.fd, COMMAND_HALT_BCAST, 2); 411227825Stheraven return; 412227825Stheraven } 413227825Stheraven if (syncchar != ' ') 414278724Sdim pp->leap = LEAP_NOTINSYNC; 415227825Stheraven else 416227825Stheraven pp->leap = LEAP_NOWARNING; 417227825Stheraven 418227825Stheraven /* 419227825Stheraven * Process the new sample in the median filter and determine the 420227825Stheraven * timecode timestamp. 421227825Stheraven */ 422227825Stheraven if (!refclock_process(pp)) 423227825Stheraven refclock_report(peer, CEVNT_BADTIME); 424227825Stheraven else if (peer->disp > MAXDISTANCE) 425227825Stheraven refclock_receive(peer); 426227825Stheraven 427227825Stheraven /* if (up->tcswitch >= MAXSTAGE) { */ 428227825Stheraven write(pp->io.fd, COMMAND_HALT_BCAST, 2); 429227825Stheraven /* } */ 430227825Stheraven} 431227825Stheraven 432227825Stheraven 433227825Stheraven/* 434227825Stheraven * arb_poll - called by the transmit procedure 435227825Stheraven */ 436227825Stheravenstatic void 437227825Stheravenarb_poll( 438227825Stheraven int unit, 439227825Stheraven struct peer *peer 440227825Stheraven ) 441227825Stheraven{ 442227825Stheraven register struct arbunit *up; 443227825Stheraven struct refclockproc *pp; 444227825Stheraven 445227825Stheraven /* 446227825Stheraven * Time to poll the clock. The Arbiter clock responds to a "B5" 447227825Stheraven * by returning a timecode in the format specified above. 448227825Stheraven * Transmission occurs once per second, unless turned off by a 449227825Stheraven * "B0". Note there is no checking on state, since this may not 450227825Stheraven * be the only customer reading the clock. Only one customer 451227825Stheraven * need poll the clock; all others just listen in. 452227825Stheraven */ 453227825Stheraven pp = peer->procptr; 454227825Stheraven up = pp->unitptr; 455227825Stheraven pp->polls++; 456227825Stheraven up->tcswitch = 0; 457227825Stheraven if (write(pp->io.fd, "TQ", 2) != 2) 458227825Stheraven refclock_report(peer, CEVNT_FAULT); 459227825Stheraven 460227825Stheraven /* 461227825Stheraven * Process median filter samples. If none received, declare a 462227825Stheraven * timeout and keep going. 463227825Stheraven */ 464278724Sdim if (pp->coderecv == pp->codeproc) { 465278724Sdim refclock_report(peer, CEVNT_TIMEOUT); 466278724Sdim return; 467278724Sdim } 468278724Sdim refclock_receive(peer); 469278724Sdim record_clock_stats(&peer->srcadr, pp->a_lastcode); 470278724Sdim#ifdef DEBUG 471278724Sdim if (debug) 472227825Stheraven printf("arbiter: timecode %d %s\n", 473227825Stheraven pp->lencode, pp->a_lastcode); 474278724Sdim#endif 475278724Sdim} 476278724Sdim 477278724Sdim#else 478int refclock_arbiter_bs; 479#endif /* REFCLOCK */ 480