Annotation of embedaddon/ntp/ntpd/refclock_trak.c, revision 1.1
1.1 ! misho 1: /*
! 2: * refclock_trak - clock driver for the TRAK 8810 GPS Station Clock
! 3: *
! 4: * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp>
! 5: * original version Dec, 1993
! 6: * revised version Sep, 1994 for ntp3.4e or later
! 7: */
! 8:
! 9: #ifdef HAVE_CONFIG_H
! 10: #include <config.h>
! 11: #endif
! 12:
! 13: #if defined(REFCLOCK) && defined(CLOCK_TRAK) && defined(PPS)
! 14:
! 15: #include "ntpd.h"
! 16: #include "ntp_io.h"
! 17: #include "ntp_refclock.h"
! 18: #include "ntp_stdlib.h"
! 19: #include "ntp_unixtime.h"
! 20:
! 21: #include <stdio.h>
! 22: #include <ctype.h>
! 23:
! 24: #ifdef HAVE_SYS_TERMIOS_H
! 25: # include <sys/termios.h>
! 26: #endif
! 27: #ifdef HAVE_SYS_PPSCLOCK_H
! 28: # include <sys/ppsclock.h>
! 29: #endif
! 30:
! 31: /*
! 32: * This driver supports the TRAK 8810/8820 GPS Station Clock. The claimed
! 33: * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
! 34: * signal; however, in most cases the actual accuracy is limited by the
! 35: * precision of the timecode and the latencies of the serial interface
! 36: * and operating system.
! 37: *
! 38: * For best accuracy, this radio requires the LDISC_ACTS line
! 39: * discipline, which captures a timestamp at the '*' on-time character
! 40: * of the timecode. Using this discipline the jitter is in the order of
! 41: * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
! 42: * timestamp is used, which is captured at the \r ending the timecode
! 43: * message. This introduces a systematic error of 23 character times, or
! 44: * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
! 45: * IPC-class machines.
! 46: *
! 47: * Using the memus, the radio should be set for 9600 bps, one stop bit
! 48: * and no parity. It should be set to operate in computer (no echo)
! 49: * mode. The timecode format includes neither the year nor leap-second
! 50: * warning. No provisions are included in this preliminary version of
! 51: * the driver to read and record detailed internal radio status.
! 52: *
! 53: * In operation, this driver sends a RQTS\r request to the radio at
! 54: * initialization in order to put it in continuous time output mode. The
! 55: * radio then sends the following message once each second:
! 56: *
! 57: * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
! 58: *
! 59: * on-time = '*' * ddd = day of year
! 60: * hh:mm:ss = hours, minutes, seconds
! 61: * q = quality indicator (phase error), 0-6:
! 62: * 0 > 20 us
! 63: * 6 > 10 us
! 64: * 5 > 1 us
! 65: * 4 > 100 ns
! 66: * 3 > 10 ns
! 67: * 2 < 10 ns
! 68: *
! 69: * The alarm condition is indicated by '0' at Q, which means the radio
! 70: * has a phase error than 20 usec relative to the broadcast time. The
! 71: * absence of year, DST and leap-second warning in this format is also
! 72: * alarming.
! 73: *
! 74: * The continuous time mode is disabled using the RQTX<cr> request,
! 75: * following which the radio sends a RQTX DONE<cr><lf> response. In the
! 76: * normal mode, other control and status requests are effective,
! 77: * including the leap-second status request RQLS<cr>. The radio responds
! 78: * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
! 79: * day. Presumably, this gives the epoch of the next leap second,
! 80: * RQLS 00,00,00 if none is specified in the GPS message. Specified in
! 81: * this form, the information is generally useless and is ignored by
! 82: * the driver.
! 83: *
! 84: * Fudge Factors
! 85: *
! 86: * There are no special fudge factors other than the generic.
! 87: */
! 88:
! 89: /*
! 90: * Interface definitions
! 91: */
! 92: #define DEVICE "/dev/trak%d" /* device name and unit */
! 93: #define SPEED232 B9600 /* uart speed (9600 baud) */
! 94: #define PRECISION (-20) /* precision assumed (about 1 us) */
! 95: #define REFID "GPS\0" /* reference ID */
! 96: #define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
! 97:
! 98: #define LENTRAK 24 /* timecode length */
! 99: #define C_CTO "RQTS\r" /* start continuous time output */
! 100:
! 101: /*
! 102: * Unit control structure
! 103: */
! 104: struct trakunit {
! 105: int polled; /* poll message flag */
! 106: l_fp tstamp; /* timestamp of last poll */
! 107: };
! 108:
! 109: /*
! 110: * Function prototypes
! 111: */
! 112: static int trak_start P((int, struct peer *));
! 113: static void trak_shutdown P((int, struct peer *));
! 114: static void trak_receive P((struct recvbuf *));
! 115: static void trak_poll P((int, struct peer *));
! 116:
! 117: /*
! 118: * Transfer vector
! 119: */
! 120: struct refclock refclock_trak = {
! 121: trak_start, /* start up driver */
! 122: trak_shutdown, /* shut down driver */
! 123: trak_poll, /* transmit poll message */
! 124: noentry, /* not used (old trak_control) */
! 125: noentry, /* initialize driver (not used) */
! 126: noentry, /* not used (old trak_buginfo) */
! 127: NOFLAGS /* not used */
! 128: };
! 129:
! 130:
! 131: /*
! 132: * trak_start - open the devices and initialize data for processing
! 133: */
! 134: static int
! 135: trak_start(
! 136: int unit,
! 137: struct peer *peer
! 138: )
! 139: {
! 140: register struct trakunit *up;
! 141: struct refclockproc *pp;
! 142: int fd;
! 143: char device[20];
! 144:
! 145: /*
! 146: * Open serial port. The LDISC_ACTS line discipline inserts a
! 147: * timestamp following the "*" on-time character of the
! 148: * timecode.
! 149: */
! 150: snprintf(device, sizeof(device), DEVICE, unit);
! 151: if (
! 152: #ifdef PPS
! 153: !(fd = refclock_open(device, SPEED232, LDISC_CLK))
! 154: #else
! 155: !(fd = refclock_open(device, SPEED232, 0))
! 156: #endif /* PPS */
! 157: )
! 158: return (0);
! 159:
! 160: /*
! 161: * Allocate and initialize unit structure
! 162: */
! 163: up = emalloc(sizeof(*up));
! 164: memset(up, 0, sizeof(*up));
! 165: pp = peer->procptr;
! 166: pp->io.clock_recv = trak_receive;
! 167: pp->io.srcclock = (caddr_t)peer;
! 168: pp->io.datalen = 0;
! 169: pp->io.fd = fd;
! 170: if (!io_addclock(&pp->io)) {
! 171: (void) close(fd);
! 172: pp->io.fd = -1;
! 173: free(up);
! 174: return (0);
! 175: }
! 176: pp->unitptr = (caddr_t)up;
! 177:
! 178: /*
! 179: * Initialize miscellaneous variables
! 180: */
! 181: peer->precision = PRECISION;
! 182: pp->clockdesc = DESCRIPTION;
! 183: memcpy((char *)&pp->refid, REFID, 4);
! 184: up->polled = 0;
! 185:
! 186: /*
! 187: * Start continuous time output. If something breaks, fold the
! 188: * tent and go home.
! 189: */
! 190: if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
! 191: refclock_report(peer, CEVNT_FAULT);
! 192: (void) close(fd);
! 193: free(up);
! 194: return (0);
! 195: }
! 196: return (1);
! 197: }
! 198:
! 199:
! 200: /*
! 201: * trak_shutdown - shut down the clock
! 202: */
! 203: static void
! 204: trak_shutdown(
! 205: int unit,
! 206: struct peer *peer
! 207: )
! 208: {
! 209: register struct trakunit *up;
! 210: struct refclockproc *pp;
! 211:
! 212: pp = peer->procptr;
! 213: up = (struct trakunit *)pp->unitptr;
! 214: if (-1 != pp->io.fd)
! 215: io_closeclock(&pp->io);
! 216: if (NULL != up)
! 217: free(up);
! 218: }
! 219:
! 220:
! 221: /*
! 222: * trak_receive - receive data from the serial interface
! 223: */
! 224: static void
! 225: trak_receive(
! 226: struct recvbuf *rbufp
! 227: )
! 228: {
! 229: register struct trakunit *up;
! 230: struct refclockproc *pp;
! 231: struct peer *peer;
! 232: l_fp trtmp;
! 233: char *dpt, *dpend;
! 234: char qchar;
! 235: #ifdef PPS
! 236: struct ppsclockev ppsev;
! 237: int request;
! 238: #ifdef HAVE_CIOGETEV
! 239: request = CIOGETEV;
! 240: #endif
! 241: #ifdef HAVE_TIOCGPPSEV
! 242: request = TIOCGPPSEV;
! 243: #endif
! 244: #endif /* PPS */
! 245:
! 246: /*
! 247: * Initialize pointers and read the timecode and timestamp. We
! 248: * then chuck out everything, including runts, except one
! 249: * message each poll interval.
! 250: */
! 251: peer = (struct peer *)rbufp->recv_srcclock;
! 252: pp = peer->procptr;
! 253: up = (struct trakunit *)pp->unitptr;
! 254: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX,
! 255: &pp->lastrec);
! 256:
! 257: /*
! 258: * We get a buffer and timestamp following the '*' on-time
! 259: * character. If a valid timestamp, we use that in place of the
! 260: * buffer timestamp and edit out the timestamp for prettyprint
! 261: * billboards.
! 262: */
! 263: dpt = pp->a_lastcode;
! 264: dpend = dpt + pp->lencode;
! 265: if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
! 266: if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
! 267: pp->lastrec.l_i + 1) {
! 268: pp->lastrec = trtmp;
! 269: dpt += 9;
! 270: while (dpt < dpend) {
! 271: *(dpt - 8) = *dpt;
! 272: ++dpt;
! 273: }
! 274: }
! 275: }
! 276: if (up->polled == 0) return;
! 277: up->polled = 0;
! 278: #ifndef PPS
! 279: get_systime(&up->tstamp);
! 280: #endif
! 281: record_clock_stats(&peer->srcadr, pp->a_lastcode);
! 282: #ifdef DEBUG
! 283: if (debug)
! 284: printf("trak: timecode %d %s\n", pp->lencode,
! 285: pp->a_lastcode);
! 286: #endif
! 287:
! 288: /*
! 289: * We get down to business, check the timecode format and decode
! 290: * its contents. If the timecode has invalid length or is not in
! 291: * proper format, we declare bad format and exit.
! 292: */
! 293: if (pp->lencode < LENTRAK) {
! 294: refclock_report(peer, CEVNT_BADREPLY);
! 295: return;
! 296: }
! 297:
! 298: /*
! 299: * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
! 300: */
! 301: if (sscanf(pp->a_lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
! 302: &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
! 303: refclock_report(peer, CEVNT_BADREPLY);
! 304: return;
! 305: }
! 306:
! 307: /*
! 308: * Decode quality and leap characters. If unsynchronized, set
! 309: * the leap bits accordingly and exit.
! 310: */
! 311: if (qchar == '0') {
! 312: pp->leap = LEAP_NOTINSYNC;
! 313: return;
! 314: }
! 315: #ifdef PPS
! 316: if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
! 317: ppsev.tv.tv_sec += (u_int32) JAN_1970;
! 318: TVTOTS(&ppsev.tv,&up->tstamp);
! 319: }
! 320: #endif /* PPS */
! 321: /* record the last ppsclock event time stamp */
! 322: pp->lastrec = up->tstamp;
! 323: if (!refclock_process(pp)) {
! 324: refclock_report(peer, CEVNT_BADTIME);
! 325: return;
! 326: }
! 327: pp->lastref = pp->lastrec;
! 328: refclock_receive(peer);
! 329: }
! 330:
! 331:
! 332: /*
! 333: * trak_poll - called by the transmit procedure
! 334: */
! 335: static void
! 336: trak_poll(
! 337: int unit,
! 338: struct peer *peer
! 339: )
! 340: {
! 341: register struct trakunit *up;
! 342: struct refclockproc *pp;
! 343:
! 344: /*
! 345: * We don't really do anything here, except arm the receiving
! 346: * side to capture a sample and check for timeouts.
! 347: */
! 348: pp = peer->procptr;
! 349: up = (struct trakunit *)pp->unitptr;
! 350: if (up->polled)
! 351: refclock_report(peer, CEVNT_TIMEOUT);
! 352: pp->polls++;
! 353: up->polled = 1;
! 354: }
! 355:
! 356: #else
! 357: int refclock_trak_bs;
! 358: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>