]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/ntp/ntpd/refclock_ulink.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / ntp / ntpd / refclock_ulink.c
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     P((int, struct peer *));
110 static  void    ulink_shutdown  P((int, struct peer *));
111 static  void    ulink_receive   P((struct recvbuf *));
112 static  void    ulink_poll      P((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 */