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