Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/ntpd/refclock_acts.c
Line
Count
Source
1
/*
2
 * refclock_acts - clock driver for the NIST/USNO/PTB/NPL Computer Time
3
 *  Services
4
 */
5
#ifdef HAVE_CONFIG_H
6
#include <config.h>
7
#endif
8
9
#if defined(REFCLOCK) && defined(CLOCK_ACTS)
10
11
#include "ntpd.h"
12
#include "ntp_io.h"
13
#include "ntp_unixtime.h"
14
#include "ntp_refclock.h"
15
#include "ntp_stdlib.h"
16
#include "ntp_control.h"
17
18
#include <stdio.h>
19
#include <ctype.h>
20
#ifdef HAVE_SYS_IOCTL_H
21
# include <sys/ioctl.h>
22
#endif /* HAVE_SYS_IOCTL_H */
23
24
/*
25
 * This driver supports the US (NIST, USNO) and European (PTB, NPL,
26
 * etc.) modem time services, as well as Spectracom GPS and WWVB
27
 * receivers connected via a modem. The driver periodically dials a
28
 * number from a telephone list, receives the timecode data and
29
 * calculates the local clock correction. It is designed primarily for
30
 * use as backup when neither a radio clock nor connectivity to Internet
31
 * time servers is available.
32
 *
33
 * This driver requires a modem with a Hayes-compatible command set and
34
 * control over the modem data terminal ready (DTR) control line. The
35
 * modem setup string is hard-coded in the driver and may require
36
 * changes for nonstandard modems or special circumstances.
37
 *
38
 * When enabled, the calling program dials the first number in the
39
 * phones file. If that call fails, it dials the second number and
40
 * so on. The phone number is specified by the Hayes ATDT prefix
41
 * followed by the number itself, including the long-distance prefix
42
 * and delay code, if necessary. The calling program is enabled
43
 * when (a) fudge flag1 is set by ntpdc, (b) at each poll interval
44
 * when no other synchronization sources are present, and (c) at each
45
 * poll interval whether or not other synchronization sources are 
46
 * present. The calling program disconnects if (a) the called party
47
 * is busy or does not answer, (b) the called party disconnects
48
 * before a sufficient nuimber of timecodes have been received. 
49
 *
50
 * The driver is transparent to each of the modem time services and
51
 * Spectracom radios. It selects the parsing algorithm depending on the
52
 * message length. There is some hazard should the message be corrupted.
53
 * However, the data format is checked carefully and only if all checks
54
 * succeed is the message accepted. Corrupted lines are discarded
55
 * without complaint.
56
 *
57
 * Fudge controls
58
 *
59
 * flag1  force a call in manual mode
60
 * flag2  enable port locking (not verified)
61
 * flag3  not used
62
 * flag4  not used
63
 *
64
 * time1  offset adjustment (s)
65
 *
66
 * Ordinarily, the serial port is connected to a modem and the phones
67
 * list is defined. If no phones list is defined, the port can be 
68
 * connected directly to a device or another computer. In this case the
69
 * driver will send a single character 'T' at each poll event. If
70
 * fudge flag2 is enabled, port locking allows the modem to be shared
71
 * when not in use by this driver.
72
 */
73
/*
74
 * National Institute of Science and Technology (NIST)
75
 *
76
 * Phone: (303) 494-4774 (Boulder, CO); (808) 335-4721 (Hawaii)
77
 *
78
 * Data Format
79
 *
80
 * National Institute of Standards and Technology
81
 * Telephone Time Service, Generator 3B
82
 * Enter question mark "?" for HELP
83
 *                         D  L D
84
 *  MJD  YR MO DA H  M  S  ST S UT1 msADV        <OTM>
85
 * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *<CR><LF>
86
 * ...
87
 *
88
 * MJD, DST, DUT1 and UTC are not used by this driver. The "*" or "#" is
89
 * the on-time markers echoed by the driver and used by NIST to measure
90
 * and correct for the propagation delay. Note: the ACTS timecode has
91
 * recently been changed to eliminate the * on-time indicator. The
92
 * reason for this and the long term implications are not clear.
93
 *
94
 * US Naval Observatory (USNO)
95
 *
96
 * Phone: (202) 762-1594 (Washington, DC); (719) 567-6742 (Boulder, CO)
97
 *
98
 * Data Format (two lines, repeating at one-second intervals)
99
 *
100
 * jjjjj nnn hhmmss UTC<CR><LF>
101
 * *<CR><LF>
102
 *
103
 * jjjjj  modified Julian day number (not used)
104
 * nnn    day of year
105
 * hhmmss second of day
106
 * *    on-time marker for previous timecode
107
 * ...
108
 *
109
 * USNO does not correct for the propagation delay. A fudge time1 of
110
 * about .06 s is advisable.
111
 *
112
 * European Services (PTB, NPL, etc.)
113
 *
114
 * PTB: +49 531 512038 (Germany)
115
 * NPL: 0906 851 6333 (UK only)
116
 *
117
 * Data format (see the documentation for phone numbers and formats.)
118
 *
119
 * 1995-01-23 20:58:51 MEZ  10402303260219950123195849740+40000500<CR><LF>
120
 *
121
 * Spectracom GPS and WWVB Receivers
122
 *
123
 * If a modem is connected to a Spectracom receiver, this driver will
124
 * call it up and retrieve the time in one of two formats. As this
125
 * driver does not send anything, the radio will have to either be
126
 * configured in continuous mode or be polled by another local driver.
127
 */
128
/*
129
 * Interface definitions
130
 */
131
#define DEVICE    "/dev/acts%d" /* device name and unit */
132
0
#define SPEED232  B19200  /* uart speed (19200 bps) */
133
0
#define PRECISION (-10)  /* precision assumed (about 1 ms) */
134
#define LOCKFILE  "/var/spool/lock/LCK..cua%d"
135
0
#define DESCRIPTION "Automated Computer Time Service" /* WRU */
136
0
#define REFID   "NONE"  /* default reference ID */
137
#define MSGCNT    20  /* max message count */
138
#define MAXPHONE  10  /* max number of phone numbers */
139
140
/*
141
 * Calling program modes (mode)
142
 */
143
0
#define MODE_BACKUP 0  /* backup mode */
144
0
#define MODE_AUTO 1  /* automatic mode */
145
0
#define MODE_MANUAL 2  /* manual mode */
146
147
/*
148
 * Service identifiers (message length)
149
 */
150
0
#define REFACTS   "NIST"  /* NIST reference ID */
151
0
#define LENACTS   50  /* NIST format A */
152
0
#define REFUSNO   "USNO"  /* USNO reference ID */
153
0
#define LENUSNO   20  /* USNO */
154
0
#define REFPTB    "PTB\0"  /* PTB/NPL reference ID */
155
0
#define LENPTB    78  /* PTB/NPL format */
156
0
#define REFWWVB   "WWVB"  /* WWVB reference ID */
157
0
#define LENWWVB0  22  /* WWVB format 0 */
158
0
#define LENWWVB2  24  /* WWVB format 2 */
159
0
#define LF    0x0a  /* ASCII LF */
160
161
/*
162
 * Modem setup strings. These may have to be changed for
163
 * some modems.
164
 *
165
 * AT command prefix
166
 * B1 US answer tone
167
 * &C0  disable carrier detect
168
 * &D2  hang up and return to command mode on DTR transition
169
 * E0 modem command echo disabled
170
 * L1 set modem speaker volume to low level
171
 * M1 speaker enabled until carrier detect
172
 * Q0 return result codes
173
 * V1 return result codes as English words
174
 * Y1 enable long-space disconnect
175
 */
176
const char def_modem_setup[] = "ATB1&C0&D2E0L1M1Q0V1Y1";
177
const char *modem_setup = def_modem_setup; 
178
179
/*
180
 * Timeouts (all in seconds)
181
 */
182
0
#define SETUP   3  /* setup timeout */
183
0
#define REDIAL    30  /* redial timeout */
184
0
#define ANSWER    60  /* answer timeout */
185
0
#define TIMECODE  60  /* message timeout */
186
0
#define MAXCODE   20  /* max timecodes */
187
188
/*
189
 * State machine codes
190
 */
191
typedef enum {
192
  S_IDLE,     /* wait for poll */
193
  S_SETUP,    /* send modem setup */
194
  S_CONNECT,    /* wait for answer */
195
  S_MSG     /* wait for timecode */
196
} teModemState;
197
198
/*
199
 * Unit control structure
200
 */
201
struct actsunit {
202
  int unit;   /* unit number */
203
  int state;    /* the first one was Delaware */
204
  int timer;    /* timeout counter */
205
  int retry;    /* retry index */
206
  int msgcnt;   /* count of messages received */
207
  l_fp  tstamp;   /* on-time timestamp */
208
  char  *bufptr;  /* next incoming char stored here */
209
  char  buf[BMAX];  /* bufptr roams within buf[] */
210
};
211
212
/*
213
 * Function prototypes
214
 */
215
static  int acts_start  (int, struct peer *);
216
static  void  acts_shutdown (int, struct peer *);
217
static  void  acts_receive  (struct recvbuf *);
218
static  void  acts_message  (struct peer *, const char *);
219
static  void  acts_timecode (struct peer *, const char *);
220
static  void  acts_poll (int, struct peer *);
221
static  void  acts_timeout  (struct peer *, teModemState);
222
static  void  acts_timer  (int, struct peer *);
223
static  void  acts_close  (struct peer *);
224
225
/*
226
 * Transfer vector (conditional structure name)
227
 */
228
struct refclock refclock_acts = {
229
  acts_start,   /* start up driver */
230
  acts_shutdown,    /* shut down driver */
231
  acts_poll,    /* transmit poll message */
232
  noentry,    /* not used */
233
  noentry,    /* not used */
234
  noentry,    /* not used */
235
  acts_timer    /* housekeeping timer */
236
};
237
238
/*
239
 * Initialize data for processing
240
 */
241
static int
242
acts_start(
243
  int unit,
244
  struct peer *peer
245
  )
246
0
{
247
0
  struct actsunit *up;
248
0
  struct refclockproc *pp;
249
0
  const char *setup;
250
251
  /*
252
   * Allocate and initialize unit structure
253
   */
254
0
  up = emalloc_zero(sizeof(struct actsunit));
255
0
  up->unit = unit;
256
0
  pp = peer->procptr;
257
0
  pp->unitptr = up;
258
0
  pp->io.clock_recv = acts_receive;
259
0
  pp->io.srcclock = peer;
260
0
  pp->io.datalen = 0;
261
0
  pp->io.fd = -1;
262
263
  /*
264
   * Initialize miscellaneous variables
265
   */
266
0
  peer->precision = PRECISION;
267
0
  pp->clockdesc = DESCRIPTION;
268
0
  memcpy(&pp->refid, REFID, 4);
269
0
  peer->sstclktype = CTL_SST_TS_TELEPHONE;
270
0
  up->bufptr = up->buf;
271
0
  if (def_modem_setup == modem_setup) {
272
0
    setup = get_ext_sys_var("modemsetup");
273
0
    if (setup != NULL)
274
0
      modem_setup = estrdup(setup);
275
0
  }
276
277
0
  return (1);
278
0
}
279
280
281
/*
282
 * acts_shutdown - shut down the clock
283
 */
284
static void
285
acts_shutdown(
286
  int unit,
287
  struct peer *peer
288
  )
289
0
{
290
0
  struct actsunit *up;
291
0
  struct refclockproc *pp;
292
293
  /*
294
   * Warning: do this only when a call is not in progress.
295
   */
296
0
  pp = peer->procptr;
297
0
  up = pp->unitptr;
298
0
  acts_close(peer);
299
0
  free(up);
300
0
}
301
302
303
/*
304
 * acts_receive - receive data from the serial interface
305
 */
306
static void
307
acts_receive(
308
  struct recvbuf *rbufp
309
  )
310
0
{
311
0
  struct actsunit *up;
312
0
  struct refclockproc *pp;
313
0
  struct peer *peer;
314
0
  char  tbuf[sizeof(up->buf)];
315
0
  char *  tptr;
316
0
  int octets;
317
318
  /*
319
   * Initialize pointers and read the timecode and timestamp. Note
320
   * we are in raw mode and victim of whatever the terminal
321
   * interface kicks up; so, we have to reassemble messages from
322
   * arbitrary fragments. Capture the timecode at the beginning of
323
   * the message and at the '*' and '#' on-time characters.
324
   */
325
0
  peer = rbufp->recv_peer;
326
0
  pp = peer->procptr;
327
0
  up = pp->unitptr;
328
0
  octets = sizeof(up->buf) - (up->bufptr - up->buf);
329
0
  refclock_gtraw(rbufp, tbuf, octets, &pp->lastrec);
330
0
  for (tptr = tbuf; *tptr != '\0'; tptr++) {
331
0
    if (*tptr == LF) {
332
0
      if (up->bufptr == up->buf) {
333
0
        up->tstamp = pp->lastrec;
334
0
        continue;
335
0
      } else {
336
0
        *up->bufptr = '\0';
337
0
        up->bufptr = up->buf;
338
0
        acts_message(peer, up->buf);
339
0
      }
340
0
    } else if (!iscntrl((unsigned char)*tptr)) {
341
0
      *up->bufptr++ = *tptr;
342
0
      if (*tptr == '*' || *tptr == '#') {
343
0
        up->tstamp = pp->lastrec;
344
0
        refclock_write(peer, tptr, 1, "data");
345
0
      }
346
0
    }
347
0
  }
348
0
}
349
350
351
/*
352
 * acts_message - process message
353
 */
354
void
355
acts_message(
356
  struct peer *peer,
357
  const char *msg
358
  )
359
0
{
360
0
  struct actsunit *up;
361
0
  struct refclockproc *pp;
362
0
  char  tbuf[BMAX];
363
0
  int   dtr = TIOCM_DTR;
364
365
0
  DPRINTF(1, ("acts: %d %s\n", (int)strlen(msg), msg));
366
367
  /*
368
   * What to do depends on the state and the first token in the
369
   * message.
370
   */
371
0
  pp = peer->procptr;
372
0
  up = pp->unitptr;
373
374
  /*
375
   * Extract the first token in the line.
376
   */
377
0
  strlcpy(tbuf, msg, sizeof(tbuf));
378
0
  strtok(tbuf, " ");
379
0
  switch (up->state) {
380
381
  /*
382
   * We are waiting for the OK response to the modem setup
383
   * command. When this happens, dial the number followed.
384
   * If anything other than OK is received, just ignore it
385
   * and wait for timeoue.
386
   */
387
0
  case S_SETUP:
388
0
    if (strcmp(tbuf, "OK") != 0) {
389
      /*
390
       * We disable echo with MODEM_SETUP's E0 but
391
       * if the modem was previously E1, we will
392
       * see MODEM_SETUP echoed before the OK/ERROR.
393
       * Ignore it.
394
       */
395
0
      if (!strcmp(tbuf, modem_setup))
396
0
        return;
397
0
      break;
398
0
    }
399
400
0
    mprintf_event(PEVNT_CLOCK, peer, "DIAL #%d %s",
401
0
            up->retry, sys_phone[up->retry]);
402
0
    if (ioctl(pp->io.fd, TIOCMBIS, &dtr) < 0)
403
0
      msyslog(LOG_ERR, "acts: ioctl(TIOCMBIS) failed: %m");
404
0
    refclock_write(peer, sys_phone[up->retry],
405
0
             strlen(sys_phone[up->retry]),
406
0
             "DIAL");
407
0
    refclock_write(peer, "\r", 1, "CR");
408
0
    up->retry++;
409
0
    up->state = S_CONNECT;
410
0
    up->timer = ANSWER;
411
0
    return;
412
413
  /*
414
   * We are waiting for the CONNECT response to the dial
415
   * command. When this happens, listen for timecodes. If
416
   * somthing other than CONNECT is received, like BUSY
417
   * or NO CARRIER, abort the call.
418
   */
419
0
  case S_CONNECT:
420
0
    if (strcmp(tbuf, "CONNECT") != 0)
421
0
      break;
422
423
0
    report_event(PEVNT_CLOCK, peer, msg);
424
0
    up->state = S_MSG;
425
0
    up->timer = TIMECODE;
426
0
    return;
427
428
  /*
429
   * We are waiting for a timecode response. Pass it to
430
   * the parser. If NO CARRIER is received, save the
431
   * messages and abort the call.
432
   */
433
0
  case S_MSG:
434
0
    if (strcmp(tbuf, "NO") == 0)
435
0
      report_event(PEVNT_CLOCK, peer, msg);
436
0
    if (up->msgcnt < MAXCODE)
437
0
      acts_timecode(peer, msg);
438
0
    else
439
0
      acts_timeout(peer, S_MSG);
440
0
    return;
441
0
  }
442
443
  /*
444
   * Other response. Tell us about it.
445
   */
446
0
  report_event(PEVNT_CLOCK, peer, msg);
447
0
  acts_close(peer);
448
0
}
449
450
451
/*
452
 * acts_timeout - called on timeout
453
 */
454
static void
455
acts_timeout(
456
  struct peer *peer,
457
  teModemState  dstate
458
  )
459
0
{
460
0
  struct actsunit *up;
461
0
  struct refclockproc *pp;
462
0
  int fd;
463
0
  char  device[20];
464
0
  char  lockfile[128], pidbuf[8];
465
466
  /*
467
   * The state machine is driven by messages from the modem,
468
   * when first started and at timeout.
469
   */
470
0
  pp = peer->procptr;
471
0
  up = pp->unitptr;
472
0
  switch (dstate) {
473
474
  /*
475
   * System poll event. Lock the modem port, open the device
476
   * and send the setup command.
477
   */
478
0
  case S_IDLE:
479
0
    if (-1 != pp->io.fd)
480
0
      return;   /* port is already open */
481
482
    /*
483
     * Lock the modem port. If busy, retry later. Note: if
484
     * something fails between here and the close, the lock
485
     * file may not be removed.
486
     */
487
0
    if (pp->sloppyclockflag & CLK_FLAG2) {
488
0
      snprintf(lockfile, sizeof(lockfile), LOCKFILE,
489
0
          up->unit);
490
0
      fd = open(lockfile, O_WRONLY | O_CREAT | O_EXCL,
491
0
          0644);
492
0
      if (fd < 0) {
493
0
        report_event(PEVNT_CLOCK, peer, "acts: port busy");
494
0
        return;
495
0
      }
496
0
      snprintf(pidbuf, sizeof(pidbuf), "%d\n",
497
0
          (u_int)getpid());
498
0
      if (write(fd, pidbuf, strlen(pidbuf)) < 0)
499
0
        msyslog(LOG_ERR, "acts: write lock fails %m");
500
0
      close(fd);
501
0
    }
502
503
    /*
504
     * Open the device in raw mode and link the I/O.
505
     */
506
0
    snprintf(device, sizeof(device), DEVICE,
507
0
        up->unit);
508
0
    fd = refclock_open(&peer->srcadr, device, SPEED232, LDISC_ACTS |
509
0
        LDISC_RAW | LDISC_REMOTE);
510
0
    if (fd < 0) {
511
0
      msyslog(LOG_ERR, "acts: open fails %m");
512
0
      return;
513
0
    }
514
0
    pp->io.fd = fd;
515
0
    if (!io_addclock(&pp->io)) {
516
0
      msyslog(LOG_ERR, "acts: addclock fails");
517
0
      close(fd);
518
0
      pp->io.fd = -1;
519
0
      return;
520
0
    }
521
0
    up->msgcnt = 0;
522
0
    up->bufptr = up->buf;
523
524
    /*
525
     * If the port is directly connected to the device, skip
526
     * the modem business and send 'T' for Spectrabum.
527
     */
528
0
    if (sys_phone[up->retry] == NULL) {
529
0
      refclock_write(peer, "T", 1, "T");
530
0
      up->state = S_MSG;
531
0
      up->timer = TIMECODE;
532
0
      return;
533
0
    }
534
535
    /*
536
     * Initialize the modem. This works with Hayes-
537
     * compatible modems.
538
     */
539
0
    mprintf_event(PEVNT_CLOCK, peer, "SETUP %s",
540
0
            modem_setup);
541
0
    refclock_write(peer, modem_setup, strlen(modem_setup),
542
0
             "SETUP");
543
0
    refclock_write(peer, "\r", 1, "CR");
544
0
    up->state = S_SETUP;
545
0
    up->timer = SETUP;
546
0
    return;
547
548
  /*
549
   * In SETUP state the modem did not respond OK to setup string.
550
   */
551
0
  case S_SETUP:
552
0
    report_event(PEVNT_CLOCK, peer, "no modem");
553
0
    break;
554
555
  /*
556
   * In CONNECT state the call did not complete. Abort the call.
557
   */
558
0
  case S_CONNECT:
559
0
    report_event(PEVNT_CLOCK, peer, "no answer");
560
0
    break;
561
562
  /*
563
   * In MSG states no further timecodes are expected. If any
564
   * timecodes have arrived, update the clock. In any case,
565
   * terminate the call.
566
   */
567
0
  case S_MSG:
568
0
    if (up->msgcnt == 0) {
569
0
      report_event(PEVNT_CLOCK, peer, "no timecodes");
570
0
    } else {
571
0
      pp->lastref = pp->lastrec;
572
0
      record_clock_stats(&peer->srcadr, pp->a_lastcode);
573
0
      refclock_receive(peer);
574
0
    }
575
0
    break;
576
0
  }
577
0
  acts_close(peer);
578
0
}
579
580
581
/*
582
 * acts_close - close and prepare for next call.
583
 *
584
 * In ClOSE state no further protocol actions are required
585
 * other than to close and release the device and prepare to
586
 * dial the next number if necessary.
587
 */
588
void
589
acts_close(
590
  struct peer *peer
591
  )
592
0
{
593
0
  struct actsunit *up;
594
0
  struct refclockproc *pp;
595
0
  char  lockfile[128];
596
0
  int dtr;
597
598
0
  pp = peer->procptr;
599
0
  up = pp->unitptr;
600
0
  if (pp->io.fd != -1) {
601
0
    report_event(PEVNT_CLOCK, peer, "close");
602
0
    dtr = TIOCM_DTR;
603
0
    if (ioctl(pp->io.fd, TIOCMBIC, &dtr) < 0)
604
0
      msyslog(LOG_ERR, "acts: ioctl(TIOCMBIC) failed: %m");
605
0
    io_closeclock(&pp->io);
606
0
    pp->io.fd = -1;
607
0
  }
608
0
  if (pp->sloppyclockflag & CLK_FLAG2) {
609
0
    snprintf(lockfile, sizeof(lockfile),
610
0
        LOCKFILE, up->unit);
611
0
    unlink(lockfile);
612
0
  }
613
0
  if (up->msgcnt == 0 && up->retry > 0) {
614
0
    if (sys_phone[up->retry] != NULL) {
615
0
      up->state = S_IDLE;
616
0
      up->timer = REDIAL;
617
0
      return;
618
0
    }
619
0
  }
620
0
  up->state = S_IDLE;
621
0
  up->timer = 0;
622
0
}
623
624
625
/*
626
 * acts_poll - called by the transmit routine
627
 */
628
static void
629
acts_poll(
630
  int unit,
631
  struct peer *peer
632
  )
633
0
{
634
0
  struct actsunit *up;
635
0
  struct refclockproc *pp;
636
637
  /*
638
   * This routine is called at every system poll. All it does is
639
   * set flag1 under certain conditions. The real work is done by
640
   * the timeout routine and state machine.
641
   */
642
0
  pp = peer->procptr;
643
0
  up = pp->unitptr;
644
0
  switch (peer->ttl) {
645
646
  /*
647
   * In manual mode the calling program is activated by the ntpdc
648
   * program using the enable flag (fudge flag1), either manually
649
   * or by a cron job.
650
   */
651
0
  case MODE_MANUAL:
652
0
    return;
653
654
  /*
655
   * In automatic mode the calling program runs continuously at
656
   * intervals determined by the poll event or specified timeout.
657
   */
658
0
  case MODE_AUTO:
659
0
    break;
660
661
  /*
662
   * In backup mode the calling program runs continuously as long
663
   * as either no peers are available or this peer is selected.
664
   */
665
0
  case MODE_BACKUP:
666
0
    if (!(sys_peer == NULL || sys_peer == peer))
667
0
      return;
668
669
0
    break;
670
0
  }
671
0
  pp->polls++;
672
0
  if (S_IDLE == up->state) {
673
0
    up->retry = 0;
674
0
    acts_timeout(peer, S_IDLE);
675
0
  }
676
0
}
677
678
679
/*
680
 * acts_timer - called at one-second intervals
681
 */
682
static void
683
acts_timer(
684
  int unit,
685
  struct peer *peer
686
  )
687
0
{
688
0
  struct actsunit *up;
689
0
  struct refclockproc *pp;
690
691
  /*
692
   * This routine implments a timeout which runs for a programmed
693
   * interval. The counter is initialized by the state machine and
694
   * counts down to zero. Upon reaching zero, the state machine is
695
   * called. If flag1 is set while timer is zero, force a call.
696
   */
697
0
  pp = peer->procptr;
698
0
  up = pp->unitptr;
699
0
  if (up->timer == 0) {
700
0
    if (pp->sloppyclockflag & CLK_FLAG1) {
701
0
      pp->sloppyclockflag &= ~CLK_FLAG1;
702
0
      acts_timeout(peer, S_IDLE);
703
0
    }
704
0
  } else {
705
0
    up->timer--;
706
0
    if (up->timer == 0)
707
0
      acts_timeout(peer, up->state);
708
0
  }
709
0
}
710
711
/*
712
 * acts_timecode - identify the service and parse the timecode message
713
 */
714
void
715
acts_timecode(
716
  struct peer * peer, /* peer structure pointer */
717
  const char *  str /* timecode string */
718
  )
719
0
{
720
0
  struct actsunit *up;
721
0
  struct refclockproc *pp;
722
0
  int day;    /* day of the month */
723
0
  int month;    /* month of the year */
724
0
  u_long  mjd;    /* Modified Julian Day */
725
0
  double  dut1;   /* DUT adjustment */
726
727
0
  u_int dst;    /* ACTS daylight/standard time */
728
0
  u_int leap;   /* ACTS leap indicator */
729
0
  double  msADV;    /* ACTS transmit advance (ms) */
730
0
  char  utc[10];  /* ACTS timescale */
731
0
  char  flag;   /* ACTS on-time character (* or #) */
732
733
0
  char  synchar;  /* WWVB synchronized indicator */
734
0
  char  qualchar; /* WWVB quality indicator */
735
0
  char  leapchar; /* WWVB leap indicator */
736
0
  char  dstchar;  /* WWVB daylight/savings indicator */
737
0
  int tz;   /* WWVB timezone */
738
739
0
  int leapmonth;  /* PTB/NPL month of leap */
740
0
  char  leapdir;  /* PTB/NPL leap direction */
741
742
  /*
743
   * The parser selects the modem format based on the message
744
   * length. Since the data are checked carefully, occasional
745
   * errors due noise are forgivable.
746
   */
747
0
  pp = peer->procptr;
748
0
  up = pp->unitptr;
749
0
  pp->nsec = 0;
750
0
  switch (strlen(str)) {
751
752
  /*
753
   * For USNO format on-time character '*', which is on a line by
754
   * itself. Be sure a timecode has been received.
755
   */
756
0
  case 1:
757
0
    if (*str == '*' && up->msgcnt > 0) 
758
0
      break;
759
760
0
    return;
761
  
762
  /*
763
   * ACTS format A: "jjjjj yy-mm-dd hh:mm:ss ds l uuu aaaaa
764
   * UTC(NIST) *".
765
   */
766
0
  case LENACTS:
767
0
    if (sscanf(str,
768
0
        "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %9s %c",
769
0
        &mjd, &pp->year, &month, &day, &pp->hour,
770
0
        &pp->minute, &pp->second, &dst, &leap, &dut1,
771
0
        &msADV, utc, &flag) != 13) {
772
0
      refclock_report(peer, CEVNT_BADREPLY);
773
0
      return;
774
0
    }
775
0
    pp->day = ymd2yd(pp->year, month, day);
776
0
    pp->leap = LEAP_NOWARNING;
777
0
    if (leap == 1)
778
0
      pp->leap = LEAP_ADDSECOND;
779
0
    else if (leap == 2)
780
0
      pp->leap = LEAP_DELSECOND;
781
0
    memcpy(&pp->refid, REFACTS, 4);
782
0
    up->msgcnt++;
783
0
    if (flag != '#' && up->msgcnt < 10)
784
0
      return;
785
786
0
    break;
787
788
  /*
789
   * USNO format: "jjjjj nnn hhmmss UTC"
790
   */
791
0
  case LENUSNO:
792
0
    if (sscanf(str, "%5ld %3d %2d%2d%2d %3s",
793
0
        &mjd, &pp->day, &pp->hour, &pp->minute,
794
0
        &pp->second, utc) != 6) {
795
0
      refclock_report(peer, CEVNT_BADREPLY);
796
0
      return;
797
0
    }
798
799
    /*
800
     * Wait for the on-time character, which follows in a
801
     * separate message. There is no provision for leap
802
     * warning.
803
     */
804
0
    pp->leap = LEAP_NOWARNING;
805
0
    memcpy(&pp->refid, REFUSNO, 4);
806
0
    up->msgcnt++;
807
0
    break;
808
809
  /*
810
   * PTB/NPL format: "yyyy-mm-dd hh:mm:ss MEZ" 
811
   */
812
0
  case LENPTB:
813
0
    if (sscanf(str,
814
0
        "%*4d-%*2d-%*2d %*2d:%*2d:%2d %*5c%*12c%4d%2d%2d%2d%2d%5ld%2lf%c%2d%3lf%*15c%c",
815
0
        &pp->second, &pp->year, &month, &day, &pp->hour,
816
0
        &pp->minute, &mjd, &dut1, &leapdir, &leapmonth,
817
0
        &msADV, &flag) != 12) {
818
0
      refclock_report(peer, CEVNT_BADREPLY);
819
0
      return;
820
0
    }
821
0
    pp->leap = LEAP_NOWARNING;
822
0
    if (leapmonth == month) {
823
0
      if (leapdir == '+')
824
0
        pp->leap = LEAP_ADDSECOND;
825
0
      else if (leapdir == '-')
826
0
        pp->leap = LEAP_DELSECOND;
827
0
    }
828
0
    pp->day = ymd2yd(pp->year, month, day);
829
0
    memcpy(&pp->refid, REFPTB, 4);
830
0
    up->msgcnt++;
831
0
    break;
832
833
834
  /*
835
   * WWVB format 0: "I  ddd hh:mm:ss DTZ=nn"
836
   */
837
0
  case LENWWVB0:
838
0
    if (sscanf(str, "%c %3d %2d:%2d:%2d %cTZ=%2d",
839
0
        &synchar, &pp->day, &pp->hour, &pp->minute,
840
0
        &pp->second, &dstchar, &tz) != 7) {
841
0
      refclock_report(peer, CEVNT_BADREPLY);
842
0
      return;
843
0
    }
844
0
    pp->leap = LEAP_NOWARNING;
845
0
    if (synchar != ' ')
846
0
      pp->leap = LEAP_NOTINSYNC;
847
0
    memcpy(&pp->refid, REFWWVB, 4);
848
0
    up->msgcnt++;
849
0
    break;
850
851
  /*
852
   * WWVB format 2: "IQyy ddd hh:mm:ss.mmm LD"
853
   */
854
0
  case LENWWVB2:
855
0
    if (sscanf(str, "%c%c%2d %3d %2d:%2d:%2d.%3ld%c%c%c",
856
0
        &synchar, &qualchar, &pp->year, &pp->day,
857
0
        &pp->hour, &pp->minute, &pp->second, &pp->nsec,
858
0
        &dstchar, &leapchar, &dstchar) != 11) {
859
0
      refclock_report(peer, CEVNT_BADREPLY);
860
0
      return;
861
0
    }
862
0
    pp->nsec *= 1000000;
863
0
    pp->leap = LEAP_NOWARNING;
864
0
    if (synchar != ' ')
865
0
      pp->leap = LEAP_NOTINSYNC;
866
0
    else if (leapchar == 'L')
867
0
      pp->leap = LEAP_ADDSECOND;
868
0
    memcpy(&pp->refid, REFWWVB, 4);
869
0
    up->msgcnt++;
870
0
    break;
871
872
  /*
873
   * None of the above. Just forget about it and wait for the next
874
   * message or timeout.
875
   */
876
0
  default:
877
0
    return;
878
0
  }
879
880
  /*
881
   * We have a valid timecode. The fudge time1 value is added to
882
   * each sample by the main line routines. Note that in current
883
   * telephone networks the propatation time can be different for
884
   * each call and can reach 200 ms for some calls.
885
   */
886
0
  peer->refid = pp->refid;
887
0
  pp->lastrec = up->tstamp;
888
0
  if (up->msgcnt == 0)
889
0
    return;
890
891
0
  strlcpy(pp->a_lastcode, str, sizeof(pp->a_lastcode));
892
0
  pp->lencode = strlen(pp->a_lastcode);
893
0
  if (!refclock_process(pp)) {
894
0
    refclock_report(peer, CEVNT_BADTIME);
895
0
    return;
896
0
  }
897
0
  pp->lastref = pp->lastrec;
898
0
}
899
#else
900
NONEMPTY_TRANSLATION_UNIT
901
#endif /* REFCLOCK */