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