Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/refclock_pst.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
3
 */
4
5
#ifdef HAVE_CONFIG_H
6
#include <config.h>
7
#endif
8
9
#if defined(REFCLOCK) && defined(CLOCK_PST)
10
11
#include "ntpd.h"
12
#include "ntp_io.h"
13
#include "ntp_refclock.h"
14
#include "ntp_stdlib.h"
15
16
#include <stdio.h>
17
#include <ctype.h>
18
19
/*
20
 * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
21
 * Receivers. No specific claim of accuracy is made for these receiver,
22
 * but actual experience suggests that 10 ms would be a conservative
23
 * assumption.
24
 * 
25
 * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
26
 * of-year format and UTC time zone. Automatic correction for DST should
27
 * be disabled. It is very important that the year be set correctly in
28
 * the DIPswitches; otherwise, the day of year will be incorrect after
29
 * 28 April of a normal or leap year. The propagation delay DIPswitches
30
 * should be set according to the distance from the transmitter for both
31
 * WWV and WWVH, as described in the instructions. While the delay can
32
 * be set only to within 11 ms, the fudge time1 parameter can be used
33
 * for vernier corrections.
34
 *
35
 * Using the poll sequence QTQDQM, the response timecode is in three
36
 * sections totalling 50 ASCII printing characters, as concatenated by
37
 * the driver, in the following format:
38
 *
39
 * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
40
 *
41
 *  on-time = first <cr>
42
 *  hh:mm:ss.fff = hours, minutes, seconds, milliseconds
43
 *  a = AM/PM indicator (' ' for 24-hour mode)
44
 *  yy = year (from internal switches)
45
 *  dd/mm/ddd = day of month, month, day of year
46
 *  s = daylight-saving indicator (' ' for 24-hour mode)
47
 *  f = frequency enable (O = all frequencies enabled)
48
 *  r = baud rate (3 = 1200, 6 = 9600)
49
 *  d = features indicator (@ = month/day display enabled)
50
 *  z = time zone (0 = UTC)
51
 *  y = year (5 = 91)
52
 *  cc = WWV propagation delay (52 = 22 ms)
53
 *  hh = WWVH propagation delay (81 = 33 ms)
54
 *  SS = status (80 or 82 = operating correctly)
55
 *  F = current receive frequency (4 = 15 MHz)
56
 *  T = transmitter (C = WWV, H = WWVH)
57
 *  tttt = time since last update (0000 = minutes)
58
 *  uu = flush character (03 = ^c)
59
 *  xx = 94 (unknown)
60
 *
61
 * The alarm condition is indicated by other than '8' at A, which occurs
62
 * during initial synchronization and when received signal is lost for
63
 * an extended period; unlock condition is indicated by other than
64
 * "0000" in the tttt subfield at Q.
65
 *
66
 * Fudge Factors
67
 *
68
 * There are no special fudge factors other than the generic.
69
 */
70
71
/*
72
 * Interface definitions
73
 */
74
#define DEVICE    "/dev/wwv%d" /* device name and unit */
75
0
#define SPEED232  B9600  /* uart speed (9600 baud) */
76
0
#define PRECISION (-10)  /* precision assumed (about 1 ms) */
77
0
#define WWVREFID  "WWV\0"  /* WWV reference ID */
78
0
#define WWVHREFID "WWVH"  /* WWVH reference ID */
79
0
#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
80
0
#define PST_PHI   (10e-6)  /* max clock oscillator offset */
81
0
#define LENPST    46  /* min timecode length */
82
83
/*
84
 * Unit control structure
85
 */
86
struct pstunit {
87
  int tcswitch; /* timecode switch */
88
  char  *lastptr; /* pointer to timecode data */
89
};
90
91
/*
92
 * Function prototypes
93
 */
94
static  int pst_start (int, struct peer *);
95
static  void  pst_shutdown  (int, struct peer *);
96
static  void  pst_receive (struct recvbuf *);
97
static  void  pst_poll  (int, struct peer *);
98
99
/*
100
 * Transfer vector
101
 */
102
struct  refclock refclock_pst = {
103
  pst_start,    /* start up driver */
104
  pst_shutdown,   /* shut down driver */
105
  pst_poll,   /* transmit poll message */
106
  noentry,    /* not used (old pst_control) */
107
  noentry,    /* initialize driver */
108
  noentry,    /* not used (old pst_buginfo) */
109
  NOFLAGS     /* not used */
110
};
111
112
113
/*
114
 * pst_start - open the devices and initialize data for processing
115
 */
116
static int
117
pst_start(
118
  int unit,
119
  struct peer *peer
120
  )
121
0
{
122
0
  register struct pstunit *up;
123
0
  struct refclockproc *pp;
124
0
  int fd;
125
0
  char device[20];
126
127
  /*
128
   * Open serial port. Use CLK line discipline, if available.
129
   */
130
0
  snprintf(device, sizeof(device), DEVICE, unit);
131
0
  fd = refclock_open(device, SPEED232, LDISC_CLK);
132
0
  if (fd <= 0)
133
0
    return (0);
134
135
  /*
136
   * Allocate and initialize unit structure
137
   */
138
0
  up = emalloc_zero(sizeof(*up));
139
0
  pp = peer->procptr;
140
0
  pp->io.clock_recv = pst_receive;
141
0
  pp->io.srcclock = peer;
142
0
  pp->io.datalen = 0;
143
0
  pp->io.fd = fd;
144
0
  if (!io_addclock(&pp->io)) {
145
0
    close(fd);
146
0
    pp->io.fd = -1;
147
0
    free(up);
148
0
    return (0);
149
0
  }
150
0
  pp->unitptr = up;
151
152
  /*
153
   * Initialize miscellaneous variables
154
   */
155
0
  peer->precision = PRECISION;
156
0
  pp->clockdesc = DESCRIPTION;
157
0
  memcpy((char *)&pp->refid, WWVREFID, 4);
158
0
  return (1);
159
0
}
160
161
162
/*
163
 * pst_shutdown - shut down the clock
164
 */
165
static void
166
pst_shutdown(
167
  int unit,
168
  struct peer *peer
169
  )
170
0
{
171
0
  register struct pstunit *up;
172
0
  struct refclockproc *pp;
173
174
0
  pp = peer->procptr;
175
0
  up = pp->unitptr;
176
0
  if (-1 != pp->io.fd)
177
0
    io_closeclock(&pp->io);
178
0
  if (NULL != up)
179
0
    free(up);
180
0
}
181
182
183
/*
184
 * pst_receive - receive data from the serial interface
185
 */
186
static void
187
pst_receive(
188
  struct recvbuf *rbufp
189
  )
190
0
{
191
0
  register struct pstunit *up;
192
0
  struct refclockproc *pp;
193
0
  struct peer *peer;
194
0
  l_fp trtmp;
195
0
  u_long ltemp;
196
0
  char ampmchar;    /* AM/PM indicator */
197
0
  char daychar;   /* standard/daylight indicator */
198
0
  char junque[10];  /* "yy/dd/mm/" discard */
199
0
  char info[14];    /* "frdzycchhSSFT" clock info */
200
201
  /*
202
   * Initialize pointers and read the timecode and timestamp
203
   */
204
0
  peer = rbufp->recv_peer;
205
0
  pp = peer->procptr;
206
0
  up = pp->unitptr;
207
0
  up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->a_lastcode
208
0
      + BMAX - 2 - up->lastptr, &trtmp);
209
0
  *up->lastptr++ = ' ';
210
0
  *up->lastptr = '\0';
211
212
  /*
213
   * Note we get a buffer and timestamp for each <cr>, but only
214
   * the first timestamp is retained.
215
   */
216
0
  if (up->tcswitch == 0)
217
0
    pp->lastrec = trtmp;
218
0
  up->tcswitch++;
219
0
  pp->lencode = up->lastptr - pp->a_lastcode;
220
0
  if (up->tcswitch < 3)
221
0
    return;
222
223
  /*
224
   * We get down to business, check the timecode format and decode
225
   * its contents. If the timecode has invalid length or is not in
226
   * proper format, we declare bad format and exit.
227
   */
228
0
  if (pp->lencode < LENPST) {
229
0
    refclock_report(peer, CEVNT_BADREPLY);
230
0
    return;
231
0
  }
232
233
  /*
234
   * Timecode format:
235
   * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
236
   */
237
0
  if (sscanf(pp->a_lastcode,
238
0
      "%c%2d:%2d:%2d.%3ld%c %9s%3d%13s%4ld",
239
0
      &ampmchar, &pp->hour, &pp->minute, &pp->second, &pp->nsec,
240
0
      &daychar, junque, &pp->day, info, &ltemp) != 10) {
241
0
    refclock_report(peer, CEVNT_BADREPLY);
242
0
    return;
243
0
  }
244
0
  pp->nsec *= 1000000;
245
246
  /*
247
   * Decode synchronization, quality and last update. If
248
   * unsynchronized, set the leap bits accordingly and exit. Once
249
   * synchronized, the dispersion depends only on when the clock
250
   * was last heard, which depends on the time since last update,
251
   * as reported by the clock.
252
   */
253
0
  if (info[9] != '8')
254
0
    pp->leap = LEAP_NOTINSYNC;
255
0
  if (info[12] == 'H')
256
0
    memcpy((char *)&pp->refid, WWVHREFID, 4);
257
0
  else
258
0
    memcpy((char *)&pp->refid, WWVREFID, 4);
259
0
  if (peer->stratum <= 1)
260
0
    peer->refid = pp->refid;
261
0
  if (ltemp == 0)
262
0
    pp->lastref = pp->lastrec;
263
0
  pp->disp = PST_PHI * ltemp * 60;
264
265
  /*
266
   * Process the new sample in the median filter and determine the
267
   * timecode timestamp.
268
   */
269
0
  if (!refclock_process(pp))
270
0
    refclock_report(peer, CEVNT_BADTIME);
271
0
  else if (peer->disp > MAXDISTANCE)
272
0
    refclock_receive(peer);
273
0
}
274
275
276
/*
277
 * pst_poll - called by the transmit procedure
278
 */
279
static void
280
pst_poll(
281
  int unit,
282
  struct peer *peer
283
  )
284
0
{
285
0
  register struct pstunit *up;
286
0
  struct refclockproc *pp;
287
288
  /*
289
   * Time to poll the clock. The PSTI/Traconex clock responds to a
290
   * "QTQDQMT" by returning a timecode in the format specified
291
   * above. Note there is no checking on state, since this may not
292
   * be the only customer reading the clock. Only one customer
293
   * need poll the clock; all others just listen in. If the clock
294
   * becomes unreachable, declare a timeout and keep going.
295
   */
296
0
  pp = peer->procptr;
297
0
  up = pp->unitptr;
298
0
  up->tcswitch = 0;
299
0
  up->lastptr = pp->a_lastcode;
300
0
  if (write(pp->io.fd, "QTQDQMT", 6) != 6)
301
0
    refclock_report(peer, CEVNT_FAULT);
302
0
  if (pp->coderecv == pp->codeproc) {
303
0
    refclock_report(peer, CEVNT_TIMEOUT);
304
0
    return;
305
0
  }
306
0
  refclock_receive(peer);
307
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
308
0
#ifdef DEBUG
309
0
  if (debug)
310
0
    printf("pst: timecode %d %s\n", pp->lencode,
311
0
        pp->a_lastcode);
312
0
#endif
313
0
  pp->polls++;
314
0
}
315
316
#else
317
int refclock_pst_int;
318
#endif /* REFCLOCK */