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