xref: /freebsd/contrib/ntp/ntpd/refclock_ulink.c (revision f5f40dd63bc7acbb5312b26ac1ea1103c12352a6)
1c0b746e5SOllivier Robert /*
2224ba2bdSOllivier Robert  * refclock_ulink - clock driver for Ultralink  WWVB receiver
3c0b746e5SOllivier Robert  */
4c0b746e5SOllivier Robert 
5c0b746e5SOllivier Robert #ifdef HAVE_CONFIG_H
6c0b746e5SOllivier Robert #include <config.h>
7c0b746e5SOllivier Robert #endif
8c0b746e5SOllivier Robert 
9c0b746e5SOllivier Robert #if defined(REFCLOCK) && defined(CLOCK_ULINK)
10c0b746e5SOllivier Robert 
11c0b746e5SOllivier Robert #include <stdio.h>
12c0b746e5SOllivier Robert #include <ctype.h>
13c0b746e5SOllivier Robert 
14c0b746e5SOllivier Robert #include "ntpd.h"
15c0b746e5SOllivier Robert #include "ntp_io.h"
16c0b746e5SOllivier Robert #include "ntp_refclock.h"
17c0b746e5SOllivier Robert #include "ntp_stdlib.h"
18c0b746e5SOllivier Robert 
19ea906c41SOllivier Robert /* This driver supports ultralink Model 320,325,330,331,332 WWVB radios
20c0b746e5SOllivier Robert  *
21224ba2bdSOllivier Robert  * this driver was based on the refclock_wwvb.c driver
22224ba2bdSOllivier Robert  * in the ntp distribution.
23c0b746e5SOllivier Robert  *
24224ba2bdSOllivier Robert  * Fudge Factors
25c0b746e5SOllivier Robert  *
26224ba2bdSOllivier Robert  * fudge flag1 0 don't poll clock
27224ba2bdSOllivier Robert  *             1 send poll character
28c0b746e5SOllivier Robert  *
29224ba2bdSOllivier Robert  * revision history:
30224ba2bdSOllivier Robert  *		99/9/09 j.c.lang	original edit's
31224ba2bdSOllivier Robert  *		99/9/11 j.c.lang	changed timecode parse to
32224ba2bdSOllivier Robert  *                                      match what the radio actually
33224ba2bdSOllivier Robert  *                                      sends.
34224ba2bdSOllivier Robert  *              99/10/11 j.c.lang       added support for continous
35224ba2bdSOllivier Robert  *                                      time code mode (dipsw2)
36224ba2bdSOllivier Robert  *		99/11/26 j.c.lang	added support for 320 decoder
37224ba2bdSOllivier Robert  *                                      (taken from Dave Strout's
38224ba2bdSOllivier Robert  *                                      Model 320 driver)
39224ba2bdSOllivier Robert  *		99/11/29 j.c.lang	added fudge flag 1 to control
40224ba2bdSOllivier Robert  *					clock polling
41224ba2bdSOllivier Robert  *		99/12/15 j.c.lang	fixed 320 quality flag
42224ba2bdSOllivier Robert  *		01/02/21 s.l.smith	fixed 33x quality flag
43224ba2bdSOllivier Robert  *					added more debugging stuff
44224ba2bdSOllivier Robert  *					updated 33x time code explanation
45ea906c41SOllivier Robert  *		04/01/23 frank migge	added support for 325 decoder
46ea906c41SOllivier Robert  *                                      (tested with ULM325.F)
47c0b746e5SOllivier Robert  *
48224ba2bdSOllivier Robert  * Questions, bugs, ideas send to:
49224ba2bdSOllivier Robert  *	Joseph C. Lang
50224ba2bdSOllivier Robert  *	tcnojl1@earthlink.net
51c0b746e5SOllivier Robert  *
52224ba2bdSOllivier Robert  *	Dave Strout
53224ba2bdSOllivier Robert  *	dstrout@linuxfoundry.com
54c0b746e5SOllivier Robert  *
55ea906c41SOllivier Robert  *      Frank Migge
56ea906c41SOllivier Robert  *      frank.migge@oracle.com
57ea906c41SOllivier Robert  *
58224ba2bdSOllivier Robert  *
59224ba2bdSOllivier Robert  * on the Ultralink model 33X decoder Dip switch 2 controls
60224ba2bdSOllivier Robert  * polled or continous timecode
61ea906c41SOllivier Robert  * set fudge flag1 if using polled (needed for model 320 and 325)
62224ba2bdSOllivier Robert  * dont set fudge flag1 if dip switch 2 is set on model 33x decoder
63c0b746e5SOllivier Robert */
64c0b746e5SOllivier Robert 
65224ba2bdSOllivier Robert 
66c0b746e5SOllivier Robert /*
67c0b746e5SOllivier Robert  * Interface definitions
68c0b746e5SOllivier Robert  */
69224ba2bdSOllivier Robert #define	DEVICE		"/dev/wwvb%d" /* device name and unit */
70c0b746e5SOllivier Robert #define	SPEED232	B9600	/* uart speed (9600 baud) */
71224ba2bdSOllivier Robert #define	PRECISION	(-10)	/* precision assumed (about 10 ms) */
72224ba2bdSOllivier Robert #define	REFID		"WWVB"	/* reference ID */
73c0b746e5SOllivier Robert #define	DESCRIPTION	"Ultralink WWVB Receiver" /* WRU */
74c0b746e5SOllivier Robert 
75ea906c41SOllivier Robert #define	LEN33X		32	/* timecode length Model 33X and 325 */
76224ba2bdSOllivier Robert #define LEN320		24	/* timecode length Model 320 */
77c0b746e5SOllivier Robert 
78ea906c41SOllivier Robert #define	SIGLCHAR33x	'S'	/* signal strength identifier char 325 */
79ea906c41SOllivier Robert #define	SIGLCHAR325	'R'	/* signal strength identifier char 33x */
80ea906c41SOllivier Robert 
81c0b746e5SOllivier Robert /*
82224ba2bdSOllivier Robert  *  unit control structure
83c0b746e5SOllivier Robert  */
84c0b746e5SOllivier Robert struct ulinkunit {
85c0b746e5SOllivier Robert 	u_char	tcswitch;	/* timecode switch */
86c0b746e5SOllivier Robert 	l_fp	laststamp;	/* last receive timestamp */
87c0b746e5SOllivier Robert };
88c0b746e5SOllivier Robert 
89c0b746e5SOllivier Robert /*
90c0b746e5SOllivier Robert  * Function prototypes
91c0b746e5SOllivier Robert  */
922b15cb3dSCy Schubert static	int	ulink_start	(int, struct peer *);
932b15cb3dSCy Schubert static	void	ulink_shutdown	(int, struct peer *);
942b15cb3dSCy Schubert static	void	ulink_receive	(struct recvbuf *);
952b15cb3dSCy Schubert static	void	ulink_poll	(int, struct peer *);
96c0b746e5SOllivier Robert 
97c0b746e5SOllivier Robert /*
98c0b746e5SOllivier Robert  * Transfer vector
99c0b746e5SOllivier Robert  */
100c0b746e5SOllivier Robert struct	refclock refclock_ulink = {
101c0b746e5SOllivier Robert 	ulink_start,		/* start up driver */
102c0b746e5SOllivier Robert 	ulink_shutdown,		/* shut down driver */
103c0b746e5SOllivier Robert 	ulink_poll,		/* transmit poll message */
104224ba2bdSOllivier Robert 	noentry,		/* not used  */
105224ba2bdSOllivier Robert 	noentry,		/* not used  */
106224ba2bdSOllivier Robert 	noentry,		/* not used  */
107224ba2bdSOllivier Robert 	NOFLAGS
108c0b746e5SOllivier Robert };
109c0b746e5SOllivier Robert 
110c0b746e5SOllivier Robert 
111c0b746e5SOllivier Robert /*
112c0b746e5SOllivier Robert  * ulink_start - open the devices and initialize data for processing
113c0b746e5SOllivier Robert  */
114c0b746e5SOllivier Robert static int
115c0b746e5SOllivier Robert ulink_start(
116c0b746e5SOllivier Robert 	int unit,
117c0b746e5SOllivier Robert 	struct peer *peer
118c0b746e5SOllivier Robert 	)
119c0b746e5SOllivier Robert {
120c0b746e5SOllivier Robert 	register struct ulinkunit *up;
121c0b746e5SOllivier Robert 	struct refclockproc *pp;
122224ba2bdSOllivier Robert 	int fd;
123c0b746e5SOllivier Robert 	char device[20];
124224ba2bdSOllivier Robert 
125c0b746e5SOllivier Robert 	/*
126c0b746e5SOllivier Robert 	 * Open serial port. Use CLK line discipline, if available.
127c0b746e5SOllivier Robert 	 */
1282b15cb3dSCy Schubert 	snprintf(device, sizeof(device), DEVICE, unit);
129a466cc55SCy Schubert 	fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_CLK);
1302b15cb3dSCy Schubert 	if (fd <= 0)
131c0b746e5SOllivier Robert 		return (0);
132c0b746e5SOllivier Robert 
133c0b746e5SOllivier Robert 	/*
134c0b746e5SOllivier Robert 	 * Allocate and initialize unit structure
135c0b746e5SOllivier Robert 	 */
1362b15cb3dSCy Schubert 	up = emalloc(sizeof(struct ulinkunit));
1372b15cb3dSCy Schubert 	memset(up, 0, sizeof(struct ulinkunit));
138c0b746e5SOllivier Robert 	pp = peer->procptr;
139c0b746e5SOllivier Robert 	pp->io.clock_recv = ulink_receive;
1402b15cb3dSCy Schubert 	pp->io.srcclock = peer;
141c0b746e5SOllivier Robert 	pp->io.datalen = 0;
142c0b746e5SOllivier Robert 	pp->io.fd = fd;
143c0b746e5SOllivier Robert 	if (!io_addclock(&pp->io)) {
1442b15cb3dSCy Schubert 		close(fd);
1452b15cb3dSCy Schubert 		pp->io.fd = -1;
146c0b746e5SOllivier Robert 		free(up);
147c0b746e5SOllivier Robert 		return (0);
148c0b746e5SOllivier Robert 	}
1492b15cb3dSCy Schubert 	pp->unitptr = up;
150c0b746e5SOllivier Robert 
151c0b746e5SOllivier Robert 	/*
152c0b746e5SOllivier Robert 	 * Initialize miscellaneous variables
153c0b746e5SOllivier Robert 	 */
154c0b746e5SOllivier Robert 	peer->precision = PRECISION;
155c0b746e5SOllivier Robert 	pp->clockdesc = DESCRIPTION;
156c0b746e5SOllivier Robert 	memcpy((char *)&pp->refid, REFID, 4);
157c0b746e5SOllivier Robert 	return (1);
158c0b746e5SOllivier Robert }
159c0b746e5SOllivier Robert 
160c0b746e5SOllivier Robert 
161c0b746e5SOllivier Robert /*
162c0b746e5SOllivier Robert  * ulink_shutdown - shut down the clock
163c0b746e5SOllivier Robert  */
164c0b746e5SOllivier Robert static void
165c0b746e5SOllivier Robert ulink_shutdown(
166c0b746e5SOllivier Robert 	int unit,
167c0b746e5SOllivier Robert 	struct peer *peer
168c0b746e5SOllivier Robert 	)
169c0b746e5SOllivier Robert {
170c0b746e5SOllivier Robert 	register struct ulinkunit *up;
171c0b746e5SOllivier Robert 	struct refclockproc *pp;
172c0b746e5SOllivier Robert 
173c0b746e5SOllivier Robert 	pp = peer->procptr;
1742b15cb3dSCy Schubert 	up = pp->unitptr;
1752b15cb3dSCy Schubert 	if (pp->io.fd != -1)
176c0b746e5SOllivier Robert 		io_closeclock(&pp->io);
1772b15cb3dSCy Schubert 	if (up != NULL)
178c0b746e5SOllivier Robert 		free(up);
179c0b746e5SOllivier Robert }
180c0b746e5SOllivier Robert 
181c0b746e5SOllivier Robert 
182c0b746e5SOllivier Robert /*
183c0b746e5SOllivier Robert  * ulink_receive - receive data from the serial interface
184c0b746e5SOllivier Robert  */
185c0b746e5SOllivier Robert static void
186c0b746e5SOllivier Robert ulink_receive(
187c0b746e5SOllivier Robert 	struct recvbuf *rbufp
188c0b746e5SOllivier Robert 	)
189c0b746e5SOllivier Robert {
190c0b746e5SOllivier Robert 	struct ulinkunit *up;
191c0b746e5SOllivier Robert 	struct refclockproc *pp;
192c0b746e5SOllivier Robert 	struct peer *peer;
193c0b746e5SOllivier Robert 
194c0b746e5SOllivier Robert 	l_fp	trtmp;			/* arrival timestamp */
1952b15cb3dSCy Schubert 	int	quality = INT_MAX;	/* quality indicator */
196c0b746e5SOllivier Robert 	int	temp;			/* int temp */
197224ba2bdSOllivier Robert 	char	syncchar;		/* synchronization indicator */
198224ba2bdSOllivier Robert 	char	leapchar;		/* leap indicator */
199224ba2bdSOllivier Robert 	char	modechar;		/* model 320 mode flag */
200ea906c41SOllivier Robert         char	siglchar;		/* model difference between 33x/325 */
201224ba2bdSOllivier Robert 	char	char_quality[2];	/* temp quality flag */
202c0b746e5SOllivier Robert 
203c0b746e5SOllivier Robert 	/*
204c0b746e5SOllivier Robert 	 * Initialize pointers and read the timecode and timestamp
205c0b746e5SOllivier Robert 	 */
2062b15cb3dSCy Schubert 	peer = rbufp->recv_peer;
207c0b746e5SOllivier Robert 	pp = peer->procptr;
2082b15cb3dSCy Schubert 	up = pp->unitptr;
209c0b746e5SOllivier Robert 	temp = refclock_gtlin(rbufp, pp->a_lastcode, BMAX, &trtmp);
210c0b746e5SOllivier Robert 
211c0b746e5SOllivier Robert 	/*
212c0b746e5SOllivier Robert 	 * Note we get a buffer and timestamp for both a <cr> and <lf>,
213224ba2bdSOllivier Robert 	 * but only the <cr> timestamp is retained.
214c0b746e5SOllivier Robert 	 */
215c0b746e5SOllivier Robert 	if (temp == 0) {
216c0b746e5SOllivier Robert 		if (up->tcswitch == 0) {
217c0b746e5SOllivier Robert 			up->tcswitch = 1;
218c0b746e5SOllivier Robert 			up->laststamp = trtmp;
219c0b746e5SOllivier Robert 		} else
220c0b746e5SOllivier Robert 		    up->tcswitch = 0;
221c0b746e5SOllivier Robert 		return;
222c0b746e5SOllivier Robert 	}
223c0b746e5SOllivier Robert 	pp->lencode = temp;
224c0b746e5SOllivier Robert 	pp->lastrec = up->laststamp;
225c0b746e5SOllivier Robert 	up->laststamp = trtmp;
226c0b746e5SOllivier Robert 	up->tcswitch = 1;
227c0b746e5SOllivier Robert #ifdef DEBUG
228c0b746e5SOllivier Robert 	if (debug)
229c0b746e5SOllivier Robert 		printf("ulink: timecode %d %s\n", pp->lencode,
230c0b746e5SOllivier Robert 		    pp->a_lastcode);
231c0b746e5SOllivier Robert #endif
232c0b746e5SOllivier Robert 
233c0b746e5SOllivier Robert 	/*
234c0b746e5SOllivier Robert 	 * We get down to business, check the timecode format and decode
235224ba2bdSOllivier Robert 	 * its contents. If the timecode has invalid length or is not in
236224ba2bdSOllivier Robert 	 * proper format, we declare bad format and exit.
237c0b746e5SOllivier Robert 	 */
238ea906c41SOllivier Robert 	syncchar = leapchar = modechar = siglchar = ' ';
239224ba2bdSOllivier Robert 	switch (pp->lencode ) {
240224ba2bdSOllivier Robert 		case LEN33X:
241ea906c41SOllivier Robert 
242c0b746e5SOllivier Robert 		/*
243ea906c41SOllivier Robert                  * First we check if the format is 33x or 325:
244ea906c41SOllivier Robert 		 *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5 (33x)
245ea906c41SOllivier Robert 		 *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5 (325)
246ea906c41SOllivier Robert 		 * simply by comparing if the signal level is 'S' or 'R'
247ea906c41SOllivier Robert                  */
248ea906c41SOllivier Robert 
249ea906c41SOllivier Robert                  if (sscanf(pp->a_lastcode, "%c%*31c",
250ea906c41SOllivier Robert                             &siglchar) == 1) {
251ea906c41SOllivier Robert 
252ea906c41SOllivier Robert                     if(siglchar == SIGLCHAR325) {
253ea906c41SOllivier Robert 
254ea906c41SOllivier Robert        		   /*
255ea906c41SOllivier Robert 		    * decode for a Model 325 decoder.
256ea906c41SOllivier Robert 		    * Timecode format from January 23, 2004 datasheet is:
257ea906c41SOllivier Robert                     *
258ea906c41SOllivier Robert 		    *   <CR><LF>R5_1C00LYYYY+DDDUTCS HH:MM:SSL+5
259ea906c41SOllivier Robert                     *
260ea906c41SOllivier Robert 		    *   R      WWVB decodersignal readability R1 - R5
261ea906c41SOllivier Robert 		    *   5      R1 is unreadable, R5 is best
262ea906c41SOllivier Robert 		    *   space  a space (0x20)
263ea906c41SOllivier Robert 		    *   1      Data bit 0, 1, M (pos mark), or ? (unknown).
264ea906c41SOllivier Robert 		    *   C      Reception from either (C)olorado or (H)awaii
265ea906c41SOllivier Robert 		    *   00     Hours since last good WWVB frame sync. Will
266ea906c41SOllivier Robert 		    *          be 00-99
267ea906c41SOllivier Robert 		    *   space  Space char (0x20) or (0xa5) if locked to wwvb
268ea906c41SOllivier Robert 		    *   YYYY   Current year, 2000-2099
269ea906c41SOllivier Robert 		    *   +      Leap year indicator. '+' if a leap year,
270ea906c41SOllivier Robert 		    *          a space (0x20) if not.
271ea906c41SOllivier Robert 		    *   DDD    Day of year, 000 - 365.
272ea906c41SOllivier Robert 		    *   UTC    Timezone (always 'UTC').
273ea906c41SOllivier Robert 		    *   S      Daylight savings indicator
274ea906c41SOllivier Robert 		    *             S - standard time (STD) in effect
275ea906c41SOllivier Robert 		    *             O - during STD to DST day 0000-2400
276ea906c41SOllivier Robert 		    *             D - daylight savings time (DST) in effect
277ea906c41SOllivier Robert 		    *             I - during DST to STD day 0000-2400
278ea906c41SOllivier Robert 		    *   space  Space character (0x20)
279ea906c41SOllivier Robert 		    *   HH     Hours 00-23
280ea906c41SOllivier Robert 		    *   :      This is the REAL in sync indicator (: = insync)
281ea906c41SOllivier Robert 		    *   MM     Minutes 00-59
282ea906c41SOllivier Robert 		    *   :      : = in sync ? = NOT in sync
283ea906c41SOllivier Robert 		    *   SS     Seconds 00-59
284ea906c41SOllivier Robert 		    *   L      Leap second flag. Changes from space (0x20)
285ea906c41SOllivier Robert 		    *          to 'I' or 'D' during month preceding leap
286ea906c41SOllivier Robert 		    *          second adjustment. (I)nsert or (D)elete
287ea906c41SOllivier Robert 		    *   +5     UT1 correction (sign + digit ))
288ea906c41SOllivier Robert 		    */
289ea906c41SOllivier Robert 
290ea906c41SOllivier Robert    		       if (sscanf(pp->a_lastcode,
291ea906c41SOllivier Robert                           "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
292ea906c41SOllivier Robert    		          char_quality, &pp->year, &pp->day,
293ea906c41SOllivier Robert                           &pp->hour, &syncchar, &pp->minute, &pp->second,
294ea906c41SOllivier Robert                           &leapchar) == 8) {
295ea906c41SOllivier Robert 
296ea906c41SOllivier Robert    			  if (char_quality[0] == '0') {
297ea906c41SOllivier Robert    				quality = 0;
298ea906c41SOllivier Robert    			  } else if (char_quality[0] == '0') {
299ea906c41SOllivier Robert    				quality = (char_quality[1] & 0x0f);
300ea906c41SOllivier Robert    			  } else  {
301ea906c41SOllivier Robert    				quality = 99;
302ea906c41SOllivier Robert    			  }
303ea906c41SOllivier Robert 
304ea906c41SOllivier Robert    		          if (leapchar == 'I' ) leapchar = '+';
305ea906c41SOllivier Robert    		          if (leapchar == 'D' ) leapchar = '-';
306ea906c41SOllivier Robert 
307ea906c41SOllivier Robert 		          /*
308ea906c41SOllivier Robert 		          #ifdef DEBUG
309ea906c41SOllivier Robert 		          if (debug) {
310ea906c41SOllivier Robert 		             printf("ulink: char_quality %c %c\n",
311ea906c41SOllivier Robert                                     char_quality[0], char_quality[1]);
312ea906c41SOllivier Robert 			     printf("ulink: quality %d\n", quality);
313ea906c41SOllivier Robert 			     printf("ulink: syncchar %x\n", syncchar);
314ea906c41SOllivier Robert 			     printf("ulink: leapchar %x\n", leapchar);
315ea906c41SOllivier Robert                           }
316ea906c41SOllivier Robert                           #endif
317ea906c41SOllivier Robert                           */
318ea906c41SOllivier Robert 
319ea906c41SOllivier Robert                        }
320ea906c41SOllivier Robert 
321ea906c41SOllivier Robert                     }
322ea906c41SOllivier Robert                     if(siglchar == SIGLCHAR33x) {
323ea906c41SOllivier Robert 
324ea906c41SOllivier Robert 		   /*
325ea906c41SOllivier Robert 		    * We got a Model 33X decoder.
326224ba2bdSOllivier Robert 		    * Timecode format from January 29, 2001 datasheet is:
327224ba2bdSOllivier Robert 		    *   <CR><LF>S9+D 00 YYYY+DDDUTCS HH:MM:SSL+5
328224ba2bdSOllivier Robert 		    *   S      WWVB decoder sync indicator. S for in-sync(?)
329224ba2bdSOllivier Robert 		    *          or N for noisy signal.
330224ba2bdSOllivier Robert 		    *   9+     RF signal level in S-units, 0-9 followed by
331224ba2bdSOllivier Robert 		    *          a space (0x20). The space turns to '+' if the
332224ba2bdSOllivier Robert 		    *          level is over 9.
333224ba2bdSOllivier Robert 		    *   D      Data bit 0, 1, 2 (position mark), or
334224ba2bdSOllivier Robert 		    *          3 (unknown).
335224ba2bdSOllivier Robert 		    *   space  Space character (0x20)
336224ba2bdSOllivier Robert 		    *   00     Hours since last good WWVB frame sync. Will
337224ba2bdSOllivier Robert 		    *          be 00-23 hrs, or '1d' to '7d'. Will be 'Lk'
338224ba2bdSOllivier Robert                     *          if currently in sync.
339224ba2bdSOllivier Robert 		    *   space  Space character (0x20)
340224ba2bdSOllivier Robert 		    *   YYYY   Current year, 1990-2089
341224ba2bdSOllivier Robert 		    *   +      Leap year indicator. '+' if a leap year,
342224ba2bdSOllivier Robert 		    *          a space (0x20) if not.
343224ba2bdSOllivier Robert 		    *   DDD    Day of year, 001 - 366.
344224ba2bdSOllivier Robert 		    *   UTC    Timezone (always 'UTC').
345224ba2bdSOllivier Robert 		    *   S      Daylight savings indicator
346224ba2bdSOllivier Robert 		    *             S - standard time (STD) in effect
347224ba2bdSOllivier Robert 		    *             O - during STD to DST day 0000-2400
348224ba2bdSOllivier Robert 		    *             D - daylight savings time (DST) in effect
349224ba2bdSOllivier Robert 		    *             I - during DST to STD day 0000-2400
350224ba2bdSOllivier Robert 		    *   space  Space character (0x20)
351224ba2bdSOllivier Robert 		    *   HH     Hours 00-23
352224ba2bdSOllivier Robert 		    *   :      This is the REAL in sync indicator (: = insync)
353224ba2bdSOllivier Robert 		    *   MM     Minutes 00-59
354224ba2bdSOllivier Robert 		    *   :      : = in sync ? = NOT in sync
355224ba2bdSOllivier Robert 		    *   SS     Seconds 00-59
356224ba2bdSOllivier Robert 		    *   L      Leap second flag. Changes from space (0x20)
357224ba2bdSOllivier Robert 		    *          to '+' or '-' during month preceding leap
358224ba2bdSOllivier Robert 		    *          second adjustment.
359224ba2bdSOllivier Robert 		    *   +5     UT1 correction (sign + digit ))
360c0b746e5SOllivier Robert 		    */
361c0b746e5SOllivier Robert 
362224ba2bdSOllivier Robert 		       if (sscanf(pp->a_lastcode,
363224ba2bdSOllivier Robert                            "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
364224ba2bdSOllivier Robert 		           char_quality, &pp->year, &pp->day,
365224ba2bdSOllivier Robert                            &pp->hour, &syncchar, &pp->minute, &pp->second,
366224ba2bdSOllivier Robert                            &leapchar) == 8) {
367224ba2bdSOllivier Robert 
368224ba2bdSOllivier Robert 			   if (char_quality[0] == 'L') {
369224ba2bdSOllivier Robert 				quality = 0;
370224ba2bdSOllivier Robert 			   } else if (char_quality[0] == '0') {
371224ba2bdSOllivier Robert 				quality = (char_quality[1] & 0x0f);
372224ba2bdSOllivier Robert 			   } else  {
373224ba2bdSOllivier Robert 				quality = 99;
374224ba2bdSOllivier Robert 		           }
375c0b746e5SOllivier Robert 
376c0b746e5SOllivier Robert                            /*
377224ba2bdSOllivier Robert                            #ifdef DEBUG
378224ba2bdSOllivier Robert          		   if (debug) {
379224ba2bdSOllivier Robert          			printf("ulink: char_quality %c %c\n",
380224ba2bdSOllivier Robert                                         char_quality[0], char_quality[1]);
381224ba2bdSOllivier Robert          			printf("ulink: quality %d\n", quality);
382224ba2bdSOllivier Robert          			printf("ulink: syncchar %x\n", syncchar);
383224ba2bdSOllivier Robert          			printf("ulink: leapchar %x\n", leapchar);
384224ba2bdSOllivier Robert                            }
385224ba2bdSOllivier Robert                            #endif
386224ba2bdSOllivier Robert                            */
387224ba2bdSOllivier Robert 
388ea906c41SOllivier Robert 		        }
389ea906c41SOllivier Robert                     }
390224ba2bdSOllivier Robert 		    break;
391224ba2bdSOllivier Robert 		}
392224ba2bdSOllivier Robert 
393224ba2bdSOllivier Robert 		case LEN320:
394ea906c41SOllivier Robert 
395224ba2bdSOllivier Robert 	        /*
396224ba2bdSOllivier Robert 		 * Model 320 Decoder
397224ba2bdSOllivier Robert 		 * The timecode format is:
398224ba2bdSOllivier Robert 		 *
399224ba2bdSOllivier Robert 		 *  <cr><lf>SQRYYYYDDD+HH:MM:SS.mmLT<cr>
400224ba2bdSOllivier Robert 		 *
401224ba2bdSOllivier Robert 		 * where:
402224ba2bdSOllivier Robert 		 *
403224ba2bdSOllivier Robert 		 * S = 'S' -- sync'd in last hour,
404224ba2bdSOllivier Robert 		 *     '0'-'9' - hours x 10 since last update,
405224ba2bdSOllivier Robert 		 *     '?' -- not in sync
406224ba2bdSOllivier Robert 		 * Q = Number of correlating time-frames, from 0 to 5
407224ba2bdSOllivier Robert 		 * R = 'R' -- reception in progress,
408224ba2bdSOllivier Robert 		 *     'N' -- Noisy reception,
409224ba2bdSOllivier Robert 		 *     ' ' -- standby mode
410224ba2bdSOllivier Robert 		 * YYYY = year from 1990 to 2089
411224ba2bdSOllivier Robert 		 * DDD = current day from 1 to 366
412224ba2bdSOllivier Robert 		 * + = '+' if current year is a leap year, else ' '
413224ba2bdSOllivier Robert 		 * HH = UTC hour 0 to 23
414224ba2bdSOllivier Robert 		 * MM = Minutes of current hour from 0 to 59
415224ba2bdSOllivier Robert 		 * SS = Seconds of current minute from 0 to 59
416224ba2bdSOllivier Robert 		 * mm = 10's milliseconds of the current second from 00 to 99
417224ba2bdSOllivier Robert 		 * L  = Leap second pending at end of month
418224ba2bdSOllivier Robert 		 *     'I' = insert, 'D'= delete
419224ba2bdSOllivier Robert 		 * T  = DST <-> STD transition indicators
420224ba2bdSOllivier Robert 		 *
421224ba2bdSOllivier Robert         	 */
422ea906c41SOllivier Robert 
4239c2daa00SOllivier Robert 		if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
424224ba2bdSOllivier Robert 	               &syncchar, &quality, &modechar, &pp->year, &pp->day,
425224ba2bdSOllivier Robert         	       &pp->hour, &pp->minute, &pp->second,
4269c2daa00SOllivier Robert 			&pp->nsec, &leapchar) == 10) {
4279c2daa00SOllivier Robert 		pp->nsec *= 10000000; /* M320 returns 10's of msecs */
428224ba2bdSOllivier Robert 		if (leapchar == 'I' ) leapchar = '+';
429224ba2bdSOllivier Robert 		if (leapchar == 'D' ) leapchar = '-';
430224ba2bdSOllivier Robert 		if (syncchar != '?' ) syncchar = ':';
431224ba2bdSOllivier Robert 
432224ba2bdSOllivier Robert  		break;
433224ba2bdSOllivier Robert 		}
434224ba2bdSOllivier Robert 
435224ba2bdSOllivier Robert 		default:
436224ba2bdSOllivier Robert 		refclock_report(peer, CEVNT_BADREPLY);
437224ba2bdSOllivier Robert 		return;
438224ba2bdSOllivier Robert 	}
439224ba2bdSOllivier Robert 
440224ba2bdSOllivier Robert 	/*
441224ba2bdSOllivier Robert 	 * Decode quality indicator
442224ba2bdSOllivier Robert 	 * For the 325 & 33x series, the lower the number the "better"
443224ba2bdSOllivier Robert 	 * the time is. I used the dispersion as the measure of time
444224ba2bdSOllivier Robert 	 * quality. The quality indicator in the 320 is the number of
445224ba2bdSOllivier Robert 	 * correlating time frames (the more the better)
446224ba2bdSOllivier Robert 	 */
447224ba2bdSOllivier Robert 
448224ba2bdSOllivier Robert 	/*
449224ba2bdSOllivier Robert 	 * The spec sheet for the 325 & 33x series states the clock will
450224ba2bdSOllivier Robert 	 * maintain +/-0.002 seconds accuracy when locked to WWVB. This
451224ba2bdSOllivier Robert 	 * is indicated by 'Lk' in the quality portion of the incoming
452224ba2bdSOllivier Robert 	 * string. When not in lock, a drift of +/-0.015 seconds should
453224ba2bdSOllivier Robert 	 * be allowed for.
454224ba2bdSOllivier Robert 	 * With the quality indicator decoding scheme above, the 'Lk'
455224ba2bdSOllivier Robert 	 * condition will produce a quality value of 0. If the quality
456224ba2bdSOllivier Robert 	 * indicator starts with '0' then the second character is the
457224ba2bdSOllivier Robert 	 * number of hours since we were last locked. If the first
458224ba2bdSOllivier Robert 	 * character is anything other than 'L' or '0' then we have been
459224ba2bdSOllivier Robert 	 * out of lock for more than 9 hours so we assume the worst and
460224ba2bdSOllivier Robert 	 * force a quality value that selects the 'default' maximum
461224ba2bdSOllivier Robert 	 * dispersion. The dispersion values below are what came with the
462224ba2bdSOllivier Robert 	 * driver. They're not unreasonable so they've not been changed.
463224ba2bdSOllivier Robert 	 */
464224ba2bdSOllivier Robert 
465224ba2bdSOllivier Robert 	if (pp->lencode == LEN33X) {
466224ba2bdSOllivier Robert 		switch (quality) {
467224ba2bdSOllivier Robert 			case 0 :
468224ba2bdSOllivier Robert 				pp->disp=.002;
469224ba2bdSOllivier Robert 				break;
470224ba2bdSOllivier Robert 			case 1 :
471224ba2bdSOllivier Robert 				pp->disp=.02;
472224ba2bdSOllivier Robert 				break;
473224ba2bdSOllivier Robert 			case 2 :
474224ba2bdSOllivier Robert 				pp->disp=.04;
475224ba2bdSOllivier Robert 				break;
476224ba2bdSOllivier Robert 			case 3 :
477224ba2bdSOllivier Robert 				pp->disp=.08;
478224ba2bdSOllivier Robert 				break;
479224ba2bdSOllivier Robert 			default:
480224ba2bdSOllivier Robert 				pp->disp=MAXDISPERSE;
481224ba2bdSOllivier Robert 				break;
482224ba2bdSOllivier Robert 		}
483224ba2bdSOllivier Robert 	} else {
484224ba2bdSOllivier Robert 		switch (quality) {
485224ba2bdSOllivier Robert 			case 5 :
486224ba2bdSOllivier Robert 				pp->disp=.002;
487224ba2bdSOllivier Robert 				break;
488224ba2bdSOllivier Robert 			case 4 :
489224ba2bdSOllivier Robert 				pp->disp=.02;
490224ba2bdSOllivier Robert 				break;
491224ba2bdSOllivier Robert 			case 3 :
492224ba2bdSOllivier Robert 				pp->disp=.04;
493224ba2bdSOllivier Robert 				break;
494224ba2bdSOllivier Robert 			case 2 :
495224ba2bdSOllivier Robert 				pp->disp=.08;
496224ba2bdSOllivier Robert 				break;
497224ba2bdSOllivier Robert 			case 1 :
498224ba2bdSOllivier Robert 				pp->disp=.16;
499224ba2bdSOllivier Robert 				break;
500224ba2bdSOllivier Robert 			default:
501224ba2bdSOllivier Robert 				pp->disp=MAXDISPERSE;
502224ba2bdSOllivier Robert 				break;
503224ba2bdSOllivier Robert 		}
504224ba2bdSOllivier Robert 
505224ba2bdSOllivier Robert 	}
506224ba2bdSOllivier Robert 
507224ba2bdSOllivier Robert 	/*
508224ba2bdSOllivier Robert 	 * Decode synchronization, and leap characters. If
509c0b746e5SOllivier Robert 	 * unsynchronized, set the leap bits accordingly and exit.
510c0b746e5SOllivier Robert 	 * Otherwise, set the leap bits according to the leap character.
511c0b746e5SOllivier Robert 	 */
512224ba2bdSOllivier Robert 
513224ba2bdSOllivier Robert 	if (syncchar != ':')
514224ba2bdSOllivier Robert 		pp->leap = LEAP_NOTINSYNC;
515224ba2bdSOllivier Robert 	else if (leapchar == '+')
516224ba2bdSOllivier Robert 		pp->leap = LEAP_ADDSECOND;
517224ba2bdSOllivier Robert 	else if (leapchar == '-')
518224ba2bdSOllivier Robert 		pp->leap = LEAP_DELSECOND;
519224ba2bdSOllivier Robert 	else
520c0b746e5SOllivier Robert 		pp->leap = LEAP_NOWARNING;
521c0b746e5SOllivier Robert 
522c0b746e5SOllivier Robert 	/*
523c0b746e5SOllivier Robert 	 * Process the new sample in the median filter and determine the
524c0b746e5SOllivier Robert 	 * timecode timestamp.
525c0b746e5SOllivier Robert 	 */
526224ba2bdSOllivier Robert 	if (!refclock_process(pp)) {
527c0b746e5SOllivier Robert 		refclock_report(peer, CEVNT_BADTIME);
528c0b746e5SOllivier Robert 	}
529c0b746e5SOllivier Robert 
530224ba2bdSOllivier Robert }
531224ba2bdSOllivier Robert 
532c0b746e5SOllivier Robert /*
533c0b746e5SOllivier Robert  * ulink_poll - called by the transmit procedure
534c0b746e5SOllivier Robert  */
535ea906c41SOllivier Robert 
536c0b746e5SOllivier Robert static void
537c0b746e5SOllivier Robert ulink_poll(
538c0b746e5SOllivier Robert 	int unit,
539c0b746e5SOllivier Robert 	struct peer *peer
540c0b746e5SOllivier Robert 	)
541c0b746e5SOllivier Robert {
542c0b746e5SOllivier Robert         struct refclockproc *pp;
543c0b746e5SOllivier Robert         char pollchar;
544c0b746e5SOllivier Robert 
545c0b746e5SOllivier Robert         pp = peer->procptr;
546c0b746e5SOllivier Robert         pollchar = 'T';
547224ba2bdSOllivier Robert 	if (pp->sloppyclockflag & CLK_FLAG1) {
548c0b746e5SOllivier Robert 	        if (write(pp->io.fd, &pollchar, 1) != 1)
549c0b746e5SOllivier Robert         	        refclock_report(peer, CEVNT_FAULT);
550c0b746e5SOllivier Robert         	else
551c0b746e5SOllivier Robert       	            pp->polls++;
552224ba2bdSOllivier Robert 	}
553224ba2bdSOllivier Robert 	else
554224ba2bdSOllivier Robert       	            pp->polls++;
555224ba2bdSOllivier Robert 
556c0b746e5SOllivier Robert         if (pp->coderecv == pp->codeproc) {
557c0b746e5SOllivier Robert                 refclock_report(peer, CEVNT_TIMEOUT);
558c0b746e5SOllivier Robert                 return;
559c0b746e5SOllivier Robert         }
5609c2daa00SOllivier Robert         pp->lastref = pp->lastrec;
561c0b746e5SOllivier Robert 	refclock_receive(peer);
5629c2daa00SOllivier Robert 	record_clock_stats(&peer->srcadr, pp->a_lastcode);
563c0b746e5SOllivier Robert 
564c0b746e5SOllivier Robert }
565c0b746e5SOllivier Robert 
566c0b746e5SOllivier Robert #else
567*f5f40dd6SCy Schubert NONEMPTY_TRANSLATION_UNIT
568c0b746e5SOllivier Robert #endif /* REFCLOCK */
569