154359Sroberto/*
282498Sroberto * refclock_ulink - clock driver for Ultralink  WWVB receiver
354359Sroberto */
454359Sroberto
554359Sroberto#ifdef HAVE_CONFIG_H
654359Sroberto#include <config.h>
754359Sroberto#endif
854359Sroberto
954359Sroberto#if defined(REFCLOCK) && defined(CLOCK_ULINK)
1054359Sroberto
1154359Sroberto#include <stdio.h>
1254359Sroberto#include <ctype.h>
1354359Sroberto
1454359Sroberto#include "ntpd.h"
1554359Sroberto#include "ntp_io.h"
1654359Sroberto#include "ntp_refclock.h"
1754359Sroberto#include "ntp_stdlib.h"
1854359Sroberto
19182007Sroberto/* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
2054359Sroberto *
2182498Sroberto * this driver was based on the refclock_wwvb.c driver
2282498Sroberto * in the ntp distribution.
2354359Sroberto *
2482498Sroberto * Fudge Factors
2554359Sroberto *
2682498Sroberto * fudge flag1 0 don't poll clock
2782498Sroberto *             1 send poll character
2854359Sroberto *
2982498Sroberto * revision history:
3082498Sroberto *		99/9/09 j.c.lang	original edit's
3182498Sroberto *		99/9/11 j.c.lang	changed timecode parse to
3282498Sroberto *                                      match what the radio actually
3382498Sroberto *                                      sends.
3482498Sroberto *              99/10/11 j.c.lang       added support for continous
3582498Sroberto *                                      time code mode (dipsw2)
3682498Sroberto *		99/11/26 j.c.lang	added support for 320 decoder
3782498Sroberto *                                      (taken from Dave Strout's
3882498Sroberto *                                      Model 320 driver)
3982498Sroberto *		99/11/29 j.c.lang	added fudge flag 1 to control
4082498Sroberto *					clock polling
4182498Sroberto *		99/12/15 j.c.lang	fixed 320 quality flag
4282498Sroberto *		01/02/21 s.l.smith	fixed 33x quality flag
4382498Sroberto *					added more debugging stuff
4482498Sroberto *					updated 33x time code explanation
45182007Sroberto *		04/01/23 frank migge	added support for 325 decoder
46182007Sroberto *                                      (tested with ULM325.F)
4754359Sroberto *
4882498Sroberto * Questions, bugs, ideas send to:
4982498Sroberto *	Joseph C. Lang
5082498Sroberto *	tcnojl1@earthlink.net
5154359Sroberto *
5282498Sroberto *	Dave Strout
5382498Sroberto *	dstrout@linuxfoundry.com
5482498Sroberto *
55182007Sroberto *      Frank Migge
56182007Sroberto *      frank.migge@oracle.com
5782498Sroberto *
58182007Sroberto *
5982498Sroberto * on the Ultralink model 33X decoder Dip switch 2 controls
6082498Sroberto * polled or continous timecode
61182007Sroberto * set fudge flag1 if using polled (needed for model 320 and 325)
6282498Sroberto * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
6382498Sroberto*/
6454359Sroberto
6582498Sroberto
6654359Sroberto/*
6754359Sroberto * Interface definitions
6854359Sroberto */
6982498Sroberto#define	DEVICE		"/dev/wwvb%d" /* device name and unit */
7054359Sroberto#define	SPEED232	B9600	/* uart speed (9600 baud) */
7182498Sroberto#define	PRECISION	(-10)	/* precision assumed (about 10 ms) */
7282498Sroberto#define	REFID		"WWVB"	/* reference ID */
7354359Sroberto#define	DESCRIPTION	"Ultralink WWVB Receiver" /* WRU */
7454359Sroberto
75182007Sroberto#define	LEN33X		32	/* timecode length Model 33X and 325 */
7682498Sroberto#define LEN320		24	/* timecode length Model 320 */
7754359Sroberto
78182007Sroberto#define	SIGLCHAR33x	'S'	/* signal strength identifier char 325 */
79182007Sroberto#define	SIGLCHAR325	'R'	/* signal strength identifier char 33x */
80182007Sroberto
8154359Sroberto/*
8282498Sroberto *  unit control structure
8354359Sroberto */
8454359Srobertostruct ulinkunit {
8554359Sroberto	u_char	tcswitch;	/* timecode switch */
8654359Sroberto	l_fp	laststamp;	/* last receive timestamp */
8754359Sroberto};
8854359Sroberto
8954359Sroberto/*
9054359Sroberto * Function prototypes
9154359Sroberto */
92290001Sglebiusstatic	int	ulink_start	(int, struct peer *);
93290001Sglebiusstatic	void	ulink_shutdown	(int, struct peer *);
94290001Sglebiusstatic	void	ulink_receive	(struct recvbuf *);
95290001Sglebiusstatic	void	ulink_poll	(int, struct peer *);
9654359Sroberto
9754359Sroberto/*
9854359Sroberto * Transfer vector
9954359Sroberto */
10054359Srobertostruct	refclock refclock_ulink = {
10154359Sroberto	ulink_start,		/* start up driver */
10254359Sroberto	ulink_shutdown,		/* shut down driver */
10354359Sroberto	ulink_poll,		/* transmit poll message */
10482498Sroberto	noentry,		/* not used  */
10582498Sroberto	noentry,		/* not used  */
10682498Sroberto	noentry,		/* not used  */
10782498Sroberto	NOFLAGS
10854359Sroberto};
10954359Sroberto
11054359Sroberto
11154359Sroberto/*
11254359Sroberto * ulink_start - open the devices and initialize data for processing
11354359Sroberto */
11454359Srobertostatic int
11554359Srobertoulink_start(
11654359Sroberto	int unit,
11754359Sroberto	struct peer *peer
11854359Sroberto	)
11954359Sroberto{
12054359Sroberto	register struct ulinkunit *up;
12154359Sroberto	struct refclockproc *pp;
12282498Sroberto	int fd;
12354359Sroberto	char device[20];
12482498Sroberto
12554359Sroberto	/*
12654359Sroberto	 * Open serial port. Use CLK line discipline, if available.
12754359Sroberto	 */
128290001Sglebius	snprintf(device, sizeof(device), DEVICE, unit);
129290001Sglebius	fd = refclock_open(device, SPEED232, LDISC_CLK);
130290001Sglebius	if (fd <= 0)
13154359Sroberto		return (0);
13254359Sroberto
13354359Sroberto	/*
13454359Sroberto	 * Allocate and initialize unit structure
13554359Sroberto	 */
136290001Sglebius	up = emalloc(sizeof(struct ulinkunit));
137290001Sglebius	memset(up, 0, sizeof(struct ulinkunit));
13854359Sroberto	pp = peer->procptr;
13954359Sroberto	pp->io.clock_recv = ulink_receive;
140290001Sglebius	pp->io.srcclock = peer;
14154359Sroberto	pp->io.datalen = 0;
14254359Sroberto	pp->io.fd = fd;
14354359Sroberto	if (!io_addclock(&pp->io)) {
144290001Sglebius		close(fd);
145290001Sglebius		pp->io.fd = -1;
14654359Sroberto		free(up);
14754359Sroberto		return (0);
14854359Sroberto	}
149290001Sglebius	pp->unitptr = up;
15054359Sroberto
15154359Sroberto	/*
15254359Sroberto	 * Initialize miscellaneous variables
15354359Sroberto	 */
15454359Sroberto	peer->precision = PRECISION;
15554359Sroberto	pp->clockdesc = DESCRIPTION;
15654359Sroberto	memcpy((char *)&pp->refid, REFID, 4);
15754359Sroberto	return (1);
15854359Sroberto}
15954359Sroberto
16054359Sroberto
16154359Sroberto/*
16254359Sroberto * ulink_shutdown - shut down the clock
16354359Sroberto */
16454359Srobertostatic void
16554359Srobertoulink_shutdown(
16654359Sroberto	int unit,
16754359Sroberto	struct peer *peer
16854359Sroberto	)
16954359Sroberto{
17054359Sroberto	register struct ulinkunit *up;
17154359Sroberto	struct refclockproc *pp;
17254359Sroberto
17354359Sroberto	pp = peer->procptr;
174290001Sglebius	up = pp->unitptr;
175290001Sglebius	if (pp->io.fd != -1)
176290001Sglebius		io_closeclock(&pp->io);
177290001Sglebius	if (up != NULL)
178290001Sglebius		free(up);
17954359Sroberto}
18054359Sroberto
18154359Sroberto
18254359Sroberto/*
18354359Sroberto * ulink_receive - receive data from the serial interface
18454359Sroberto */
18554359Srobertostatic void
18654359Srobertoulink_receive(
18754359Sroberto	struct recvbuf *rbufp
18854359Sroberto	)
18954359Sroberto{
19054359Sroberto	struct ulinkunit *up;
19154359Sroberto	struct refclockproc *pp;
19254359Sroberto	struct peer *peer;
19354359Sroberto
194290001Sglebius	l_fp	trtmp;			/* arrival timestamp */
195290001Sglebius	int	quality = INT_MAX;	/* quality indicator */
196290001Sglebius	int	temp;			/* int temp */
197290001Sglebius	char	syncchar;		/* synchronization indicator */
198290001Sglebius	char	leapchar;		/* leap indicator */
199290001Sglebius	char	modechar;		/* model 320 mode flag */
200290001Sglebius        char	siglchar;		/* model difference between 33x/325 */
20182498Sroberto	char	char_quality[2];	/* temp quality flag */
20254359Sroberto
20354359Sroberto	/*
20454359Sroberto	 * Initialize pointers and read the timecode and timestamp
20554359Sroberto	 */
206290001Sglebius	peer = rbufp->recv_peer;
20754359Sroberto	pp = peer->procptr;
208290001Sglebius	up = pp->unitptr;
20954359Sroberto	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
21054359Sroberto
21154359Sroberto	/*
21254359Sroberto	 * Note we get a buffer and timestamp for both a <cr> and <lf>,
21382498Sroberto	 * but only the <cr> timestamp is retained.
21454359Sroberto	 */
21554359Sroberto	if (temp == 0) {
21654359Sroberto		if (up->tcswitch == 0) {
21754359Sroberto			up->tcswitch = 1;
21854359Sroberto			up->laststamp = trtmp;
21954359Sroberto		} else
22054359Sroberto		    up->tcswitch = 0;
22154359Sroberto		return;
22254359Sroberto	}
22354359Sroberto	pp->lencode = temp;
22454359Sroberto	pp->lastrec = up->laststamp;
22554359Sroberto	up->laststamp = trtmp;
22654359Sroberto	up->tcswitch = 1;
22754359Sroberto#ifdef DEBUG
22854359Sroberto	if (debug)
22954359Sroberto		printf("ulink: timecode %d %s\n", pp->lencode,
23054359Sroberto		    pp->a_lastcode);
23154359Sroberto#endif
23254359Sroberto
23354359Sroberto	/*
23454359Sroberto	 * We get down to business, check the timecode format and decode
23582498Sroberto	 * its contents. If the timecode has invalid length or is not in
23682498Sroberto	 * proper format, we declare bad format and exit.
23754359Sroberto	 */
238182007Sroberto	syncchar = leapchar = modechar = siglchar = ' ';
23982498Sroberto	switch (pp->lencode ) {
24082498Sroberto		case LEN33X:
241182007Sroberto
24282498Sroberto		/*
243182007Sroberto                 * First we check if the format is 33x or 325:
244182007Sroberto		 *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
245182007Sroberto		 *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
246182007Sroberto		 * simply by comparing if the signal level is 'S' or 'R'
247182007Sroberto                 */
24882498Sroberto
249182007Sroberto                 if (sscanf(pp->a_lastcode, "%c%*31c",
250182007Sroberto                            &siglchar) == 1) {
251182007Sroberto
252182007Sroberto                    if(siglchar == SIGLCHAR325) {
253182007Sroberto
254182007Sroberto       		   /*
255182007Sroberto		    * decode for a Model 325 decoder.
256182007Sroberto		    * Timecode format from January 23, 2004 datasheet is:
257182007Sroberto                    *
258182007Sroberto		    *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
259182007Sroberto                    *
260182007Sroberto		    *   R      WWVB decodersignal readability R1 - R5
261182007Sroberto		    *   5      R1 is unreadable, R5 is best
262182007Sroberto		    *   space  a space (0x20)
263182007Sroberto		    *   1      Data bit 0, 1, M (pos mark), or ? (unknown).
264182007Sroberto		    *   C      Reception from either (C)olorado or (H)awaii
265182007Sroberto		    *   00     Hours since last good WWVB frame sync. Will
266182007Sroberto		    *          be 00-99
267182007Sroberto		    *   space  Space char (0x20) or (0xa5) if locked to wwvb
268182007Sroberto		    *   YYYY   Current year, 2000-2099
269182007Sroberto		    *   +      Leap year indicator. '+' if a leap year,
270182007Sroberto		    *          a space (0x20) if not.
271182007Sroberto		    *   DDD    Day of year, 000 - 365.
272182007Sroberto		    *   UTC    Timezone (always 'UTC').
273182007Sroberto		    *   S      Daylight savings indicator
274182007Sroberto		    *             S - standard time (STD) in effect
275182007Sroberto		    *             O - during STD to DST day 0000-2400
276182007Sroberto		    *             D - daylight savings time (DST) in effect
277182007Sroberto		    *             I - during DST to STD day 0000-2400
278182007Sroberto		    *   space  Space character (0x20)
279182007Sroberto		    *   HH     Hours 00-23
280182007Sroberto		    *   :      This is the REAL in sync indicator (: = insync)
281182007Sroberto		    *   MM     Minutes 00-59
282182007Sroberto		    *   :      : = in sync ? = NOT in sync
283182007Sroberto		    *   SS     Seconds 00-59
284182007Sroberto		    *   L      Leap second flag. Changes from space (0x20)
285182007Sroberto		    *          to 'I' or 'D' during month preceding leap
286182007Sroberto		    *          second adjustment. (I)nsert or (D)elete
287182007Sroberto		    *   +5     UT1 correction (sign + digit ))
288182007Sroberto		    */
289182007Sroberto
290182007Sroberto   		       if (sscanf(pp->a_lastcode,
291182007Sroberto                          "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
292182007Sroberto   		          char_quality, &pp->year, &pp->day,
293182007Sroberto                          &pp->hour, &syncchar, &pp->minute, &pp->second,
294182007Sroberto                          &leapchar) == 8) {
295182007Sroberto
296182007Sroberto   			  if (char_quality[0] == '0') {
297182007Sroberto   				quality = 0;
298182007Sroberto   			  } else if (char_quality[0] == '0') {
299182007Sroberto   				quality = (char_quality[1] & 0x0f);
300182007Sroberto   			  } else  {
301182007Sroberto   				quality = 99;
302182007Sroberto   			  }
303182007Sroberto
304182007Sroberto   		          if (leapchar == 'I' ) leapchar = '+';
305182007Sroberto   		          if (leapchar == 'D' ) leapchar = '-';
306182007Sroberto
307182007Sroberto		          /*
308182007Sroberto		          #ifdef DEBUG
309182007Sroberto		          if (debug) {
310182007Sroberto		             printf("ulink: char_quality %c %c\n",
311182007Sroberto                                    char_quality[0], char_quality[1]);
312182007Sroberto			     printf("ulink: quality %d\n", quality);
313182007Sroberto			     printf("ulink: syncchar %x\n", syncchar);
314182007Sroberto			     printf("ulink: leapchar %x\n", leapchar);
315182007Sroberto                          }
316182007Sroberto                          #endif
317182007Sroberto                          */
318182007Sroberto
319182007Sroberto                       }
32082498Sroberto
321182007Sroberto                    }
322182007Sroberto                    if(siglchar == SIGLCHAR33x) {
323182007Sroberto
324182007Sroberto		   /*
325182007Sroberto		    * We got a Model 33X decoder.
326182007Sroberto		    * Timecode format from January 29, 2001 datasheet is:
327182007Sroberto		    *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
328182007Sroberto		    *   S      WWVB decoder sync indicator. S for in-sync(?)
329182007Sroberto		    *          or N for noisy signal.
330182007Sroberto		    *   9+     RF signal level in S-units, 0-9 followed by
331182007Sroberto		    *          a space (0x20). The space turns to '+' if the
332182007Sroberto		    *          level is over 9.
333182007Sroberto		    *   D      Data bit 0, 1, 2 (position mark), or
334182007Sroberto		    *          3 (unknown).
335182007Sroberto		    *   space  Space character (0x20)
336182007Sroberto		    *   00     Hours since last good WWVB frame sync. Will
337182007Sroberto		    *          be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
338182007Sroberto                    *          if currently in sync.
339182007Sroberto		    *   space  Space character (0x20)
340182007Sroberto		    *   YYYY   Current year, 1990-2089
341182007Sroberto		    *   +      Leap year indicator. '+' if a leap year,
342182007Sroberto		    *          a space (0x20) if not.
343182007Sroberto		    *   DDD    Day of year, 001 - 366.
344182007Sroberto		    *   UTC    Timezone (always 'UTC').
345182007Sroberto		    *   S      Daylight savings indicator
346182007Sroberto		    *             S - standard time (STD) in effect
347182007Sroberto		    *             O - during STD to DST day 0000-2400
348182007Sroberto		    *             D - daylight savings time (DST) in effect
349182007Sroberto		    *             I - during DST to STD day 0000-2400
350182007Sroberto		    *   space  Space character (0x20)
351182007Sroberto		    *   HH     Hours 00-23
352182007Sroberto		    *   :      This is the REAL in sync indicator (: = insync)
353182007Sroberto		    *   MM     Minutes 00-59
354182007Sroberto		    *   :      : = in sync ? = NOT in sync
355182007Sroberto		    *   SS     Seconds 00-59
356182007Sroberto		    *   L      Leap second flag. Changes from space (0x20)
357182007Sroberto		    *          to '+' or '-' during month preceding leap
358182007Sroberto		    *          second adjustment.
359182007Sroberto		    *   +5     UT1 correction (sign + digit ))
360182007Sroberto		    */
361182007Sroberto
362182007Sroberto		       if (sscanf(pp->a_lastcode,
363182007Sroberto                           "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
364182007Sroberto		           char_quality, &pp->year, &pp->day,
365182007Sroberto                           &pp->hour, &syncchar, &pp->minute, &pp->second,
366182007Sroberto                           &leapchar) == 8) {
367182007Sroberto
368182007Sroberto			   if (char_quality[0] == 'L') {
36982498Sroberto				quality = 0;
370182007Sroberto			   } else if (char_quality[0] == '0') {
37182498Sroberto				quality = (char_quality[1] & 0x0f);
372182007Sroberto			   } else  {
37382498Sroberto				quality = 99;
374182007Sroberto		           }
37554359Sroberto
376182007Sroberto                           /*
377182007Sroberto                           #ifdef DEBUG
378182007Sroberto         		   if (debug) {
379182007Sroberto         			printf("ulink: char_quality %c %c\n",
380182007Sroberto                                        char_quality[0], char_quality[1]);
381182007Sroberto         			printf("ulink: quality %d\n", quality);
382182007Sroberto         			printf("ulink: syncchar %x\n", syncchar);
383182007Sroberto         			printf("ulink: leapchar %x\n", leapchar);
384182007Sroberto                           }
385182007Sroberto                           #endif
386182007Sroberto                           */
38782498Sroberto
388182007Sroberto		        }
389182007Sroberto                    }
390182007Sroberto		    break;
39182498Sroberto		}
392182007Sroberto
39382498Sroberto		case LEN320:
394182007Sroberto
39582498Sroberto	        /*
39682498Sroberto		 * Model 320 Decoder
39782498Sroberto		 * The timecode format is:
39882498Sroberto		 *
39982498Sroberto		 *  <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
40082498Sroberto		 *
40182498Sroberto		 * where:
40282498Sroberto		 *
40382498Sroberto		 * S = 'S' -- sync'd in last hour,
40482498Sroberto		 *     '0'-'9' - hours x 10 since last update,
40582498Sroberto		 *     '?' -- not in sync
40682498Sroberto		 * Q = Number of correlating time-frames, from 0 to 5
40782498Sroberto		 * R = 'R' -- reception in progress,
40882498Sroberto		 *     'N' -- Noisy reception,
40982498Sroberto		 *     ' ' -- standby mode
41082498Sroberto		 * YYYY = year from 1990 to 2089
41182498Sroberto		 * DDD = current day from 1 to 366
41282498Sroberto		 * + = '+' if current year is a leap year, else ' '
41382498Sroberto		 * HH = UTC hour 0 to 23
41482498Sroberto		 * MM = Minutes of current hour from 0 to 59
41582498Sroberto		 * SS = Seconds of current minute from 0 to 59
41682498Sroberto		 * mm = 10's milliseconds of the current second from 00 to 99
41782498Sroberto		 * L  = Leap second pending at end of month
41882498Sroberto		 *     'I' = insert, 'D'= delete
41982498Sroberto		 * T  = DST <-> STD transition indicators
42082498Sroberto		 *
42182498Sroberto        	 */
422182007Sroberto
423132451Sroberto		if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
42482498Sroberto	               &syncchar, &quality, &modechar, &pp->year, &pp->day,
42582498Sroberto        	       &pp->hour, &pp->minute, &pp->second,
426132451Sroberto			&pp->nsec, &leapchar) == 10) {
427132451Sroberto		pp->nsec *= 10000000; /* M320 returns 10's of msecs */
42882498Sroberto		if (leapchar == 'I' ) leapchar = '+';
42982498Sroberto		if (leapchar == 'D' ) leapchar = '-';
43082498Sroberto		if (syncchar != '?' ) syncchar = ':';
43182498Sroberto
43282498Sroberto 		break;
43382498Sroberto		}
43482498Sroberto
43582498Sroberto		default:
43682498Sroberto		refclock_report(peer, CEVNT_BADREPLY);
43782498Sroberto		return;
43882498Sroberto	}
43982498Sroberto
44054359Sroberto	/*
44182498Sroberto	 * Decode quality indicator
44282498Sroberto	 * For the 325 & 33x series, the lower the number the "better"
44382498Sroberto	 * the time is. I used the dispersion as the measure of time
44482498Sroberto	 * quality. The quality indicator in the 320 is the number of
44582498Sroberto	 * correlating time frames (the more the better)
44654359Sroberto	 */
44754359Sroberto
44882498Sroberto	/*
44982498Sroberto	 * The spec sheet for the 325 & 33x series states the clock will
45082498Sroberto	 * maintain +/-0.002 seconds accuracy when locked to WWVB. This
45182498Sroberto	 * is indicated by 'Lk' in the quality portion of the incoming
45282498Sroberto	 * string. When not in lock, a drift of +/-0.015 seconds should
45382498Sroberto	 * be allowed for.
45482498Sroberto	 * With the quality indicator decoding scheme above, the 'Lk'
45582498Sroberto	 * condition will produce a quality value of 0. If the quality
45682498Sroberto	 * indicator starts with '0' then the second character is the
45782498Sroberto	 * number of hours since we were last locked. If the first
45882498Sroberto	 * character is anything other than 'L' or '0' then we have been
45982498Sroberto	 * out of lock for more than 9 hours so we assume the worst and
46082498Sroberto	 * force a quality value that selects the 'default' maximum
46182498Sroberto	 * dispersion. The dispersion values below are what came with the
46282498Sroberto	 * driver. They're not unreasonable so they've not been changed.
46382498Sroberto	 */
46454359Sroberto
46582498Sroberto	if (pp->lencode == LEN33X) {
46682498Sroberto		switch (quality) {
46782498Sroberto			case 0 :
46882498Sroberto				pp->disp=.002;
46982498Sroberto				break;
47082498Sroberto			case 1 :
47182498Sroberto				pp->disp=.02;
47282498Sroberto				break;
47382498Sroberto			case 2 :
47482498Sroberto				pp->disp=.04;
47582498Sroberto				break;
47682498Sroberto			case 3 :
47782498Sroberto				pp->disp=.08;
47882498Sroberto				break;
47982498Sroberto			default:
48082498Sroberto				pp->disp=MAXDISPERSE;
48182498Sroberto				break;
48282498Sroberto		}
48382498Sroberto	} else {
48482498Sroberto		switch (quality) {
48582498Sroberto			case 5 :
48682498Sroberto				pp->disp=.002;
48782498Sroberto				break;
48882498Sroberto			case 4 :
48982498Sroberto				pp->disp=.02;
49082498Sroberto				break;
49182498Sroberto			case 3 :
49282498Sroberto				pp->disp=.04;
49382498Sroberto				break;
49482498Sroberto			case 2 :
49582498Sroberto				pp->disp=.08;
49682498Sroberto				break;
49782498Sroberto			case 1 :
49882498Sroberto				pp->disp=.16;
49982498Sroberto				break;
50082498Sroberto			default:
50182498Sroberto				pp->disp=MAXDISPERSE;
50282498Sroberto				break;
50382498Sroberto		}
50482498Sroberto
50582498Sroberto	}
50682498Sroberto
50754359Sroberto	/*
50882498Sroberto	 * Decode synchronization, and leap characters. If
50954359Sroberto	 * unsynchronized, set the leap bits accordingly and exit.
51054359Sroberto	 * Otherwise, set the leap bits according to the leap character.
51154359Sroberto	 */
51254359Sroberto
51382498Sroberto	if (syncchar != ':')
51482498Sroberto		pp->leap = LEAP_NOTINSYNC;
51582498Sroberto	else if (leapchar == '+')
51682498Sroberto		pp->leap = LEAP_ADDSECOND;
51782498Sroberto	else if (leapchar == '-')
51882498Sroberto		pp->leap = LEAP_DELSECOND;
51982498Sroberto	else
52082498Sroberto		pp->leap = LEAP_NOWARNING;
52182498Sroberto
52254359Sroberto	/*
52354359Sroberto	 * Process the new sample in the median filter and determine the
52454359Sroberto	 * timecode timestamp.
52554359Sroberto	 */
52682498Sroberto	if (!refclock_process(pp)) {
52754359Sroberto		refclock_report(peer, CEVNT_BADTIME);
52882498Sroberto	}
52982498Sroberto
53054359Sroberto}
53154359Sroberto
53254359Sroberto/*
53354359Sroberto * ulink_poll - called by the transmit procedure
53454359Sroberto */
535182007Sroberto
53654359Srobertostatic void
53754359Srobertoulink_poll(
53854359Sroberto	int unit,
53954359Sroberto	struct peer *peer
54054359Sroberto	)
54154359Sroberto{
54282498Sroberto        struct refclockproc *pp;
54382498Sroberto        char pollchar;
54454359Sroberto
54582498Sroberto        pp = peer->procptr;
54682498Sroberto        pollchar = 'T';
54782498Sroberto	if (pp->sloppyclockflag & CLK_FLAG1) {
54882498Sroberto	        if (write(pp->io.fd, &pollchar, 1) != 1)
54982498Sroberto        	        refclock_report(peer, CEVNT_FAULT);
55082498Sroberto        	else
55182498Sroberto      	            pp->polls++;
55282498Sroberto	}
55354359Sroberto	else
55482498Sroberto      	            pp->polls++;
55554359Sroberto
55682498Sroberto        if (pp->coderecv == pp->codeproc) {
55782498Sroberto                refclock_report(peer, CEVNT_TIMEOUT);
55882498Sroberto                return;
55982498Sroberto        }
560132451Sroberto        pp->lastref = pp->lastrec;
561132451Sroberto	refclock_receive(peer);
562132451Sroberto	record_clock_stats(&peer->srcadr, pp->a_lastcode);
56382498Sroberto
56454359Sroberto}
56554359Sroberto
56654359Sroberto#else
56754359Srobertoint refclock_ulink_bs;
56854359Sroberto#endif /* REFCLOCK */
569