Annotation of embedaddon/ntp/ntpd/refclock_as2201.c, revision 1.1
1.1 ! misho 1: /*
! 2: * refclock_as2201 - clock driver for the Austron 2201A GPS
! 3: * Timing Receiver
! 4: */
! 5: #ifdef HAVE_CONFIG_H
! 6: #include <config.h>
! 7: #endif
! 8:
! 9: #if defined(REFCLOCK) && defined(CLOCK_AS2201)
! 10:
! 11: #include "ntpd.h"
! 12: #include "ntp_io.h"
! 13: #include "ntp_refclock.h"
! 14: #include "ntp_unixtime.h"
! 15: #include "ntp_stdlib.h"
! 16:
! 17: #include <stdio.h>
! 18: #include <ctype.h>
! 19:
! 20: /*
! 21: * This driver supports the Austron 2200A/2201A GPS Receiver with
! 22: * Buffered RS-232-C Interface Module. Note that the original 2200/2201
! 23: * receivers will not work reliably with this driver, since the older
! 24: * design cannot accept input commands at any reasonable data rate.
! 25: *
! 26: * The program sends a "*toc\r" to the radio and expects a response of
! 27: * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
! 28: * day of year, hh:mm:ss = second of day and mmm = millisecond of
! 29: * second. Then, it sends statistics commands to the radio and expects
! 30: * a multi-line reply showing the corresponding statistics or other
! 31: * selected data. Statistics commands are sent in order as determined by
! 32: * a vector of commands; these might have to be changed with different
! 33: * radio options. If flag4 of the fudge configuration command is set to
! 34: * 1, the statistics data are written to the clockstats file for later
! 35: * processing.
! 36: *
! 37: * In order for this code to work, the radio must be placed in non-
! 38: * interactive mode using the "off" command and with a single <cr>
! 39: * response using the "term cr" command. The setting of the "echo"
! 40: * and "df" commands does not matter. The radio should select UTC
! 41: * timescale using the "ts utc" command.
! 42: *
! 43: * There are two modes of operation for this driver. The first with
! 44: * default configuration is used with stock kernels and serial-line
! 45: * drivers and works with almost any machine. In this mode the driver
! 46: * assumes the radio captures a timestamp upon receipt of the "*" that
! 47: * begins the driver query. Accuracies in this mode are in the order of
! 48: * a millisecond or two and the receiver can be connected to only one
! 49: * host.
! 50: *
! 51: * The second mode of operation can be used for SunOS kernels that have
! 52: * been modified with the ppsclock streams module included in this
! 53: * distribution. The mode is enabled if flag3 of the fudge configuration
! 54: * command has been set to 1. In this mode a precise timestamp is
! 55: * available using a gadget box and 1-pps signal from the receiver. This
! 56: * improves the accuracy to the order of a few tens of microseconds. In
! 57: * addition, the serial output and 1-pps signal can be bussed to more
! 58: * than one hosts, but only one of them should be connected to the
! 59: * radio input data line.
! 60: */
! 61:
! 62: /*
! 63: * GPS Definitions
! 64: */
! 65: #define SMAX 200 /* statistics buffer length */
! 66: #define DEVICE "/dev/gps%d" /* device name and unit */
! 67: #define SPEED232 B9600 /* uart speed (9600 baud) */
! 68: #define PRECISION (-20) /* precision assumed (about 1 us) */
! 69: #define REFID "GPS\0" /* reference ID */
! 70: #define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
! 71:
! 72: #define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
! 73:
! 74: /*
! 75: * AS2201 unit control structure.
! 76: */
! 77: struct as2201unit {
! 78: char *lastptr; /* statistics buffer pointer */
! 79: char stats[SMAX]; /* statistics buffer */
! 80: int linect; /* count of lines remaining */
! 81: int index; /* current statistics command */
! 82: };
! 83:
! 84: /*
! 85: * Radio commands to extract statitistics
! 86: *
! 87: * A command consists of an ASCII string terminated by a <cr> (\r). The
! 88: * command list consist of a sequence of commands terminated by a null
! 89: * string ("\0"). One command from the list is sent immediately
! 90: * following each received timecode (*toc\r command) and the ASCII
! 91: * strings received from the radio are saved along with the timecode in
! 92: * the clockstats file. Subsequent commands are sent at each timecode,
! 93: * with the last one in the list followed by the first one. The data
! 94: * received from the radio consist of ASCII strings, each terminated by
! 95: * a <cr> (\r) character. The number of strings for each command is
! 96: * specified as the first line of output as an ASCII-encode number. Note
! 97: * that the ETF command requires the Input Buffer Module and the LORAN
! 98: * commands require the LORAN Assist Module. However, if these modules
! 99: * are not installed, the radio and this driver will continue to operate
! 100: * successfuly, but no data will be captured for these commands.
! 101: */
! 102: static char stat_command[][30] = {
! 103: "ITF\r", /* internal time/frequency */
! 104: "ETF\r", /* external time/frequency */
! 105: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
! 106: "LORAN TDATA\r", /* LORAN signal data */
! 107: "ID;OPT;VER\r", /* model; options; software version */
! 108:
! 109: "ITF\r", /* internal time/frequency */
! 110: "ETF\r", /* external time/frequency */
! 111: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
! 112: "TRSTAT\r", /* satellite tracking status */
! 113: "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
! 114:
! 115: "ITF\r", /* internal time/frequency */
! 116: "ETF\r", /* external time/frequency */
! 117: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
! 118: "LORAN TDATA\r", /* LORAN signal data */
! 119: "UTC\r", /* UTC leap info */
! 120:
! 121: "ITF\r", /* internal time/frequency */
! 122: "ETF\r", /* external time/frequency */
! 123: "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
! 124: "TRSTAT\r", /* satellite tracking status */
! 125: "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
! 126: "\0" /* end of table */
! 127: };
! 128:
! 129: /*
! 130: * Function prototypes
! 131: */
! 132: static int as2201_start (int, struct peer *);
! 133: static void as2201_shutdown (int, struct peer *);
! 134: static void as2201_receive (struct recvbuf *);
! 135: static void as2201_poll (int, struct peer *);
! 136:
! 137: /*
! 138: * Transfer vector
! 139: */
! 140: struct refclock refclock_as2201 = {
! 141: as2201_start, /* start up driver */
! 142: as2201_shutdown, /* shut down driver */
! 143: as2201_poll, /* transmit poll message */
! 144: noentry, /* not used (old as2201_control) */
! 145: noentry, /* initialize driver (not used) */
! 146: noentry, /* not used (old as2201_buginfo) */
! 147: NOFLAGS /* not used */
! 148: };
! 149:
! 150:
! 151: /*
! 152: * as2201_start - open the devices and initialize data for processing
! 153: */
! 154: static int
! 155: as2201_start(
! 156: int unit,
! 157: struct peer *peer
! 158: )
! 159: {
! 160: register struct as2201unit *up;
! 161: struct refclockproc *pp;
! 162: int fd;
! 163: char gpsdev[20];
! 164:
! 165: /*
! 166: * Open serial port. Use CLK line discipline, if available.
! 167: */
! 168: snprintf(gpsdev, sizeof(gpsdev), DEVICE, unit);
! 169: if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
! 170: return (0);
! 171:
! 172: /*
! 173: * Allocate and initialize unit structure
! 174: */
! 175: up = emalloc(sizeof(*up));
! 176: memset(up, 0, sizeof(*up));
! 177: pp = peer->procptr;
! 178: pp->io.clock_recv = as2201_receive;
! 179: pp->io.srcclock = (caddr_t)peer;
! 180: pp->io.datalen = 0;
! 181: pp->io.fd = fd;
! 182: if (!io_addclock(&pp->io)) {
! 183: close(fd);
! 184: pp->io.fd = -1;
! 185: free(up);
! 186: return (0);
! 187: }
! 188: pp->unitptr = (caddr_t)up;
! 189:
! 190: /*
! 191: * Initialize miscellaneous variables
! 192: */
! 193: peer->precision = PRECISION;
! 194: peer->burst = NSTAGE;
! 195: pp->clockdesc = DESCRIPTION;
! 196: memcpy((char *)&pp->refid, REFID, 4);
! 197: up->lastptr = up->stats;
! 198: up->index = 0;
! 199: return (1);
! 200: }
! 201:
! 202:
! 203: /*
! 204: * as2201_shutdown - shut down the clock
! 205: */
! 206: static void
! 207: as2201_shutdown(
! 208: int unit,
! 209: struct peer *peer
! 210: )
! 211: {
! 212: register struct as2201unit *up;
! 213: struct refclockproc *pp;
! 214:
! 215: pp = peer->procptr;
! 216: up = (struct as2201unit *)pp->unitptr;
! 217: if (-1 != pp->io.fd)
! 218: io_closeclock(&pp->io);
! 219: if (NULL != up)
! 220: free(up);
! 221: }
! 222:
! 223:
! 224: /*
! 225: * as2201__receive - receive data from the serial interface
! 226: */
! 227: static void
! 228: as2201_receive(
! 229: struct recvbuf *rbufp
! 230: )
! 231: {
! 232: register struct as2201unit *up;
! 233: struct refclockproc *pp;
! 234: struct peer *peer;
! 235: l_fp trtmp;
! 236:
! 237: /*
! 238: * Initialize pointers and read the timecode and timestamp.
! 239: */
! 240: peer = (struct peer *)rbufp->recv_srcclock;
! 241: pp = peer->procptr;
! 242: up = (struct as2201unit *)pp->unitptr;
! 243: pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
! 244: #ifdef DEBUG
! 245: if (debug)
! 246: printf("gps: timecode %d %d %s\n",
! 247: up->linect, pp->lencode, pp->a_lastcode);
! 248: #endif
! 249: if (pp->lencode == 0)
! 250: return;
! 251:
! 252: /*
! 253: * If linect is greater than zero, we must be in the middle of a
! 254: * statistics operation, so simply tack the received data at the
! 255: * end of the statistics string. If not, we could either have
! 256: * just received the timecode itself or a decimal number
! 257: * indicating the number of following lines of the statistics
! 258: * reply. In the former case, write the accumulated statistics
! 259: * data to the clockstats file and continue onward to process
! 260: * the timecode; in the later case, save the number of lines and
! 261: * quietly return.
! 262: */
! 263: if (pp->sloppyclockflag & CLK_FLAG2)
! 264: pp->lastrec = trtmp;
! 265: if (up->linect > 0) {
! 266: up->linect--;
! 267: if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2)
! 268: return;
! 269: *up->lastptr++ = ' ';
! 270: (void)strcpy(up->lastptr, pp->a_lastcode);
! 271: up->lastptr += pp->lencode;
! 272: return;
! 273: } else {
! 274: if (pp->lencode == 1) {
! 275: up->linect = atoi(pp->a_lastcode);
! 276: return;
! 277: } else {
! 278: record_clock_stats(&peer->srcadr, up->stats);
! 279: #ifdef DEBUG
! 280: if (debug)
! 281: printf("gps: stat %s\n", up->stats);
! 282: #endif
! 283: }
! 284: }
! 285: up->lastptr = up->stats;
! 286: *up->lastptr = '\0';
! 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 < LENTOC) {
! 294: refclock_report(peer, CEVNT_BADREPLY);
! 295: return;
! 296: }
! 297:
! 298: /*
! 299: * Timecode format: "yy:ddd:hh:mm:ss.mmm"
! 300: */
! 301: if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3ld", &pp->year,
! 302: &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->nsec)
! 303: != 6) {
! 304: refclock_report(peer, CEVNT_BADREPLY);
! 305: return;
! 306: }
! 307: pp->nsec *= 1000000;
! 308:
! 309: /*
! 310: * Test for synchronization (this is a temporary crock).
! 311: */
! 312: if (pp->a_lastcode[2] != ':')
! 313: pp->leap = LEAP_NOTINSYNC;
! 314: else
! 315: pp->leap = LEAP_NOWARNING;
! 316:
! 317: /*
! 318: * Process the new sample in the median filter and determine the
! 319: * timecode timestamp.
! 320: */
! 321: if (!refclock_process(pp)) {
! 322: refclock_report(peer, CEVNT_BADTIME);
! 323: return;
! 324: }
! 325:
! 326: /*
! 327: * If CLK_FLAG4 is set, initialize the statistics buffer and
! 328: * send the next command. If not, simply write the timecode to
! 329: * the clockstats file.
! 330: */
! 331: (void)strcpy(up->lastptr, pp->a_lastcode);
! 332: up->lastptr += pp->lencode;
! 333: if (pp->sloppyclockflag & CLK_FLAG4) {
! 334: *up->lastptr++ = ' ';
! 335: (void)strcpy(up->lastptr, stat_command[up->index]);
! 336: up->lastptr += strlen(stat_command[up->index]);
! 337: up->lastptr--;
! 338: *up->lastptr = '\0';
! 339: (void)write(pp->io.fd, stat_command[up->index],
! 340: strlen(stat_command[up->index]));
! 341: up->index++;
! 342: if (*stat_command[up->index] == '\0')
! 343: up->index = 0;
! 344: }
! 345: }
! 346:
! 347:
! 348: /*
! 349: * as2201_poll - called by the transmit procedure
! 350: *
! 351: * We go to great pains to avoid changing state here, since there may be
! 352: * more than one eavesdropper receiving the same timecode.
! 353: */
! 354: static void
! 355: as2201_poll(
! 356: int unit,
! 357: struct peer *peer
! 358: )
! 359: {
! 360: struct refclockproc *pp;
! 361:
! 362: /*
! 363: * Send a "\r*toc\r" to get things going. We go to great pains
! 364: * to avoid changing state, since there may be more than one
! 365: * eavesdropper watching the radio.
! 366: */
! 367: pp = peer->procptr;
! 368: if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
! 369: refclock_report(peer, CEVNT_FAULT);
! 370: } else {
! 371: pp->polls++;
! 372: if (!(pp->sloppyclockflag & CLK_FLAG2))
! 373: get_systime(&pp->lastrec);
! 374: }
! 375: if (peer->burst > 0)
! 376: return;
! 377: if (pp->coderecv == pp->codeproc) {
! 378: refclock_report(peer, CEVNT_TIMEOUT);
! 379: return;
! 380: }
! 381: refclock_receive(peer);
! 382: peer->burst = NSTAGE;
! 383: }
! 384:
! 385: #else
! 386: int refclock_as2201_bs;
! 387: #endif /* REFCLOCK */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>