diff options
author | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
---|---|---|
committer | roberto <roberto@FreeBSD.org> | 1999-12-09 13:01:21 +0000 |
commit | ef64b99e8412f2273dd2e8b3291c2f78ffc4667f (patch) | |
tree | fc0cfa1aab0ff6b228f511b410733ef4f35d1ead /contrib/ntp/ntpd/refclock_as2201.c | |
download | FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.zip FreeBSD-src-ef64b99e8412f2273dd2e8b3291c2f78ffc4667f.tar.gz |
Virgin import of ntpd 4.0.98f
Diffstat (limited to 'contrib/ntp/ntpd/refclock_as2201.c')
-rw-r--r-- | contrib/ntp/ntpd/refclock_as2201.c | 388 |
1 files changed, 388 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/refclock_as2201.c b/contrib/ntp/ntpd/refclock_as2201.c new file mode 100644 index 0000000..2c10440 --- /dev/null +++ b/contrib/ntp/ntpd/refclock_as2201.c @@ -0,0 +1,388 @@ +/* + * refclock_as2201 - clock driver for the Austron 2201A GPS + * Timing Receiver + */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#if defined(REFCLOCK) && defined(CLOCK_AS2201) + +#include <stdio.h> +#include <ctype.h> +#include <sys/time.h> + +#include "ntpd.h" +#include "ntp_io.h" +#include "ntp_refclock.h" +#include "ntp_unixtime.h" +#include "ntp_stdlib.h" + +/* + * This driver supports the Austron 2200A/2201A GPS Receiver with + * Buffered RS-232-C Interface Module. Note that the original 2200/2201 + * receivers will not work reliably with this driver, since the older + * design cannot accept input commands at any reasonable data rate. + * + * The program sends a "*toc\r" to the radio and expects a response of + * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd = + * day of year, hh:mm:ss = second of day and mmm = millisecond of + * second. Then, it sends statistics commands to the radio and expects + * a multi-line reply showing the corresponding statistics or other + * selected data. Statistics commands are sent in order as determined by + * a vector of commands; these might have to be changed with different + * radio options. If flag4 of the fudge configuration command is set to + * 1, the statistics data are written to the clockstats file for later + * processing. + * + * In order for this code to work, the radio must be placed in non- + * interactive mode using the "off" command and with a single <cr> + * response using the "term cr" command. The setting of the "echo" + * and "df" commands does not matter. The radio should select UTC + * timescale using the "ts utc" command. + * + * There are two modes of operation for this driver. The first with + * default configuration is used with stock kernels and serial-line + * drivers and works with almost any machine. In this mode the driver + * assumes the radio captures a timestamp upon receipt of the "*" that + * begins the driver query. Accuracies in this mode are in the order of + * a millisecond or two and the receiver can be connected to only one + * host. + * + * The second mode of operation can be used for SunOS kernels that have + * been modified with the ppsclock streams module included in this + * distribution. The mode is enabled if flag3 of the fudge configuration + * command has been set to 1. In this mode a precise timestamp is + * available using a gadget box and 1-pps signal from the receiver. This + * improves the accuracy to the order of a few tens of microseconds. In + * addition, the serial output and 1-pps signal can be bussed to more + * than one hosts, but only one of them should be connected to the + * radio input data line. + */ + +/* + * GPS Definitions + */ +#define SMAX 200 /* statistics buffer length */ +#define DEVICE "/dev/gps%d" /* device name and unit */ +#define SPEED232 B9600 /* uart speed (9600 baud) */ +#define PRECISION (-20) /* precision assumed (about 1 us) */ +#define REFID "GPS\0" /* reference ID */ +#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */ + +#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */ + +/* + * AS2201 unit control structure. + */ +struct as2201unit { + char *lastptr; /* statistics buffer pointer */ + char stats[SMAX]; /* statistics buffer */ + int linect; /* count of lines remaining */ + int index; /* current statistics command */ +}; + +/* + * Radio commands to extract statitistics + * + * A command consists of an ASCII string terminated by a <cr> (\r). The + * command list consist of a sequence of commands terminated by a null + * string ("\0"). One command from the list is sent immediately + * following each received timecode (*toc\r command) and the ASCII + * strings received from the radio are saved along with the timecode in + * the clockstats file. Subsequent commands are sent at each timecode, + * with the last one in the list followed by the first one. The data + * received from the radio consist of ASCII strings, each terminated by + * a <cr> (\r) character. The number of strings for each command is + * specified as the first line of output as an ASCII-encode number. Note + * that the ETF command requires the Input Buffer Module and the LORAN + * commands require the LORAN Assist Module. However, if these modules + * are not installed, the radio and this driver will continue to operate + * successfuly, but no data will be captured for these commands. + */ +static char stat_command[][30] = { + "ITF\r", /* internal time/frequency */ + "ETF\r", /* external time/frequency */ + "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */ + "LORAN TDATA\r", /* LORAN signal data */ + "ID;OPT;VER\r", /* model; options; software version */ + + "ITF\r", /* internal time/frequency */ + "ETF\r", /* external time/frequency */ + "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */ + "TRSTAT\r", /* satellite tracking status */ + "POS;PPS;PPSOFF\r", /* position, pps source, offsets */ + + "ITF\r", /* internal time/frequency */ + "ETF\r", /* external time/frequency */ + "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */ + "LORAN TDATA\r", /* LORAN signal data */ + "UTC\r", /* UTC leap info */ + + "ITF\r", /* internal time/frequency */ + "ETF\r", /* external time/frequency */ + "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */ + "TRSTAT\r", /* satellite tracking status */ + "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */ + "\0" /* end of table */ +}; + +/* + * Function prototypes + */ +static int as2201_start P((int, struct peer *)); +static void as2201_shutdown P((int, struct peer *)); +static void as2201_receive P((struct recvbuf *)); +static void as2201_poll P((int, struct peer *)); + +/* + * Transfer vector + */ +struct refclock refclock_as2201 = { + as2201_start, /* start up driver */ + as2201_shutdown, /* shut down driver */ + as2201_poll, /* transmit poll message */ + noentry, /* not used (old as2201_control) */ + noentry, /* initialize driver (not used) */ + noentry, /* not used (old as2201_buginfo) */ + NOFLAGS /* not used */ +}; + + +/* + * as2201_start - open the devices and initialize data for processing + */ +static int +as2201_start( + int unit, + struct peer *peer + ) +{ + register struct as2201unit *up; + struct refclockproc *pp; + int fd; + char gpsdev[20]; + + /* + * Open serial port. Use CLK line discipline, if available. + */ + (void)sprintf(gpsdev, DEVICE, unit); + if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK))) + return (0); + + /* + * Allocate and initialize unit structure + */ + if (!(up = (struct as2201unit *) + emalloc(sizeof(struct as2201unit)))) { + (void) close(fd); + return (0); + } + memset((char *)up, 0, sizeof(struct as2201unit)); + pp = peer->procptr; + pp->io.clock_recv = as2201_receive; + pp->io.srcclock = (caddr_t)peer; + pp->io.datalen = 0; + pp->io.fd = fd; + if (!io_addclock(&pp->io)) { + (void) close(fd); + free(up); + return (0); + } + pp->unitptr = (caddr_t)up; + + /* + * Initialize miscellaneous variables + */ + peer->precision = PRECISION; + peer->burst = NSTAGE; + pp->clockdesc = DESCRIPTION; + memcpy((char *)&pp->refid, REFID, 4); + up->lastptr = up->stats; + up->index = 0; + return (1); +} + + +/* + * as2201_shutdown - shut down the clock + */ +static void +as2201_shutdown( + int unit, + struct peer *peer + ) +{ + register struct as2201unit *up; + struct refclockproc *pp; + + pp = peer->procptr; + up = (struct as2201unit *)pp->unitptr; + io_closeclock(&pp->io); + free(up); +} + + +/* + * as2201__receive - receive data from the serial interface + */ +static void +as2201_receive( + struct recvbuf *rbufp + ) +{ + register struct as2201unit *up; + struct refclockproc *pp; + struct peer *peer; + l_fp trtmp; + + /* + * Initialize pointers and read the timecode and timestamp. + */ + peer = (struct peer *)rbufp->recv_srcclock; + pp = peer->procptr; + up = (struct as2201unit *)pp->unitptr; + pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp); +#ifdef DEBUG + if (debug) + printf("gps: timecode %d %d %s\n", + up->linect, pp->lencode, pp->a_lastcode); +#endif + if (pp->lencode == 0) + return; + + /* + * If linect is greater than zero, we must be in the middle of a + * statistics operation, so simply tack the received data at the + * end of the statistics string. If not, we could either have + * just received the timecode itself or a decimal number + * indicating the number of following lines of the statistics + * reply. In the former case, write the accumulated statistics + * data to the clockstats file and continue onward to process + * the timecode; in the later case, save the number of lines and + * quietly return. + */ + if (pp->sloppyclockflag & CLK_FLAG2) + pp->lastrec = trtmp; + if (up->linect > 0) { + up->linect--; + if ((int)(up->lastptr - up->stats + pp->lencode) > SMAX - 2) + return; + *up->lastptr++ = ' '; + (void)strcpy(up->lastptr, pp->a_lastcode); + up->lastptr += pp->lencode; + return; + } else { + if (pp->lencode == 1) { + up->linect = atoi(pp->a_lastcode); + return; + } else { + record_clock_stats(&peer->srcadr, up->stats); +#ifdef DEBUG + if (debug) + printf("gps: stat %s\n", up->stats); +#endif + } + } + up->lastptr = up->stats; + *up->lastptr = '\0'; + + /* + * We get down to business, check the timecode format and decode + * its contents. If the timecode has invalid length or is not in + * proper format, we declare bad format and exit. + */ + if (pp->lencode < LENTOC) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Timecode format: "yy:ddd:hh:mm:ss.mmm" + */ + if (sscanf(pp->a_lastcode, "%2d:%3d:%2d:%2d:%2d.%3d", &pp->year, + &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->msec) + != 6) { + refclock_report(peer, CEVNT_BADREPLY); + return; + } + + /* + * Test for synchronization (this is a temporary crock). + */ + if (pp->a_lastcode[2] != ':') + pp->leap = LEAP_NOTINSYNC; + else + pp->leap = LEAP_NOWARNING; + + /* + * Process the new sample in the median filter and determine the + * timecode timestamp. + */ + if (!refclock_process(pp)) { + refclock_report(peer, CEVNT_BADTIME); + return; + } + + /* + * If CLK_FLAG4 is set, initialize the statistics buffer and + * send the next command. If not, simply write the timecode to + * the clockstats file. + */ + (void)strcpy(up->lastptr, pp->a_lastcode); + up->lastptr += pp->lencode; + if (pp->sloppyclockflag & CLK_FLAG4) { + *up->lastptr++ = ' '; + (void)strcpy(up->lastptr, stat_command[up->index]); + up->lastptr += strlen(stat_command[up->index]); + up->lastptr--; + *up->lastptr = '\0'; + (void)write(pp->io.fd, stat_command[up->index], + strlen(stat_command[up->index])); + up->index++; + if (*stat_command[up->index] == '\0') + up->index = 0; + } +} + + +/* + * as2201_poll - called by the transmit procedure + * + * We go to great pains to avoid changing state here, since there may be + * more than one eavesdropper receiving the same timecode. + */ +static void +as2201_poll( + int unit, + struct peer *peer + ) +{ + struct refclockproc *pp; + + /* + * Send a "\r*toc\r" to get things going. We go to great pains + * to avoid changing state, since there may be more than one + * eavesdropper watching the radio. + */ + pp = peer->procptr; + if (write(pp->io.fd, "\r*toc\r", 6) != 6) { + refclock_report(peer, CEVNT_FAULT); + } else { + pp->polls++; + if (!(pp->sloppyclockflag & CLK_FLAG2)) + get_systime(&pp->lastrec); + } + if (peer->burst > 0) + return; + if (pp->coderecv == pp->codeproc) { + refclock_report(peer, CEVNT_TIMEOUT); + return; + } + refclock_receive(peer); + peer->burst = NSTAGE; +} + +#else +int refclock_as2201_bs; +#endif /* REFCLOCK */ |