Coverage Report

Created: 2023-05-19 06:16

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