Coverage Report

Created: 2023-05-19 06:16

/src/ntp-dev/ntpd/refclock_zyfer.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
3
 *
4
 * Harlan Stenn, Jan 2002
5
 */
6
7
#ifdef HAVE_CONFIG_H
8
#include <config.h>
9
#endif
10
11
#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
12
13
#include "ntpd.h"
14
#include "ntp_io.h"
15
#include "ntp_refclock.h"
16
#include "ntp_stdlib.h"
17
#include "ntp_unixtime.h"
18
19
#include <stdio.h>
20
#include <ctype.h>
21
22
#if defined(HAVE_TERMIOS_H)
23
# include <termios.h>
24
#elif defined(HAVE_SYS_TERMIOS_H)
25
# include <sys/termios.h>
26
#endif
27
#ifdef HAVE_SYS_PPSCLOCK_H
28
# include <sys/ppsclock.h>
29
#endif
30
31
/*
32
 * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
33
 * This clock also provides PPS as well as IRIG outputs.
34
 * Precision is limited by the serial driver, etc.
35
 *
36
 * If I was really brave I'd hack/generalize the serial driver to deal
37
 * with arbitrary on-time characters.  This clock *begins* the stream with
38
 * `!`, the on-time character, and the string is *not* EOL-terminated.
39
 *
40
 * Configure the beast for 9600, 8N1.  While I see leap-second stuff
41
 * in the documentation, the published specs on the TOD format only show
42
 * the seconds going to '59'.  I see no leap warning in the TOD format.
43
 *
44
 * The clock sends the following message once per second:
45
 *
46
 *  !TIME,2002,017,07,59,32,2,4,1
47
 *        YYYY DDD HH MM SS m T O
48
 *
49
 *  !   On-time character
50
 *  YYYY    Year
51
 *  DDD 001-366 Day of Year
52
 *  HH  00-23 Hour
53
 *  MM  00-59 Minute
54
 *  SS  00-59 Second (probably 00-60)
55
 *  m 1-5 Time Mode:
56
 *      1 = GPS time
57
 *      2 = UTC time
58
 *      3 = LGPS time (Local GPS)
59
 *      4 = LUTC time (Local UTC)
60
 *      5 = Manual time
61
 *  T 4-9 Time Figure Of Merit:
62
 *      4         x <= 1us
63
 *      5   1us < x <= 10 us
64
 *      6  10us < x <= 100us
65
 *      7 100us < x <= 1ms
66
 *      8   1ms < x <= 10ms
67
 *      9  10ms < x
68
 *  O 0-4 Operation Mode:
69
 *      0 Warm-up
70
 *      1 Time Locked
71
 *      2 Coasting
72
 *      3 Recovering
73
 *      4 Manual
74
 *
75
 */
76
77
/*
78
 * Interface definitions
79
 */
80
#define DEVICE    "/dev/zyfer%d" /* device name and unit */
81
0
#define SPEED232  B9600  /* uart speed (9600 baud) */
82
0
#define PRECISION (-20)  /* precision assumed (about 1 us) */
83
0
#define REFID   "GPS\0"  /* reference ID */
84
0
#define DESCRIPTION "Zyfer GPStarplus" /* WRU */
85
86
0
#define LENZYFER  29  /* timecode length */
87
88
/*
89
 * Unit control structure
90
 */
91
struct zyferunit {
92
  u_char  Rcvbuf[LENZYFER + 1];
93
  u_char  polled;   /* poll message flag */
94
  int pollcnt;
95
  l_fp    tstamp;         /* timestamp of last poll */
96
  int Rcvptr;
97
};
98
99
/*
100
 * Function prototypes
101
 */
102
static  int zyfer_start (int, struct peer *);
103
static  void  zyfer_shutdown  (int, struct peer *);
104
static  void  zyfer_receive (struct recvbuf *);
105
static  void  zyfer_poll  (int, struct peer *);
106
107
/*
108
 * Transfer vector
109
 */
110
struct  refclock refclock_zyfer = {
111
  zyfer_start,    /* start up driver */
112
  zyfer_shutdown,   /* shut down driver */
113
  zyfer_poll,   /* transmit poll message */
114
  noentry,    /* not used (old zyfer_control) */
115
  noentry,    /* initialize driver (not used) */
116
  noentry,    /* not used (old zyfer_buginfo) */
117
  NOFLAGS     /* not used */
118
};
119
120
121
/*
122
 * zyfer_start - open the devices and initialize data for processing
123
 */
124
static int
125
zyfer_start(
126
  int unit,
127
  struct peer *peer
128
  )
129
0
{
130
0
  register struct zyferunit *up;
131
0
  struct refclockproc *pp;
132
0
  int fd;
133
0
  char device[20];
134
135
  /*
136
   * Open serial port.
137
   * Something like LDISC_ACTS that looked for ! would be nice...
138
   */
139
0
  snprintf(device, sizeof(device), DEVICE, unit);
140
0
  fd = refclock_open(device, SPEED232, LDISC_RAW);
141
0
  if (fd <= 0)
142
0
    return (0);
143
144
0
  msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
145
146
  /*
147
   * Allocate and initialize unit structure
148
   */
149
0
  up = emalloc(sizeof(struct zyferunit));
150
0
  memset(up, 0, sizeof(struct zyferunit));
151
0
  pp = peer->procptr;
152
0
  pp->io.clock_recv = zyfer_receive;
153
0
  pp->io.srcclock = peer;
154
0
  pp->io.datalen = 0;
155
0
  pp->io.fd = fd;
156
0
  if (!io_addclock(&pp->io)) {
157
0
    close(fd);
158
0
    pp->io.fd = -1;
159
0
    free(up);
160
0
    return (0);
161
0
  }
162
0
  pp->unitptr = up;
163
164
  /*
165
   * Initialize miscellaneous variables
166
   */
167
0
  peer->precision = PRECISION;
168
0
  pp->clockdesc = DESCRIPTION;
169
0
  memcpy((char *)&pp->refid, REFID, 4);
170
0
  up->pollcnt = 2;
171
0
  up->polled = 0;   /* May not be needed... */
172
173
0
  return (1);
174
0
}
175
176
177
/*
178
 * zyfer_shutdown - shut down the clock
179
 */
180
static void
181
zyfer_shutdown(
182
  int unit,
183
  struct peer *peer
184
  )
185
0
{
186
0
  register struct zyferunit *up;
187
0
  struct refclockproc *pp;
188
189
0
  pp = peer->procptr;
190
0
  up = pp->unitptr;
191
0
  if (pp->io.fd != -1)
192
0
    io_closeclock(&pp->io);
193
0
  if (up != NULL)
194
0
    free(up);
195
0
}
196
197
198
/*
199
 * zyfer_receive - receive data from the serial interface
200
 */
201
static void
202
zyfer_receive(
203
  struct recvbuf *rbufp
204
  )
205
0
{
206
0
  register struct zyferunit *up;
207
0
  struct refclockproc *pp;
208
0
  struct peer *peer;
209
0
  int tmode;    /* Time mode */
210
0
  int tfom;   /* Time Figure Of Merit */
211
0
  int omode;    /* Operation mode */
212
0
  u_char *p;
213
214
0
  peer = rbufp->recv_peer;
215
0
  pp = peer->procptr;
216
0
  up = pp->unitptr;
217
0
  p = (u_char *) &rbufp->recv_space;
218
  /*
219
   * If lencode is 0:
220
   * - if *rbufp->recv_space is !
221
   * - - call refclock_gtlin to get things going
222
   * - else flush
223
   * else stuff it on the end of lastcode
224
   * If we don't have LENZYFER bytes
225
   * - wait for more data
226
   * Crack the beast, and if it's OK, process it.
227
   *
228
   * We use refclock_gtlin() because we might use LDISC_CLK.
229
   *
230
   * Under FreeBSD, we get the ! followed by two 14-byte packets.
231
   */
232
233
0
  if (pp->lencode >= LENZYFER)
234
0
    pp->lencode = 0;
235
236
0
  if (!pp->lencode) {
237
0
    if (*p == '!')
238
0
      pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
239
0
                 BMAX, &pp->lastrec);
240
0
    else
241
0
      return;
242
0
  } else {
243
0
    memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
244
0
    pp->lencode += rbufp->recv_length;
245
0
    pp->a_lastcode[pp->lencode] = '\0';
246
0
  }
247
248
0
  if (pp->lencode < LENZYFER)
249
0
    return;
250
251
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
252
253
  /*
254
   * We get down to business, check the timecode format and decode
255
   * its contents. If the timecode has invalid length or is not in
256
   * proper format, we declare bad format and exit.
257
   */
258
259
0
  if (pp->lencode != LENZYFER) {
260
0
    refclock_report(peer, CEVNT_BADTIME);
261
0
    return;
262
0
  }
263
264
  /*
265
   * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
266
   */
267
0
  if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
268
0
       &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
269
0
       &tmode, &tfom, &omode) != 8) {
270
0
    refclock_report(peer, CEVNT_BADREPLY);
271
0
    return;
272
0
  }
273
274
0
  if (tmode != 2) {
275
0
    refclock_report(peer, CEVNT_BADTIME);
276
0
    return;
277
0
  }
278
279
  /* Should we make sure tfom is 4? */
280
281
0
  if (omode != 1) {
282
0
    pp->leap = LEAP_NOTINSYNC;
283
0
    return;
284
0
  }
285
286
0
  if (!refclock_process(pp)) {
287
0
    refclock_report(peer, CEVNT_BADTIME);
288
0
    return;
289
0
        }
290
291
  /*
292
   * Good place for record_clock_stats()
293
   */
294
0
  up->pollcnt = 2;
295
296
0
  if (up->polled) {
297
0
    up->polled = 0;
298
0
    refclock_receive(peer);
299
0
  }
300
0
}
301
302
303
/*
304
 * zyfer_poll - called by the transmit procedure
305
 */
306
static void
307
zyfer_poll(
308
  int unit,
309
  struct peer *peer
310
  )
311
0
{
312
0
  register struct zyferunit *up;
313
0
  struct refclockproc *pp;
314
315
  /*
316
   * We don't really do anything here, except arm the receiving
317
   * side to capture a sample and check for timeouts.
318
   */
319
0
  pp = peer->procptr;
320
0
  up = pp->unitptr;
321
0
  if (!up->pollcnt)
322
0
    refclock_report(peer, CEVNT_TIMEOUT);
323
0
  else
324
0
    up->pollcnt--;
325
0
  pp->polls++;
326
0
  up->polled = 1;
327
0
}
328
329
#else
330
int refclock_zyfer_bs;
331
#endif /* REFCLOCK */