File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_pst.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, 2 months ago) by misho
Branches: ntp, MAIN
CVS tags: v4_2_6p5p0, v4_2_6p5, HEAD
ntp 4.2.6p5

    1: /*
    2:  * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
    3:  */
    4: 
    5: #ifdef HAVE_CONFIG_H
    6: #include <config.h>
    7: #endif
    8: 
    9: #if defined(REFCLOCK) && defined(CLOCK_PST)
   10: 
   11: #include "ntpd.h"
   12: #include "ntp_io.h"
   13: #include "ntp_refclock.h"
   14: #include "ntp_stdlib.h"
   15: 
   16: #include <stdio.h>
   17: #include <ctype.h>
   18: 
   19: /*
   20:  * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
   21:  * Receivers. No specific claim of accuracy is made for these receiver,
   22:  * but actual experience suggests that 10 ms would be a conservative
   23:  * assumption.
   24:  * 
   25:  * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
   26:  * of-year format and UTC time zone. Automatic correction for DST should
   27:  * be disabled. It is very important that the year be set correctly in
   28:  * the DIPswitches; otherwise, the day of year will be incorrect after
   29:  * 28 April of a normal or leap year. The propagation delay DIPswitches
   30:  * should be set according to the distance from the transmitter for both
   31:  * WWV and WWVH, as described in the instructions. While the delay can
   32:  * be set only to within 11 ms, the fudge time1 parameter can be used
   33:  * for vernier corrections.
   34:  *
   35:  * Using the poll sequence QTQDQM, the response timecode is in three
   36:  * sections totalling 50 ASCII printing characters, as concatenated by
   37:  * the driver, in the following format:
   38:  *
   39:  * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
   40:  *
   41:  *	on-time = first <cr>
   42:  *	hh:mm:ss.fff = hours, minutes, seconds, milliseconds
   43:  *	a = AM/PM indicator (' ' for 24-hour mode)
   44:  *	yy = year (from internal switches)
   45:  *	dd/mm/ddd = day of month, month, day of year
   46:  *	s = daylight-saving indicator (' ' for 24-hour mode)
   47:  *	f = frequency enable (O = all frequencies enabled)
   48:  *	r = baud rate (3 = 1200, 6 = 9600)
   49:  *	d = features indicator (@ = month/day display enabled)
   50:  *	z = time zone (0 = UTC)
   51:  *	y = year (5 = 91)
   52:  *	cc = WWV propagation delay (52 = 22 ms)
   53:  *	hh = WWVH propagation delay (81 = 33 ms)
   54:  *	SS = status (80 or 82 = operating correctly)
   55:  *	F = current receive frequency (4 = 15 MHz)
   56:  *	T = transmitter (C = WWV, H = WWVH)
   57:  *	tttt = time since last update (0000 = minutes)
   58:  *	uu = flush character (03 = ^c)
   59:  *	xx = 94 (unknown)
   60:  *
   61:  * The alarm condition is indicated by other than '8' at A, which occurs
   62:  * during initial synchronization and when received signal is lost for
   63:  * an extended period; unlock condition is indicated by other than
   64:  * "0000" in the tttt subfield at Q.
   65:  *
   66:  * Fudge Factors
   67:  *
   68:  * There are no special fudge factors other than the generic.
   69:  */
   70: 
   71: /*
   72:  * Interface definitions
   73:  */
   74: #define	DEVICE		"/dev/wwv%d" /* device name and unit */
   75: #define	SPEED232	B9600	/* uart speed (9600 baud) */
   76: #define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
   77: #define	WWVREFID	"WWV\0"	/* WWV reference ID */
   78: #define	WWVHREFID	"WWVH"	/* WWVH reference ID */
   79: #define	DESCRIPTION	"PSTI/Traconex WWV/WWVH Receiver" /* WRU */
   80: #define PST_PHI		(10e-6)	/* max clock oscillator offset */
   81: #define LENPST		46	/* min timecode length */
   82: 
   83: /*
   84:  * Unit control structure
   85:  */
   86: struct pstunit {
   87: 	int	tcswitch;	/* timecode switch */
   88: 	char	*lastptr;	/* pointer to timecode data */
   89: };
   90: 
   91: /*
   92:  * Function prototypes
   93:  */
   94: static	int	pst_start	(int, struct peer *);
   95: static	void	pst_shutdown	(int, struct peer *);
   96: static	void	pst_receive	(struct recvbuf *);
   97: static	void	pst_poll	(int, struct peer *);
   98: 
   99: /*
  100:  * Transfer vector
  101:  */
  102: struct	refclock refclock_pst = {
  103: 	pst_start,		/* start up driver */
  104: 	pst_shutdown,		/* shut down driver */
  105: 	pst_poll,		/* transmit poll message */
  106: 	noentry,		/* not used (old pst_control) */
  107: 	noentry,		/* initialize driver */
  108: 	noentry,		/* not used (old pst_buginfo) */
  109: 	NOFLAGS			/* not used */
  110: };
  111: 
  112: 
  113: /*
  114:  * pst_start - open the devices and initialize data for processing
  115:  */
  116: static int
  117: pst_start(
  118: 	int unit,
  119: 	struct peer *peer
  120: 	)
  121: {
  122: 	register struct pstunit *up;
  123: 	struct refclockproc *pp;
  124: 	int fd;
  125: 	char device[20];
  126: 
  127: 	/*
  128: 	 * Open serial port. Use CLK line discipline, if available.
  129: 	 */
  130: 	snprintf(device, sizeof(device), DEVICE, unit);
  131: 	if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
  132: 		return (0);
  133: 
  134: 	/*
  135: 	 * Allocate and initialize unit structure
  136: 	 */
  137: 	up = emalloc(sizeof(*up));
  138: 	memset(up, 0, sizeof(*up));
  139: 	pp = peer->procptr;
  140: 	pp->io.clock_recv = pst_receive;
  141: 	pp->io.srcclock = (caddr_t)peer;
  142: 	pp->io.datalen = 0;
  143: 	pp->io.fd = fd;
  144: 	if (!io_addclock(&pp->io)) {
  145: 		close(fd);
  146: 		pp->io.fd = -1;
  147: 		free(up);
  148: 		return (0);
  149: 	}
  150: 	pp->unitptr = (caddr_t)up;
  151: 
  152: 	/*
  153: 	 * Initialize miscellaneous variables
  154: 	 */
  155: 	peer->precision = PRECISION;
  156: 	pp->clockdesc = DESCRIPTION;
  157: 	memcpy((char *)&pp->refid, WWVREFID, 4);
  158: 	peer->burst = MAXSTAGE;
  159: 	return (1);
  160: }
  161: 
  162: 
  163: /*
  164:  * pst_shutdown - shut down the clock
  165:  */
  166: static void
  167: pst_shutdown(
  168: 	int unit,
  169: 	struct peer *peer
  170: 	)
  171: {
  172: 	register struct pstunit *up;
  173: 	struct refclockproc *pp;
  174: 
  175: 	pp = peer->procptr;
  176: 	up = (struct pstunit *)pp->unitptr;
  177: 	if (-1 != pp->io.fd)
  178: 		io_closeclock(&pp->io);
  179: 	if (NULL != up)
  180: 		free(up);
  181: }
  182: 
  183: 
  184: /*
  185:  * pst_receive - receive data from the serial interface
  186:  */
  187: static void
  188: pst_receive(
  189: 	struct recvbuf *rbufp
  190: 	)
  191: {
  192: 	register struct pstunit *up;
  193: 	struct refclockproc *pp;
  194: 	struct peer *peer;
  195: 	l_fp trtmp;
  196: 	u_long ltemp;
  197: 	char ampmchar;		/* AM/PM indicator */
  198: 	char daychar;		/* standard/daylight indicator */
  199: 	char junque[10];	/* "yy/dd/mm/" discard */
  200: 	char info[14];		/* "frdzycchhSSFT" clock info */
  201: 
  202: 	/*
  203: 	 * Initialize pointers and read the timecode and timestamp
  204: 	 */
  205: 	peer = (struct peer *)rbufp->recv_srcclock;
  206: 	pp = peer->procptr;
  207: 	up = (struct pstunit *)pp->unitptr;
  208: 	up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
  209: 	    + BMAX - 2 - up->lastptr, &trtmp);
  210: 	*up->lastptr++ = ' ';
  211: 	*up->lastptr = '\0';
  212: 
  213: 	/*
  214: 	 * Note we get a buffer and timestamp for each <cr>, but only
  215: 	 * the first timestamp is retained.
  216: 	 */
  217: 	if (up->tcswitch == 0)
  218: 		pp->lastrec = trtmp;
  219: 	up->tcswitch++;
  220: 	pp->lencode = up->lastptr - pp->a_lastcode;
  221: 	if (up->tcswitch < 3)
  222: 		return;
  223: 
  224: 	/*
  225: 	 * We get down to business, check the timecode format and decode
  226: 	 * its contents. If the timecode has invalid length or is not in
  227: 	 * proper format, we declare bad format and exit.
  228: 	 */
  229: 	if (pp->lencode < LENPST) {
  230: 		refclock_report(peer, CEVNT_BADREPLY);
  231: 		return;
  232: 	}
  233: 
  234: 	/*
  235: 	 * Timecode format:
  236: 	 * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
  237: 	 */
  238: 	if (sscanf(pp->a_lastcode,
  239: 	    "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
  240: 	    &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
  241: 	    &daychar, junque, &pp->day, info, &ltemp) != 10) {
  242: 		refclock_report(peer, CEVNT_BADREPLY);
  243: 		return;
  244: 	}
  245: 	pp->nsec *= 1000000;
  246: 
  247: 	/*
  248: 	 * Decode synchronization, quality and last update. If
  249: 	 * unsynchronized, set the leap bits accordingly and exit. Once
  250: 	 * synchronized, the dispersion depends only on when the clock
  251: 	 * was last heard, which depends on the time since last update,
  252: 	 * as reported by the clock.
  253: 	 */
  254: 	if (info[9] != '8')
  255: 		pp->leap = LEAP_NOTINSYNC;
  256: 	if (info[12] == 'H')
  257: 		memcpy((char *)&pp->refid, WWVHREFID, 4);
  258: 	else
  259: 		memcpy((char *)&pp->refid, WWVREFID, 4);
  260: 	if (peer->stratum <= 1)
  261: 		peer->refid = pp->refid;
  262: 	if (ltemp == 0)
  263: 		pp->lastref = pp->lastrec;
  264: 	pp->disp = PST_PHI * ltemp * 60;
  265: 
  266: 	/*
  267: 	 * Process the new sample in the median filter and determine the
  268: 	 * timecode timestamp.
  269: 	 */
  270: 	if (!refclock_process(pp))
  271: 		refclock_report(peer, CEVNT_BADTIME);
  272: 	else if (peer->disp > MAXDISTANCE)
  273: 		refclock_receive(peer);
  274: }
  275: 
  276: 
  277: /*
  278:  * pst_poll - called by the transmit procedure
  279:  */
  280: static void
  281: pst_poll(
  282: 	int unit,
  283: 	struct peer *peer
  284: 	)
  285: {
  286: 	register struct pstunit *up;
  287: 	struct refclockproc *pp;
  288: 
  289: 	/*
  290: 	 * Time to poll the clock. The PSTI/Traconex clock responds to a
  291: 	 * "QTQDQMT" by returning a timecode in the format specified
  292: 	 * above. Note there is no checking on state, since this may not
  293: 	 * be the only customer reading the clock. Only one customer
  294: 	 * need poll the clock; all others just listen in. If the clock
  295: 	 * becomes unreachable, declare a timeout and keep going.
  296: 	 */
  297: 	pp = peer->procptr;
  298: 	up = (struct pstunit *)pp->unitptr;
  299: 	up->tcswitch = 0;
  300: 	up->lastptr = pp->a_lastcode;
  301: 	if (write(pp->io.fd, "QTQDQMT", 6) != 6)
  302: 		refclock_report(peer, CEVNT_FAULT);
  303: 	if (peer->burst > 0)
  304: 		return;
  305: 	if (pp->coderecv == pp->codeproc) {
  306: 		refclock_report(peer, CEVNT_TIMEOUT);
  307: 		return;
  308: 	}
  309: 	refclock_receive(peer);
  310: 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
  311: #ifdef DEBUG
  312: 	if (debug)
  313: 		printf("pst: timecode %d %s\n", pp->lencode,
  314: 		    pp->a_lastcode);
  315: #endif
  316: 	peer->burst = MAXSTAGE;
  317: 	pp->polls++;
  318: }
  319: 
  320: #else
  321: int refclock_pst_int;
  322: #endif /* REFCLOCK */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>