File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_trak.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_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>