2 * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
4 * Harlan Stenn, Jan 2002
11 #include "ntp_refclock.h"
12 #include "ntp_stdlib.h"
20 * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
21 * This clock also provides PPS as well as IRIG outputs.
22 * Precision is limited by the serial driver, etc.
24 * If I was really brave I'd hack/generalize the serial driver to deal
25 * with arbitrary on-time characters. This clock *begins* the stream with
26 * `!`, the on-time character, and the string is *not* EOL-terminated.
28 * Configure the beast for 9600, 8N1. While I see leap-second stuff
29 * in the documentation, the published specs on the TOD format only show
30 * the seconds going to '59'. I see no leap warning in the TOD format.
32 * The clock sends the following message once per second:
34 * !TIME,2002,017,07,59,32,2,4,1
35 * YYYY DDD HH MM SS m T O
39 * DDD 001-366 Day of Year
42 * SS 00-59 Second (probably 00-60)
46 * 3 = LGPS time (Local GPS)
47 * 4 = LUTC time (Local UTC)
49 * T 4-9 Time Figure Of Merit:
56 * O 0-4 Operation Mode:
66 * Interface definitions
68 #define DEVICE "/dev/zyfer%d" /* device name and unit */
69 #define SPEED232 B9600 /* uart speed (9600 baud) */
70 #define PRECISION (-20) /* precision assumed (about 1 us) */
71 #define REFID "GPS\0" /* reference ID */
72 #define NAME "ZYFER" /* shortname */
73 #define DESCRIPTION "Zyfer GPStarplus" /* WRU */
75 #define LENZYFER 29 /* timecode length */
78 * Unit control structure
81 uint8_t Rcvbuf
[LENZYFER
+ 1];
82 uint8_t polled
; /* poll message flag */
90 static bool zyfer_start (int, struct peer
*);
91 static void zyfer_receive (struct recvbuf
*);
92 static void zyfer_poll (int, struct peer
*);
97 struct refclock refclock_zyfer
= {
98 NAME
, /* basename of driver */
99 zyfer_start
, /* start up driver */
100 NULL
, /* shut down driver in the standard way */
101 zyfer_poll
, /* transmit poll message */
102 NULL
, /* not used (old zyfer_control) */
103 NULL
, /* initialize driver (not used) */
104 NULL
/* timer - not used */
109 * zyfer_start - open the devices and initialize data for processing
117 struct zyferunit
*up
;
118 struct refclockproc
*pp
;
124 * Something like LDISC_ACTS that looked for ! would be nice...
126 snprintf(device
, sizeof(device
), DEVICE
, unit
);
127 fd
= refclock_open(peer
->cfg
.path
? peer
->cfg
.path
: device
,
128 peer
->cfg
.baud
? peer
->cfg
.baud
: SPEED232
,
131 /* coverity[leaked_handle] */
134 msyslog(LOG_NOTICE
, "REFCLOCK: zyfer(%d) fd: %d", unit
, fd
);
137 * Allocate and initialize unit structure
139 up
= emalloc_zero(sizeof(struct zyferunit
));
141 pp
->io
.clock_recv
= zyfer_receive
;
142 pp
->io
.srcclock
= peer
;
145 if (!io_addclock(&pp
->io
)) {
154 * Initialize miscellaneous variables
156 peer
->precision
= PRECISION
;
157 pp
->clockname
= NAME
;
158 pp
->clockdesc
= DESCRIPTION
;
159 memcpy((char *)&pp
->refid
, REFID
, REFIDLEN
);
160 peer
->sstclktype
= CTL_SST_TS_UHF
;
162 up
->polled
= 0; /* May not be needed... */
169 * zyfer_receive - receive data from the serial interface
173 struct recvbuf
*rbufp
176 struct zyferunit
*up
;
177 struct refclockproc
*pp
;
179 int tmode
; /* Time mode */
180 int tfom
; /* Time Figure Of Merit */
181 int omode
; /* Operation mode */
184 peer
= rbufp
->recv_peer
;
187 p
= (uint8_t *) &rbufp
->recv_buffer
;
190 * - if *rbufp->recv_buffer is !
191 * - - call refclock_gtlin to get things going
193 * else stuff it on the end of lastcode
194 * If we don't have LENZYFER bytes
195 * - wait for more data
196 * Crack the beast, and if it's OK, process it.
198 * We use refclock_gtlin() because we might use LDISC_CLK.
200 * Under FreeBSD, we get the ! followed by two 14-byte packets.
203 if (pp
->lencode
>= LENZYFER
)
208 pp
->lencode
= refclock_gtlin(rbufp
, pp
->a_lastcode
,
213 memcpy(pp
->a_lastcode
+ pp
->lencode
, p
, rbufp
->recv_length
);
214 pp
->lencode
+= (int)rbufp
->recv_length
;
215 pp
->a_lastcode
[pp
->lencode
] = '\0';
218 if (pp
->lencode
< LENZYFER
)
221 record_clock_stats(peer
, pp
->a_lastcode
);
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.
229 if (pp
->lencode
!= LENZYFER
) {
230 refclock_report(peer
, CEVNT_BADTIME
);
235 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
237 if (sscanf(pp
->a_lastcode
, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
238 &pp
->year
, &pp
->yday
, &pp
->hour
, &pp
->minute
, &pp
->second
,
239 &tmode
, &tfom
, &omode
) != 8) {
240 refclock_report(peer
, CEVNT_BADREPLY
);
245 refclock_report(peer
, CEVNT_BADTIME
);
249 /* Should we make sure tfom is 4? */
252 pp
->leap
= LEAP_NOTINSYNC
;
256 if (!refclock_process(pp
)) {
257 refclock_report(peer
, CEVNT_BADTIME
);
262 * Good place for record_clock_stats()
268 refclock_receive(peer
);
274 * zyfer_poll - called by the transmit procedure
282 struct zyferunit
*up
;
283 struct refclockproc
*pp
;
288 * We don't really do anything here, except arm the receiving
289 * side to capture a sample and check for timeouts.
294 refclock_report(peer
, CEVNT_TIMEOUT
);