2 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
4 * Harlan Stenn, Jan 2002
11 #if defined(REFCLOCK) && defined(CLOCK_ZYFER)
15 #include "ntp_refclock.h"
16 #include "ntp_stdlib.h"
17 #include "ntp_unixtime.h"
22 #if defined(HAVE_TERMIOS_H)
24 #elif defined(HAVE_SYS_TERMIOS_H)
25 # include <sys/termios.h>
27 #ifdef HAVE_SYS_PPSCLOCK_H
28 # include <sys/ppsclock.h>
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.
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.
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.
44 * The clock sends the following message once per second:
46 * !TIME,2002,017,07,59,32,2,4,1
47 * YYYY DDD HH MM SS m T O
51 * DDD 001-366 Day of Year
54 * SS 00-59 Second (probably 00-60)
58 * 3 = LGPS time (Local GPS)
59 * 4 = LUTC time (Local UTC)
61 * T 4-9 Time Figure Of Merit:
68 * O 0-4 Operation Mode:
78 * Interface definitions
80 #define DEVICE "/dev/zyfer%d" /* device name and unit */
81 #define SPEED232 B9600 /* uart speed (9600 baud) */
82 #define PRECISION (-20) /* precision assumed (about 1 us) */
83 #define REFID "GPS\0" /* reference ID */
84 #define DESCRIPTION "Zyfer GPStarplus" /* WRU */
86 #define LENZYFER 29 /* timecode length */
89 * Unit control structure
92 u_char Rcvbuf[LENZYFER + 1];
93 u_char polled; /* poll message flag */
95 l_fp tstamp; /* timestamp of last poll */
100 * Function prototypes
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 *);
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 */
122 * zyfer_start - open the devices and initialize data for processing
130 register struct zyferunit *up;
131 struct refclockproc *pp;
137 * Something like LDISC_ACTS that looked for ! would be nice...
139 snprintf(device, sizeof(device), DEVICE, unit);
140 fd = refclock_open(device, SPEED232, LDISC_RAW);
144 msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
147 * Allocate and initialize unit structure
149 up = emalloc(sizeof(struct zyferunit));
150 memset(up, 0, sizeof(struct zyferunit));
152 pp->io.clock_recv = zyfer_receive;
153 pp->io.srcclock = peer;
156 if (!io_addclock(&pp->io)) {
165 * Initialize miscellaneous variables
167 peer->precision = PRECISION;
168 pp->clockdesc = DESCRIPTION;
169 memcpy((char *)&pp->refid, REFID, 4);
171 up->polled = 0; /* May not be needed... */
178 * zyfer_shutdown - shut down the clock
186 register struct zyferunit *up;
187 struct refclockproc *pp;
192 io_closeclock(&pp->io);
199 * zyfer_receive - receive data from the serial interface
203 struct recvbuf *rbufp
206 register struct zyferunit *up;
207 struct refclockproc *pp;
209 int tmode; /* Time mode */
210 int tfom; /* Time Figure Of Merit */
211 int omode; /* Operation mode */
214 peer = rbufp->recv_peer;
217 p = (u_char *) &rbufp->recv_space;
220 * - if *rbufp->recv_space is !
221 * - - call refclock_gtlin to get things going
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.
228 * We use refclock_gtlin() because we might use LDISC_CLK.
230 * Under FreeBSD, we get the ! followed by two 14-byte packets.
233 if (pp->lencode >= LENZYFER)
238 pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
243 memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
244 pp->lencode += rbufp->recv_length;
245 pp->a_lastcode[pp->lencode] = '\0';
248 if (pp->lencode < LENZYFER)
251 record_clock_stats(&peer->srcadr, pp->a_lastcode);
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.
259 if (pp->lencode != LENZYFER) {
260 refclock_report(peer, CEVNT_BADTIME);
265 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
267 if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
268 &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
269 &tmode, &tfom, &omode) != 8) {
270 refclock_report(peer, CEVNT_BADREPLY);
275 refclock_report(peer, CEVNT_BADTIME);
279 /* Should we make sure tfom is 4? */
282 pp->leap = LEAP_NOTINSYNC;
286 if (!refclock_process(pp)) {
287 refclock_report(peer, CEVNT_BADTIME);
292 * Good place for record_clock_stats()
298 refclock_receive(peer);
304 * zyfer_poll - called by the transmit procedure
312 register struct zyferunit *up;
313 struct refclockproc *pp;
316 * We don't really do anything here, except arm the receiving
317 * side to capture a sample and check for timeouts.
322 refclock_report(peer, CEVNT_TIMEOUT);
330 int refclock_zyfer_bs;
331 #endif /* REFCLOCK */