Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/refclock_ulink.c
Line
Count
Source (jump to first uncovered line)
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
0
#define SPEED232  B9600  /* uart speed (9600 baud) */
71
0
#define PRECISION (-10)  /* precision assumed (about 10 ms) */
72
0
#define REFID   "WWVB"  /* reference ID */
73
0
#define DESCRIPTION "Ultralink WWVB Receiver" /* WRU */
74
75
0
#define LEN33X    32  /* timecode length Model 33X and 325 */
76
0
#define LEN320    24  /* timecode length Model 320 */
77
78
0
#define SIGLCHAR33x 'S'  /* signal strength identifier char 325 */
79
0
#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
0
{
120
0
  register struct ulinkunit *up;
121
0
  struct refclockproc *pp;
122
0
  int fd;
123
0
  char device[20];
124
125
  /*
126
   * Open serial port. Use CLK line discipline, if available.
127
   */
128
0
  snprintf(device, sizeof(device), DEVICE, unit);
129
0
  fd = refclock_open(device, SPEED232, LDISC_CLK);
130
0
  if (fd <= 0)
131
0
    return (0);
132
133
  /*
134
   * Allocate and initialize unit structure
135
   */
136
0
  up = emalloc(sizeof(struct ulinkunit));
137
0
  memset(up, 0, sizeof(struct ulinkunit));
138
0
  pp = peer->procptr;
139
0
  pp->io.clock_recv = ulink_receive;
140
0
  pp->io.srcclock = peer;
141
0
  pp->io.datalen = 0;
142
0
  pp->io.fd = fd;
143
0
  if (!io_addclock(&pp->io)) {
144
0
    close(fd);
145
0
    pp->io.fd = -1;
146
0
    free(up);
147
0
    return (0);
148
0
  }
149
0
  pp->unitptr = up;
150
151
  /*
152
   * Initialize miscellaneous variables
153
   */
154
0
  peer->precision = PRECISION;
155
0
  pp->clockdesc = DESCRIPTION;
156
0
  memcpy((char *)&pp->refid, REFID, 4);
157
0
  return (1);
158
0
}
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
0
{
170
0
  register struct ulinkunit *up;
171
0
  struct refclockproc *pp;
172
173
0
  pp = peer->procptr;
174
0
  up = pp->unitptr;
175
0
  if (pp->io.fd != -1)
176
0
    io_closeclock(&pp->io);
177
0
  if (up != NULL)
178
0
    free(up);
179
0
}
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
0
{
190
0
  struct ulinkunit *up;
191
0
  struct refclockproc *pp;
192
0
  struct peer *peer;
193
194
0
  l_fp  trtmp;      /* arrival timestamp */
195
0
  int quality = INT_MAX;  /* quality indicator */
196
0
  int temp;     /* int temp */
197
0
  char  syncchar;   /* synchronization indicator */
198
0
  char  leapchar;   /* leap indicator */
199
0
  char  modechar;   /* model 320 mode flag */
200
0
        char  siglchar;   /* model difference between 33x/325 */
201
0
  char  char_quality[2];  /* temp quality flag */
202
203
  /*
204
   * Initialize pointers and read the timecode and timestamp
205
   */
206
0
  peer = rbufp->recv_peer;
207
0
  pp = peer->procptr;
208
0
  up = pp->unitptr;
209
0
  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
0
  if (temp == 0) {
216
0
    if (up->tcswitch == 0) {
217
0
      up->tcswitch = 1;
218
0
      up->laststamp = trtmp;
219
0
    } else
220
0
        up->tcswitch = 0;
221
0
    return;
222
0
  }
223
0
  pp->lencode = temp;
224
0
  pp->lastrec = up->laststamp;
225
0
  up->laststamp = trtmp;
226
0
  up->tcswitch = 1;
227
0
#ifdef DEBUG
228
0
  if (debug)
229
0
    printf("ulink: timecode %d %s\n", pp->lencode,
230
0
        pp->a_lastcode);
231
0
#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
0
  syncchar = leapchar = modechar = siglchar = ' ';
239
0
  switch (pp->lencode ) {
240
0
    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
0
                 if (sscanf(pp->a_lastcode, "%c%*31c",
250
0
                            &siglchar) == 1) {
251
252
0
                    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
0
             if (sscanf(pp->a_lastcode, 
291
0
                          "%*2c %*2c%2c%*c%4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
292
0
                char_quality, &pp->year, &pp->day, 
293
0
                          &pp->hour, &syncchar, &pp->minute, &pp->second, 
294
0
                          &leapchar) == 8) { 
295
      
296
0
          if (char_quality[0] == '0') {
297
0
          quality = 0;
298
0
          } else if (char_quality[0] == '0') {
299
0
          quality = (char_quality[1] & 0x0f);
300
0
          } else  {
301
0
          quality = 99;
302
0
          }
303
304
0
                if (leapchar == 'I' ) leapchar = '+';
305
0
                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
0
                       }
320
    
321
0
                    } 
322
0
                    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
0
           if (sscanf(pp->a_lastcode, 
363
0
                           "%*4c %2c %4d%*c%3d%*4c %2d%c%2d:%2d%c%*2c",
364
0
               char_quality, &pp->year, &pp->day, 
365
0
                           &pp->hour, &syncchar, &pp->minute, &pp->second, 
366
0
                           &leapchar) == 8) { 
367
    
368
0
         if (char_quality[0] == 'L') {
369
0
        quality = 0;
370
0
         } else if (char_quality[0] == '0') {
371
0
        quality = (char_quality[1] & 0x0f);
372
0
         } else  {
373
0
        quality = 99;
374
0
               }
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
0
            }
389
0
                    }
390
0
        break;
391
0
    }
392
393
0
    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
0
    if (sscanf(pp->a_lastcode, "%c%1d%c%4d%3d%*c%2d:%2d:%2d.%2ld%c",
424
0
                 &syncchar, &quality, &modechar, &pp->year, &pp->day,
425
0
                 &pp->hour, &pp->minute, &pp->second,
426
0
      &pp->nsec, &leapchar) == 10) {
427
0
    pp->nsec *= 10000000; /* M320 returns 10's of msecs */
428
0
    if (leapchar == 'I' ) leapchar = '+';
429
0
    if (leapchar == 'D' ) leapchar = '-';
430
0
    if (syncchar != '?' ) syncchar = ':';
431
432
0
    break;
433
0
    }
434
435
0
    default:
436
0
    refclock_report(peer, CEVNT_BADREPLY);
437
0
    return;
438
0
  }
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
0
  if (pp->lencode == LEN33X) {
466
0
    switch (quality) {
467
0
      case 0 :
468
0
        pp->disp=.002;
469
0
        break;
470
0
      case 1 :
471
0
        pp->disp=.02;
472
0
        break;
473
0
      case 2 :
474
0
        pp->disp=.04;
475
0
        break;
476
0
      case 3 :
477
0
        pp->disp=.08;
478
0
        break;
479
0
      default:
480
0
        pp->disp=MAXDISPERSE;
481
0
        break;
482
0
    }
483
0
  } else {
484
0
    switch (quality) {
485
0
      case 5 :
486
0
        pp->disp=.002;
487
0
        break;
488
0
      case 4 :
489
0
        pp->disp=.02;
490
0
        break;
491
0
      case 3 :
492
0
        pp->disp=.04;
493
0
        break;
494
0
      case 2 :
495
0
        pp->disp=.08;
496
0
        break;
497
0
      case 1 :
498
0
        pp->disp=.16;
499
0
        break;
500
0
      default:
501
0
        pp->disp=MAXDISPERSE;
502
0
        break;
503
0
    }
504
505
0
  }
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
0
  if (syncchar != ':')
514
0
    pp->leap = LEAP_NOTINSYNC;
515
0
  else if (leapchar == '+')
516
0
    pp->leap = LEAP_ADDSECOND;
517
0
  else if (leapchar == '-')
518
0
    pp->leap = LEAP_DELSECOND;
519
0
  else
520
0
    pp->leap = LEAP_NOWARNING;
521
522
  /*
523
   * Process the new sample in the median filter and determine the
524
   * timecode timestamp.
525
   */
526
0
  if (!refclock_process(pp)) {
527
0
    refclock_report(peer, CEVNT_BADTIME);
528
0
  }
529
530
0
}
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
0
{
542
0
        struct refclockproc *pp;
543
0
        char pollchar;
544
545
0
        pp = peer->procptr;
546
0
        pollchar = 'T';
547
0
  if (pp->sloppyclockflag & CLK_FLAG1) {
548
0
          if (write(pp->io.fd, &pollchar, 1) != 1)
549
0
                  refclock_report(peer, CEVNT_FAULT);
550
0
          else
551
0
                    pp->polls++;
552
0
  }
553
0
  else
554
0
                    pp->polls++;
555
556
0
        if (pp->coderecv == pp->codeproc) {
557
0
                refclock_report(peer, CEVNT_TIMEOUT);
558
0
                return;
559
0
        }
560
0
        pp->lastref = pp->lastrec;
561
0
  refclock_receive(peer);
562
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
563
564
0
}
565
566
#else
567
int refclock_ulink_bs;
568
#endif /* REFCLOCK */