File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / ntp / ntpd / refclock_ulink.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_ulink - clock driver for Ultralink  WWVB receiver
    3:  */
    4: 
    5: /***********************************************************************
    6:  *                                                                     *
    7:  * Copyright (c) David L. Mills 1992-1998                              *
    8:  *                                                                     *
    9:  * Permission to use, copy, modify, and distribute this software and   *
   10:  * its documentation for any purpose and without fee is hereby         *
   11:  * granted, provided that the above copyright notice appears in all    *
   12:  * copies and that both the copyright notice and this permission       *
   13:  * notice appear in supporting documentation, and that the name        *
   14:  * University of Delaware not be used in advertising or publicity      *
   15:  * pertaining to distribution of the software without specific,        *
   16:  * written prior permission. The University of Delaware makes no       *
   17:  * representations about the suitability this software for any         *
   18:  * purpose. It is provided "as is" without express or implied          *
   19:  * warranty.                                                           *
   20:  **********************************************************************/
   21: 
   22: #ifdef HAVE_CONFIG_H
   23: #include <config.h>
   24: #endif
   25: 
   26: #if defined(REFCLOCK) && defined(CLOCK_ULINK)
   27: 
   28: #include <stdio.h>
   29: #include <ctype.h>
   30: 
   31: #include "ntpd.h"
   32: #include "ntp_io.h"
   33: #include "ntp_refclock.h"
   34: #include "ntp_stdlib.h"
   35: 
   36: /* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
   37:  *
   38:  * this driver was based on the refclock_wwvb.c driver
   39:  * in the ntp distribution.
   40:  *
   41:  * Fudge Factors
   42:  *
   43:  * fudge flag1 0 don't poll clock
   44:  *             1 send poll character
   45:  *
   46:  * revision history:
   47:  *		99/9/09 j.c.lang	original edit's
   48:  *		99/9/11 j.c.lang	changed timecode parse to 
   49:  *                                      match what the radio actually
   50:  *                                      sends. 
   51:  *              99/10/11 j.c.lang       added support for continous
   52:  *                                      time code mode (dipsw2)
   53:  *		99/11/26 j.c.lang	added support for 320 decoder
   54:  *                                      (taken from Dave Strout's
   55:  *                                      Model 320 driver)
   56:  *		99/11/29 j.c.lang	added fudge flag 1 to control
   57:  *					clock polling
   58:  *		99/12/15 j.c.lang	fixed 320 quality flag
   59:  *		01/02/21 s.l.smith	fixed 33x quality flag
   60:  *					added more debugging stuff
   61:  *					updated 33x time code explanation
   62:  *		04/01/23 frank migge	added support for 325 decoder
   63:  *                                      (tested with ULM325.F)
   64:  *
   65:  * Questions, bugs, ideas send to:
   66:  *	Joseph C. Lang
   67:  *	tcnojl1@earthlink.net
   68:  *
   69:  *	Dave Strout
   70:  *	dstrout@linuxfoundry.com
   71:  *
   72:  *      Frank Migge
   73:  *      frank.migge@oracle.com
   74:  *
   75:  *
   76:  * on the Ultralink model 33X decoder Dip switch 2 controls
   77:  * polled or continous timecode 
   78:  * set fudge flag1 if using polled (needed for model 320 and 325)
   79:  * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
   80: */
   81: 
   82: 
   83: /*
   84:  * Interface definitions
   85:  */
   86: #define	DEVICE		"/dev/wwvb%d" /* device name and unit */
   87: #define	SPEED232	B9600	/* uart speed (9600 baud) */
   88: #define	PRECISION	(-10)	/* precision assumed (about 10 ms) */
   89: #define	REFID		"WWVB"	/* reference ID */
   90: #define	DESCRIPTION	"Ultralink WWVB Receiver" /* WRU */
   91: 
   92: #define	LEN33X		32	/* timecode length Model 33X and 325 */
   93: #define LEN320		24	/* timecode length Model 320 */
   94: 
   95: #define	SIGLCHAR33x	'S'	/* signal strength identifier char 325 */
   96: #define	SIGLCHAR325	'R'	/* signal strength identifier char 33x */
   97: 
   98: /*
   99:  *  unit control structure
  100:  */
  101: struct ulinkunit {
  102: 	u_char	tcswitch;	/* timecode switch */
  103: 	l_fp	laststamp;	/* last receive timestamp */
  104: };
  105: 
  106: /*
  107:  * Function prototypes
  108:  */
  109: static	int	ulink_start	(int, struct peer *);
  110: static	void	ulink_shutdown	(int, struct peer *);
  111: static	void	ulink_receive	(struct recvbuf *);
  112: static	void	ulink_poll	(int, struct peer *);
  113: 
  114: /*
  115:  * Transfer vector
  116:  */
  117: struct	refclock refclock_ulink = {
  118: 	ulink_start,		/* start up driver */
  119: 	ulink_shutdown,		/* shut down driver */
  120: 	ulink_poll,		/* transmit poll message */
  121: 	noentry,		/* not used  */
  122: 	noentry,		/* not used  */
  123: 	noentry,		/* not used  */
  124: 	NOFLAGS
  125: };
  126: 
  127: 
  128: /*
  129:  * ulink_start - open the devices and initialize data for processing
  130:  */
  131: static int
  132: ulink_start(
  133: 	int unit,
  134: 	struct peer *peer
  135: 	)
  136: {
  137: 	register struct ulinkunit *up;
  138: 	struct refclockproc *pp;
  139: 	int fd;
  140: 	char device[20];
  141: 
  142: 	/*
  143: 	 * Open serial port. Use CLK line discipline, if available.
  144: 	 */
  145: 	(void)sprintf(device, DEVICE, unit);
  146: 	if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
  147: 		return (0);
  148: 
  149: 	/*
  150: 	 * Allocate and initialize unit structure
  151: 	 */
  152: 	if (!(up = (struct ulinkunit *)
  153: 	      emalloc(sizeof(struct ulinkunit)))) {
  154: 		(void) close(fd);
  155: 		return (0);
  156: 	}
  157: 	memset((char *)up, 0, sizeof(struct ulinkunit));
  158: 	pp = peer->procptr;
  159: 	pp->unitptr = (caddr_t)up;
  160: 	pp->io.clock_recv = ulink_receive;
  161: 	pp->io.srcclock = (caddr_t)peer;
  162: 	pp->io.datalen = 0;
  163: 	pp->io.fd = fd;
  164: 	if (!io_addclock(&pp->io)) {
  165: 		(void) close(fd);
  166: 		free(up);
  167: 		return (0);
  168: 	}
  169: 
  170: 	/*
  171: 	 * Initialize miscellaneous variables
  172: 	 */
  173: 	peer->precision = PRECISION;
  174: 	peer->burst = NSTAGE;
  175: 	pp->clockdesc = DESCRIPTION;
  176: 	memcpy((char *)&pp->refid, REFID, 4);
  177: 	return (1);
  178: }
  179: 
  180: 
  181: /*
  182:  * ulink_shutdown - shut down the clock
  183:  */
  184: static void
  185: ulink_shutdown(
  186: 	int unit,
  187: 	struct peer *peer
  188: 	)
  189: {
  190: 	register struct ulinkunit *up;
  191: 	struct refclockproc *pp;
  192: 
  193: 	pp = peer->procptr;
  194: 	up = (struct ulinkunit *)pp->unitptr;
  195: 	io_closeclock(&pp->io);
  196: 	free(up);
  197: }
  198: 
  199: 
  200: /*
  201:  * ulink_receive - receive data from the serial interface
  202:  */
  203: static void
  204: ulink_receive(
  205: 	struct recvbuf *rbufp
  206: 	)
  207: {
  208: 	struct ulinkunit *up;
  209: 	struct refclockproc *pp;
  210: 	struct peer *peer;
  211: 
  212: 	l_fp	trtmp;		/* arrival timestamp */
  213: 	int	quality;	/* quality indicator */
  214: 	int	temp;		/* int temp */
  215: 	char	syncchar;	/* synchronization indicator */
  216: 	char	leapchar;	/* leap indicator */
  217: 	char	modechar;	/* model 320 mode flag */
  218:         char	siglchar;	/* model difference between 33x/325 */
  219: 	char	char_quality[2];	/* temp quality flag */
  220: 
  221: 	/*
  222: 	 * Initialize pointers and read the timecode and timestamp
  223: 	 */
  224: 	peer = (struct peer *)rbufp->recv_srcclock;
  225: 	pp = peer->procptr;
  226: 	up = (struct ulinkunit *)pp->unitptr;
  227: 	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
  228: 
  229: 	/*
  230: 	 * Note we get a buffer and timestamp for both a <cr> and <lf>,
  231: 	 * but only the <cr> timestamp is retained. 
  232: 	 */
  233: 	if (temp == 0) {
  234: 		if (up->tcswitch == 0) {
  235: 			up->tcswitch = 1;
  236: 			up->laststamp = trtmp;
  237: 		} else
  238: 		    up->tcswitch = 0;
  239: 		return;
  240: 	}
  241: 	pp->lencode = temp;
  242: 	pp->lastrec = up->laststamp;
  243: 	up->laststamp = trtmp;
  244: 	up->tcswitch = 1;
  245: #ifdef DEBUG
  246: 	if (debug)
  247: 		printf("ulink: timecode %d %s\n", pp->lencode,
  248: 		    pp->a_lastcode);
  249: #endif
  250: 
  251: 	/*
  252: 	 * We get down to business, check the timecode format and decode
  253: 	 * its contents. If the timecode has invalid length or is not in
  254: 	 * proper format, we declare bad format and exit.
  255: 	 */
  256: 	syncchar = leapchar = modechar = siglchar = ' ';
  257: 	switch (pp->lencode ) {
  258: 		case LEN33X:
  259: 
  260: 		/*
  261:                  * First we check if the format is 33x or 325:
  262: 		 *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
  263: 		 *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
  264: 		 * simply by comparing if the signal level is 'S' or 'R'
  265:                  */
  266: 
  267:                  if (sscanf(pp->a_lastcode, "%c%*31c",
  268:                             &siglchar) == 1) {
  269: 
  270:                     if(siglchar == SIGLCHAR325) {
  271: 
  272:        		   /*
  273: 		    * decode for a Model 325 decoder.
  274: 		    * Timecode format from January 23, 2004 datasheet is:
  275:                     *
  276: 		    *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
  277:                     *
  278: 		    *   R      WWVB decodersignal readability R1 - R5
  279: 		    *   5      R1 is unreadable, R5 is best
  280: 		    *   space  a space (0x20)
  281: 		    *   1      Data bit 0, 1, M (pos mark), or ? (unknown).
  282: 		    *   C      Reception from either (C)olorado or (H)awaii 
  283: 		    *   00     Hours since last good WWVB frame sync. Will 
  284: 		    *          be 00-99
  285: 		    *   space  Space char (0x20) or (0xa5) if locked to wwvb
  286: 		    *   YYYY   Current year, 2000-2099
  287: 		    *   +      Leap year indicator. '+' if a leap year,
  288: 		    *          a space (0x20) if not.
  289: 		    *   DDD    Day of year, 000 - 365.
  290: 		    *   UTC    Timezone (always 'UTC').
  291: 		    *   S      Daylight savings indicator
  292: 		    *             S - standard time (STD) in effect
  293: 		    *             O - during STD to DST day 0000-2400
  294: 		    *             D - daylight savings time (DST) in effect
  295: 		    *             I - during DST to STD day 0000-2400
  296: 		    *   space  Space character (0x20)
  297: 		    *   HH     Hours 00-23
  298: 		    *   :      This is the REAL in sync indicator (: = insync)	
  299: 		    *   MM     Minutes 00-59
  300: 		    *   :      : = in sync ? = NOT in sync
  301: 		    *   SS     Seconds 00-59
  302: 		    *   L      Leap second flag. Changes from space (0x20)
  303: 		    *          to 'I' or 'D' during month preceding leap
  304: 		    *          second adjustment. (I)nsert or (D)elete
  305: 		    *   +5     UT1 correction (sign + digit ))
  306: 		    */
  307: 
  308:    		       if (sscanf(pp->a_lastcode, 
  309:                           "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
  310:    		          char_quality, &pp->year, &pp->day, 
  311:                           &pp->hour, &syncchar, &pp->minute, &pp->second, 
  312:                           &leapchar) == 8) { 
  313:    		
  314:    			  if (char_quality[0] == '0') {
  315:    				quality = 0;
  316:    			  } else if (char_quality[0] == '0') {
  317:    				quality = (char_quality[1] & 0x0f);
  318:    			  } else  {
  319:    				quality = 99;
  320:    			  }
  321: 
  322:    		          if (leapchar == 'I' ) leapchar = '+';
  323:    		          if (leapchar == 'D' ) leapchar = '-';
  324: 
  325: 		          /*
  326: 		          #ifdef DEBUG
  327: 		          if (debug) {
  328: 		             printf("ulink: char_quality %c %c\n", 
  329:                                     char_quality[0], char_quality[1]);
  330: 			     printf("ulink: quality %d\n", quality);
  331: 			     printf("ulink: syncchar %x\n", syncchar);
  332: 			     printf("ulink: leapchar %x\n", leapchar);
  333:                           }
  334:                           #endif
  335:                           */
  336: 
  337:                        }
  338: 		
  339:                     } 
  340:                     if(siglchar == SIGLCHAR33x) {
  341:                 
  342: 		   /*
  343: 		    * We got a Model 33X decoder.
  344: 		    * Timecode format from January 29, 2001 datasheet is:
  345: 		    *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
  346: 		    *   S      WWVB decoder sync indicator. S for in-sync(?)
  347: 		    *          or N for noisy signal.
  348: 		    *   9+     RF signal level in S-units, 0-9 followed by
  349: 		    *          a space (0x20). The space turns to '+' if the
  350: 		    *          level is over 9.
  351: 		    *   D      Data bit 0, 1, 2 (position mark), or
  352: 		    *          3 (unknown).
  353: 		    *   space  Space character (0x20)
  354: 		    *   00     Hours since last good WWVB frame sync. Will 
  355: 		    *          be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
  356:                     *          if currently in sync. 
  357: 		    *   space  Space character (0x20)
  358: 		    *   YYYY   Current year, 1990-2089
  359: 		    *   +      Leap year indicator. '+' if a leap year,
  360: 		    *          a space (0x20) if not.
  361: 		    *   DDD    Day of year, 001 - 366.
  362: 		    *   UTC    Timezone (always 'UTC').
  363: 		    *   S      Daylight savings indicator
  364: 		    *             S - standard time (STD) in effect
  365: 		    *             O - during STD to DST day 0000-2400
  366: 		    *             D - daylight savings time (DST) in effect
  367: 		    *             I - during DST to STD day 0000-2400
  368: 		    *   space  Space character (0x20)
  369: 		    *   HH     Hours 00-23
  370: 		    *   :      This is the REAL in sync indicator (: = insync)	
  371: 		    *   MM     Minutes 00-59
  372: 		    *   :      : = in sync ? = NOT in sync
  373: 		    *   SS     Seconds 00-59
  374: 		    *   L      Leap second flag. Changes from space (0x20)
  375: 		    *          to '+' or '-' during month preceding leap
  376: 		    *          second adjustment.
  377: 		    *   +5     UT1 correction (sign + digit ))
  378: 		    */
  379: 
  380: 		       if (sscanf(pp->a_lastcode, 
  381:                            "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
  382: 		           char_quality, &pp->year, &pp->day, 
  383:                            &pp->hour, &syncchar, &pp->minute, &pp->second, 
  384:                            &leapchar) == 8) { 
  385: 		
  386: 			   if (char_quality[0] == 'L') {
  387: 				quality = 0;
  388: 			   } else if (char_quality[0] == '0') {
  389: 				quality = (char_quality[1] & 0x0f);
  390: 			   } else  {
  391: 				quality = 99;
  392: 		           }
  393: 	
  394:                            /*
  395:                            #ifdef DEBUG
  396:          		   if (debug) {
  397:          			printf("ulink: char_quality %c %c\n", 
  398:                                         char_quality[0], char_quality[1]);
  399:          			printf("ulink: quality %d\n", quality);
  400:          			printf("ulink: syncchar %x\n", syncchar);
  401:          			printf("ulink: leapchar %x\n", leapchar);
  402:                            }
  403:                            #endif
  404:                            */
  405: 
  406: 		        }
  407:                     }
  408: 		    break;
  409: 		}
  410: 
  411: 		case LEN320:
  412: 
  413: 	        /*
  414: 		 * Model 320 Decoder
  415: 		 * The timecode format is:
  416: 		 *
  417: 		 *  <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
  418: 		 *
  419: 		 * where:
  420: 		 *
  421: 		 * S = 'S' -- sync'd in last hour,
  422: 		 *     '0'-'9' - hours x 10 since last update,
  423: 		 *     '?' -- not in sync
  424: 		 * Q = Number of correlating time-frames, from 0 to 5
  425: 		 * R = 'R' -- reception in progress,
  426: 		 *     'N' -- Noisy reception,
  427: 		 *     ' ' -- standby mode
  428: 		 * YYYY = year from 1990 to 2089
  429: 		 * DDD = current day from 1 to 366
  430: 		 * + = '+' if current year is a leap year, else ' '
  431: 		 * HH = UTC hour 0 to 23
  432: 		 * MM = Minutes of current hour from 0 to 59
  433: 		 * SS = Seconds of current minute from 0 to 59
  434: 		 * mm = 10's milliseconds of the current second from 00 to 99
  435: 		 * L  = Leap second pending at end of month
  436: 		 *     'I' = insert, 'D'= delete
  437: 		 * T  = DST <-> STD transition indicators
  438: 		 *
  439:         	 */
  440: 
  441: 		if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
  442: 	               &syncchar, &quality, &modechar, &pp->year, &pp->day,
  443:         	       &pp->hour, &pp->minute, &pp->second,
  444: 			&pp->nsec, &leapchar) == 10) {
  445: 		pp->nsec *= 10000000; /* M320 returns 10's of msecs */
  446: 		if (leapchar == 'I' ) leapchar = '+';
  447: 		if (leapchar == 'D' ) leapchar = '-';
  448: 		if (syncchar != '?' ) syncchar = ':';
  449: 
  450:  		break;
  451: 		}
  452: 
  453: 		default:
  454: 		refclock_report(peer, CEVNT_BADREPLY);
  455: 		return;
  456: 	}
  457: 
  458: 	/*
  459: 	 * Decode quality indicator
  460: 	 * For the 325 & 33x series, the lower the number the "better" 
  461: 	 * the time is. I used the dispersion as the measure of time 
  462: 	 * quality. The quality indicator in the 320 is the number of 
  463: 	 * correlating time frames (the more the better)
  464: 	 */
  465: 
  466: 	/* 
  467: 	 * The spec sheet for the 325 & 33x series states the clock will
  468: 	 * maintain +/-0.002 seconds accuracy when locked to WWVB. This 
  469: 	 * is indicated by 'Lk' in the quality portion of the incoming 
  470: 	 * string. When not in lock, a drift of +/-0.015 seconds should 
  471: 	 * be allowed for.
  472: 	 * With the quality indicator decoding scheme above, the 'Lk' 
  473: 	 * condition will produce a quality value of 0. If the quality 
  474: 	 * indicator starts with '0' then the second character is the 
  475: 	 * number of hours since we were last locked. If the first 
  476: 	 * character is anything other than 'L' or '0' then we have been 
  477: 	 * out of lock for more than 9 hours so we assume the worst and 
  478: 	 * force a quality value that selects the 'default' maximum 
  479: 	 * dispersion. The dispersion values below are what came with the
  480: 	 * driver. They're not unreasonable so they've not been changed.
  481: 	 */
  482: 
  483: 	if (pp->lencode == LEN33X) {
  484: 		switch (quality) {
  485: 			case 0 :
  486: 				pp->disp=.002;
  487: 				break;
  488: 			case 1 :
  489: 				pp->disp=.02;
  490: 				break;
  491: 			case 2 :
  492: 				pp->disp=.04;
  493: 				break;
  494: 			case 3 :
  495: 				pp->disp=.08;
  496: 				break;
  497: 			default:
  498: 				pp->disp=MAXDISPERSE;
  499: 				break;
  500: 		}
  501: 	} else {
  502: 		switch (quality) {
  503: 			case 5 :
  504: 				pp->disp=.002;
  505: 				break;
  506: 			case 4 :
  507: 				pp->disp=.02;
  508: 				break;
  509: 			case 3 :
  510: 				pp->disp=.04;
  511: 				break;
  512: 			case 2 :
  513: 				pp->disp=.08;
  514: 				break;
  515: 			case 1 :
  516: 				pp->disp=.16;
  517: 				break;
  518: 			default:
  519: 				pp->disp=MAXDISPERSE;
  520: 				break;
  521: 		}
  522: 
  523: 	}
  524: 
  525: 	/*
  526: 	 * Decode synchronization, and leap characters. If
  527: 	 * unsynchronized, set the leap bits accordingly and exit.
  528: 	 * Otherwise, set the leap bits according to the leap character.
  529: 	 */
  530: 
  531: 	if (syncchar != ':')
  532: 		pp->leap = LEAP_NOTINSYNC;
  533: 	else if (leapchar == '+')
  534: 		pp->leap = LEAP_ADDSECOND;
  535: 	else if (leapchar == '-')
  536: 		pp->leap = LEAP_DELSECOND;
  537: 	else
  538: 		pp->leap = LEAP_NOWARNING;
  539: 
  540: 	/*
  541: 	 * Process the new sample in the median filter and determine the
  542: 	 * timecode timestamp.
  543: 	 */
  544: 	if (!refclock_process(pp)) {
  545: 		refclock_report(peer, CEVNT_BADTIME);
  546: 	}
  547: 
  548: }
  549: 
  550: /*
  551:  * ulink_poll - called by the transmit procedure
  552:  */
  553: 
  554: static void
  555: ulink_poll(
  556: 	int unit,
  557: 	struct peer *peer
  558: 	)
  559: {
  560:         struct refclockproc *pp;
  561:         char pollchar;
  562: 
  563:         pp = peer->procptr;
  564:         pollchar = 'T';
  565: 	if (pp->sloppyclockflag & CLK_FLAG1) {
  566: 	        if (write(pp->io.fd, &pollchar, 1) != 1)
  567:         	        refclock_report(peer, CEVNT_FAULT);
  568:         	else
  569:       	            pp->polls++;
  570: 	}
  571: 	else
  572:       	            pp->polls++;
  573: 
  574:         if (peer->burst > 0)
  575:                 return;
  576:         if (pp->coderecv == pp->codeproc) {
  577:                 refclock_report(peer, CEVNT_TIMEOUT);
  578:                 return;
  579:         }
  580:         pp->lastref = pp->lastrec;
  581: 	refclock_receive(peer);
  582: 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
  583:         peer->burst = NSTAGE;
  584: 
  585: }
  586: 
  587: #else
  588: int refclock_ulink_bs;
  589: #endif /* REFCLOCK */

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