File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_as2201.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue May 29 12:08:38 2012 UTC (12 years, 1 month ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    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>