refclock_zyfer.c revision 106163
1229997Sken/*
2229997Sken * refclock_zyfer - clock driver for the Zyfer GPSTarplus Clock
3288348Smav *
4229997Sken * Harlan Stenn, Jan 2002
5229997Sken */
6229997Sken
7229997Sken#ifdef HAVE_CONFIG_H
8229997Sken#include <config.h>
9229997Sken#endif
10229997Sken
11229997Sken#if defined(REFCLOCK) && defined(CLOCK_ZYFER)
12229997Sken
13229997Sken#include "ntpd.h"
14229997Sken#include "ntp_io.h"
15229997Sken#include "ntp_refclock.h"
16229997Sken#include "ntp_stdlib.h"
17229997Sken#include "ntp_unixtime.h"
18229997Sken
19229997Sken#include <stdio.h>
20229997Sken#include <ctype.h>
21229997Sken
22229997Sken#ifdef HAVE_SYS_TERMIOS_H
23229997Sken# include <sys/termios.h>
24229997Sken#endif
25229997Sken#ifdef HAVE_SYS_PPSCLOCK_H
26229997Sken# include <sys/ppsclock.h>
27229997Sken#endif
28229997Sken
29229997Sken/*
30229997Sken * This driver provides support for the TOD serial port of a Zyfer GPStarplus.
31229997Sken * This clock also provides PPS as well as IRIG outputs.
32229997Sken * Precision is limited by the serial driver, etc.
33229997Sken *
34229997Sken * If I was really brave I'd hack/generalize the serial driver to deal
35229997Sken * with arbitrary on-time characters.  This clock *begins* the stream with
36229997Sken * `!`, the on-time character, and the string is *not* EOL-terminated.
37229997Sken *
38229997Sken * Configure the beast for 9600, 8N1.  While I see leap-second stuff
39229997Sken * in the documentation, the published specs on the TOD format only show
40229997Sken * the seconds going to '59'.  I see no leap warning in the TOD format.
41229997Sken *
42229997Sken * The clock sends the following message once per second:
43229997Sken *
44229997Sken *	!TIME,2002,017,07,59,32,2,4,1
45229997Sken *	      YYYY DDD HH MM SS m T O
46229997Sken *
47229997Sken *	!		On-time character
48233963Sken *	YYYY		Year
49229997Sken *	DDD	001-366	Day of Year
50229997Sken *	HH	00-23	Hour
51229997Sken *	MM	00-59	Minute
52229997Sken *	SS	00-59	Second (probably 00-60)
53229997Sken *	m	1-5	Time Mode:
54229997Sken *			1 = GPS time
55229997Sken *			2 = UTC time
56229997Sken *			3 = LGPS time (Local GPS)
57229997Sken *			4 = LUTC time (Local UTC)
58229997Sken *			5 = Manual time
59229997Sken *	T	4-9	Time Figure Of Merit:
60229997Sken *			4         x <= 1us
61268096Smav *			5   1us < x <= 10 us
62268096Smav *			6  10us < x <= 100us
63229997Sken *			7 100us < x <= 1ms
64268096Smav *			8   1ms < x <= 10ms
65268096Smav *			9  10ms < x
66268096Smav *	O	0-4	Operation Mode:
67229997Sken *			0 Warm-up
68268096Smav *			1 Time Locked
69268581Smav *			2 Coasting
70268096Smav *			3 Recovering
71288348Smav *			4 Manual
72287621Smav *
73268096Smav */
74268096Smav
75268096Smav/*
76268096Smav * Interface definitions
77268096Smav */
78268096Smav#define	DEVICE		"/dev/zyfer%d" /* device name and unit */
79268581Smav#define	SPEED232	B9600	/* uart speed (9600 baud) */
80268096Smav#define	PRECISION	(-20)	/* precision assumed (about 1 us) */
81288348Smav#define	REFID		"GPS\0"	/* reference ID */
82287621Smav#define	DESCRIPTION	"Zyfer GPStarplus" /* WRU */
83268096Smav
84268096Smav#define	LENZYFER	29	/* timecode length */
85268096Smav
86268096Smav/*
87268096Smav * Unit control structure
88268096Smav */
89268096Smavstruct zyferunit {
90268096Smav	u_char	Rcvbuf[LENZYFER + 1];
91288348Smav	u_char	polled;		/* poll message flag */
92287621Smav	int	pollcnt;
93268096Smav	l_fp    tstamp;         /* timestamp of last poll */
94268096Smav	int	Rcvptr;
95268096Smav};
96268096Smav
97268096Smav/*
98268096Smav * Function prototypes
99268363Smav */
100268363Smavstatic	int	zyfer_start	P((int, struct peer *));
101288348Smavstatic	void	zyfer_shutdown	P((int, struct peer *));
102287621Smavstatic	void	zyfer_receive	P((struct recvbuf *));
103268363Smavstatic	void	zyfer_poll	P((int, struct peer *));
104268363Smav
105268363Smav/*
106268363Smav * Transfer vector
107268096Smav */
108268096Smavstruct	refclock refclock_zyfer = {
109268096Smav	zyfer_start,		/* start up driver */
110268096Smav	zyfer_shutdown,		/* shut down driver */
111268096Smav	zyfer_poll,		/* transmit poll message */
112268096Smav	noentry,		/* not used (old zyfer_control) */
113268096Smav	noentry,		/* initialize driver (not used) */
114268096Smav	noentry,		/* not used (old zyfer_buginfo) */
115268581Smav	NOFLAGS			/* not used */
116268581Smav};
117288348Smav
118287621Smav
119268581Smav/*
120268581Smav * zyfer_start - open the devices and initialize data for processing
121268096Smav */
122268096Smavstatic int
123268096Smavzyfer_start(
124268096Smav	int unit,
125268581Smav	struct peer *peer
126268581Smav	)
127288348Smav{
128287621Smav	register struct zyferunit *up;
129268581Smav	struct refclockproc *pp;
130268581Smav	int fd;
131268096Smav	char device[20];
132288229Smav
133268096Smav	/*
134268096Smav	 * Open serial port.
135268581Smav	 * Something like LDISC_ACTS that looked for ! would be nice...
136268581Smav	 */
137288348Smav	(void)sprintf(device, DEVICE, unit);
138287621Smav	if ( !(fd = refclock_open(device, SPEED232, LDISC_RAW)) )
139268581Smav	    return (0);
140268581Smav
141268096Smav	msyslog(LOG_NOTICE, "zyfer(%d) fd: %d dev <%s>", unit, fd, device);
142288229Smav
143268096Smav	/*
144268096Smav	 * Allocate and initialize unit structure
145268581Smav	 */
146268581Smav	if (!(up = (struct zyferunit *)
147288348Smav	      emalloc(sizeof(struct zyferunit)))) {
148287621Smav		(void) close(fd);
149268581Smav		return (0);
150268581Smav	}
151268096Smav	memset((char *)up, 0, sizeof(struct zyferunit));
152268361Smav	pp = peer->procptr;
153268096Smav	pp->io.clock_recv = zyfer_receive;
154268096Smav	pp->io.srcclock = (caddr_t)peer;
155268581Smav	pp->io.datalen = 0;
156268581Smav	pp->io.fd = fd;
157288348Smav	if (!io_addclock(&pp->io)) {
158287621Smav		(void) close(fd);
159268581Smav		free(up);
160268581Smav		return (0);
161268096Smav	}
162268361Smav	pp->unitptr = (caddr_t)up;
163268096Smav
164268096Smav	/*
165274333Smav	 * Initialize miscellaneous variables
166274333Smav	 */
167288348Smav	peer->precision = PRECISION;
168287621Smav	pp->clockdesc = DESCRIPTION;
169274333Smav	memcpy((char *)&pp->refid, REFID, 4);
170274333Smav	up->pollcnt = 2;
171274333Smav	up->polled = 0;		/* May not be needed... */
172274333Smav
173268096Smav	return (1);
174268096Smav}
175268581Smav
176268581Smav
177288348Smav/*
178287621Smav * zyfer_shutdown - shut down the clock
179268581Smav */
180268581Smavstatic void
181268096Smavzyfer_shutdown(
182268361Smav	int unit,
183268096Smav	struct peer *peer
184268096Smav	)
185268096Smav{
186268096Smav	register struct zyferunit *up;
187268096Smav	struct refclockproc *pp;
188268096Smav
189268096Smav	pp = peer->procptr;
190268767Smav	up = (struct zyferunit *)pp->unitptr;
191268767Smav	io_closeclock(&pp->io);
192268767Smav	free(up);
193268767Smav}
194268767Smav
195288348Smav
196268767Smav/*
197268767Smav * zyfer_receive - receive data from the serial interface
198268767Smav */
199268767Smavstatic void
200268767Smavzyfer_receive(
201268767Smav	struct recvbuf *rbufp
202288348Smav	)
203268767Smav{
204268767Smav	register struct zyferunit *up;
205268767Smav	struct refclockproc *pp;
206268767Smav	struct peer *peer;
207268767Smav	int tmode;		/* Time mode */
208268767Smav	int tfom;		/* Time Figure Of Merit */
209268767Smav	int omode;		/* Operation mode */
210268767Smav	u_char *p;
211268767Smav#ifdef PPS
212268767Smav	struct ppsclockev ppsev;
213268767Smav	int request;
214268767Smav#ifdef HAVE_CIOGETEV
215268767Smav        request = CIOGETEV;
216268767Smav#endif
217268767Smav#ifdef HAVE_TIOCGPPSEV
218268767Smav        request = TIOCGPPSEV;
219268767Smav#endif
220268767Smav#endif /* PPS */
221268767Smav
222268767Smav	peer = (struct peer *)rbufp->recv_srcclock;
223268767Smav	pp = peer->procptr;
224268767Smav	up = (struct zyferunit *)pp->unitptr;
225268767Smav	p = (u_char *) &rbufp->recv_space;
226268767Smav	/*
227268767Smav	 * If lencode is 0:
228268767Smav	 * - if *rbufp->recv_space is !
229268767Smav	 * - - call refclock_gtlin to get things going
230268767Smav	 * - else flush
231268767Smav	 * else stuff it on the end of lastcode
232268767Smav	 * If we don't have LENZYFER bytes
233268767Smav	 * - wait for more data
234268767Smav	 * Crack the beast, and if it's OK, process it.
235268767Smav	 *
236268767Smav	 * We use refclock_getlin() because we might use LDISC_CLK.
237268767Smav	 *
238268767Smav	 * Under FreeBSD, we get the ! followed by two 14-byte packets.
239268767Smav	 */
240268767Smav
241268767Smav	if (pp->lencode >= LENZYFER)
242268767Smav		pp->lencode = 0;
243268767Smav
244268767Smav	if (!pp->lencode) {
245268767Smav		if (*p == '!')
246268767Smav			pp->lencode = refclock_gtlin(rbufp, pp->a_lastcode,
247268767Smav						     BMAX, &pp->lastrec);
248268767Smav		else
249268767Smav			return;
250288310Smav	} else {
251273730Smav		memcpy(pp->a_lastcode + pp->lencode, p, rbufp->recv_length);
252273730Smav		pp->lencode += rbufp->recv_length;
253269497Smav		pp->a_lastcode[pp->lencode] = '\0';
254269497Smav	}
255269497Smav
256268767Smav	if (pp->lencode < LENZYFER)
257268767Smav		return;
258288310Smav
259269497Smav	record_clock_stats(&peer->srcadr, pp->a_lastcode);
260269497Smav
261269497Smav	/*
262269497Smav	 * We get down to business, check the timecode format and decode
263268767Smav	 * its contents. If the timecode has invalid length or is not in
264268767Smav	 * proper format, we declare bad format and exit.
265268767Smav	 */
266268767Smav
267268767Smav	if (pp->lencode != LENZYFER) {
268268767Smav		refclock_report(peer, CEVNT_BADTIME);
269268767Smav		return;
270268767Smav	}
271268767Smav
272268767Smav	/*
273268767Smav	 * Timecode sample: "!TIME,2002,017,07,59,32,2,4,1"
274268767Smav	 */
275268767Smav	if (sscanf(pp->a_lastcode, "!TIME,%4d,%3d,%2d,%2d,%2d,%d,%d,%d",
276268767Smav		   &pp->year, &pp->day, &pp->hour, &pp->minute, &pp->second,
277268767Smav		   &tmode, &tfom, &omode) != 8) {
278268767Smav		refclock_report(peer, CEVNT_BADREPLY);
279268767Smav		return;
280268767Smav	}
281268767Smav
282268767Smav	if (tmode != 2) {
283268767Smav		refclock_report(peer, CEVNT_BADTIME);
284268767Smav		return;
285268767Smav	}
286268767Smav
287268767Smav	/* Should we make sure tfom is 4? */
288268767Smav
289268767Smav	if (omode != 1) {
290268767Smav		pp->leap = LEAP_NOTINSYNC;
291268767Smav		return;
292268767Smav	}
293268767Smav#ifdef PPS
294268767Smav	if(ioctl(fdpps,request,(caddr_t) &ppsev) >=0) {
295268767Smav		ppsev.tv.tv_sec += (u_int32) JAN_1970;
296288348Smav		TVTOTS(&ppsev.tv,&up->tstamp);
297268767Smav	}
298268767Smav	/* record the last ppsclock event time stamp */
299268767Smav	pp->lastrec = up->tstamp;
300268767Smav#endif /* PPS */
301268767Smav	if (!refclock_process(pp)) {
302268767Smav		refclock_report(peer, CEVNT_BADTIME);
303268767Smav		return;
304268767Smav        }
305268767Smav
306268767Smav	/*
307268767Smav	 * Good place for record_clock_stats()
308288348Smav	 */
309271845Smav	up->pollcnt = 2;
310271845Smav
311268767Smav	if (up->polled) {
312268767Smav		up->polled = 0;
313268767Smav		refclock_receive(peer);
314268767Smav	}
315268767Smav}
316268767Smav
317268767Smav
318268767Smav/*
319268767Smav * zyfer_poll - called by the transmit procedure
320268767Smav */
321268767Smavstatic void
322268767Smavzyfer_poll(
323288348Smav	int unit,
324287621Smav	struct peer *peer
325271845Smav	)
326271845Smav{
327268767Smav	register struct zyferunit *up;
328268767Smav	struct refclockproc *pp;
329268767Smav
330268767Smav	/*
331268767Smav	 * We don't really do anything here, except arm the receiving
332268767Smav	 * side to capture a sample and check for timeouts.
333288348Smav	 */
334271845Smav	pp = peer->procptr;
335271845Smav	up = (struct zyferunit *)pp->unitptr;
336268767Smav	if (!up->pollcnt)
337268767Smav		refclock_report(peer, CEVNT_TIMEOUT);
338268767Smav	else
339268767Smav		up->pollcnt--;
340268767Smav	pp->polls++;
341268767Smav	up->polled = 1;
342288348Smav}
343273730Smav
344273730Smav#else
345268767Smavint refclock_zyfer_bs;
346268767Smav#endif /* REFCLOCK */
347268767Smav