1187712Sraj/*
2187712Sraj * refclock_fg - clock driver for the Forum Graphic GPS datating station
3187712Sraj */
4187712Sraj
5187712Sraj#ifdef HAVE_CONFIG_H
6187712Sraj# include <config.h>
7187712Sraj#endif
8187712Sraj
9187712Sraj#if defined(REFCLOCK) && defined(CLOCK_FG)
10187712Sraj
11187712Sraj#include "ntpd.h"
12187712Sraj#include "ntp_io.h"
13187712Sraj#include "ntp_refclock.h"
14187712Sraj#include "ntp_calendar.h"
15187712Sraj#include "ntp_stdlib.h"
16187712Sraj
17187712Sraj/*
18187712Sraj * This driver supports the Forum Graphic GPS dating station.
19187712Sraj * More information about FG GPS is available on http://www.forumgraphic.com
20187712Sraj * Contact das@amt.ru for any question about this driver.
21187712Sraj */
22187712Sraj
23187712Sraj/*
24187712Sraj * Interface definitions
25187712Sraj */
26187712Sraj#define	DEVICE		"/dev/fgclock%d"
27187712Sraj#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
28187712Sraj#define REFID		"GPS"
29187712Sraj#define DESCRIPTION	"Forum Graphic GPS dating station"
30187712Sraj#define LENFG		26	/* timecode length */
31187712Sraj#define SPEED232        B9600   /* uart speed (9600 baud) */
32187712Sraj
33187712Sraj/*
34187712Sraj * Function prototypes
35187712Sraj */
36187712Srajstatic	int 	fg_init 	(int);
37187712Srajstatic	int 	fg_start 	(int, struct peer *);
38187712Srajstatic	void	fg_shutdown	(int, struct peer *);
39187712Srajstatic	void	fg_poll		(int, struct peer *);
40187712Srajstatic	void	fg_receive	(struct recvbuf *);
41187712Sraj
42187712Sraj/*
43187712Sraj * Forum Graphic unit control structure
44187712Sraj */
45187712Sraj
46187712Srajstruct fgunit {
47187712Sraj	int pollnum;	/* Use peer.poll instead? */
48187712Sraj	int status; 	/* Hug to check status information on GPS */
49187712Sraj	int y2kwarn;	/* Y2K bug */
50187712Sraj};
51187712Sraj
52187712Sraj/*
53187712Sraj * Queries definition
54187712Sraj */
55187712Srajstatic char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56187712Sraj0, 0, 0, 0, 0, 0, 0, 0, 0 };
57187712Srajstatic char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58187712Sraj0, 0, 0, 0, 0, 0, 0, 0, 0 };
59187712Sraj
60187712Sraj/*
61187712Sraj * Transfer vector
62187712Sraj */
63187712Srajstruct  refclock refclock_fg = {
64187712Sraj	fg_start,		/* start up driver */
65187712Sraj	fg_shutdown,		/* shut down driver */
66187712Sraj	fg_poll,		/* transmit poll message */
67187712Sraj	noentry,		/* not used */
68187712Sraj	noentry,		/* initialize driver (not used) */
69187712Sraj	noentry,		/* not used */
70187712Sraj	NOFLAGS			/* not used */
71187712Sraj};
72187712Sraj
73187712Sraj/*
74187712Sraj * fg_init - Initialization of FG GPS.
75187712Sraj */
76187712Sraj
77187712Srajstatic int
78187712Srajfg_init(
79187712Sraj	int fd
80187712Sraj	)
81187712Sraj{
82187712Sraj	if (write(fd, fginit, LENFG) != LENFG)
83187712Sraj		return 0;
84187712Sraj
85187712Sraj	return 1;
86187712Sraj}
87187712Sraj
88187712Sraj/*
89187712Sraj * fg_start - open the device and initialize data for processing
90187712Sraj */
91187712Srajstatic int
92187712Srajfg_start(
93187712Sraj	int unit,
94187712Sraj	struct peer *peer
95187712Sraj	)
96187712Sraj{
97187712Sraj	struct refclockproc *pp;
98187712Sraj	struct fgunit *up;
99187712Sraj	int fd;
100187712Sraj	char device[20];
101187712Sraj
102187712Sraj
103187712Sraj	/*
104187712Sraj	 * Open device file for reading.
105187712Sraj	 */
106187712Sraj	snprintf(device, sizeof(device), DEVICE, unit);
107187712Sraj
108187712Sraj	DPRINTF(1, ("starting FG with device %s\n",device));
109187712Sraj
110187712Sraj	fd = refclock_open(device, SPEED232, LDISC_CLK);
111187712Sraj	if (fd <= 0)
112187712Sraj		return (0);
113187712Sraj
114187712Sraj	/*
115187712Sraj	 * Allocate and initialize unit structure
116187712Sraj	 */
117187712Sraj
118187712Sraj	up = emalloc(sizeof(struct fgunit));
119187712Sraj	memset(up, 0, sizeof(struct fgunit));
120187712Sraj	pp = peer->procptr;
121187712Sraj	pp->unitptr = up;
122187712Sraj	pp->io.clock_recv = fg_receive;
123187712Sraj	pp->io.srcclock = peer;
124187712Sraj	pp->io.datalen = 0;
125187712Sraj	pp->io.fd = fd;
126187712Sraj 	if (!io_addclock(&pp->io)) {
127187712Sraj		close(fd);
128187712Sraj		pp->io.fd = -1;
129187712Sraj		return 0;
130187712Sraj	}
131187712Sraj
132187712Sraj
133187712Sraj	/*
134187712Sraj	 * Initialize miscellaneous variables
135187712Sraj	 */
136187712Sraj	peer->precision = PRECISION;
137187712Sraj	pp->clockdesc = DESCRIPTION;
138187712Sraj	memcpy(&pp->refid, REFID, 3);
139187712Sraj	up->pollnum = 0;
140187712Sraj
141187712Sraj	/*
142187712Sraj	 * Setup dating station to use GPS receiver.
143187712Sraj	 * GPS receiver should work before this operation.
144187712Sraj	 */
145276876Sloos	if(!fg_init(pp->io.fd))
146187712Sraj		refclock_report(peer, CEVNT_FAULT);
147187712Sraj
148187712Sraj	return (1);
149187712Sraj}
150187712Sraj
151187712Sraj
152187712Sraj/*
153187712Sraj * fg_shutdown - shut down the clock
154276876Sloos */
155187712Srajstatic void
156187712Srajfg_shutdown(
157187712Sraj	int unit,
158187712Sraj	struct peer *peer
159187712Sraj	)
160187712Sraj{
161187712Sraj	struct refclockproc *pp;
162187712Sraj	struct fgunit *up;
163187712Sraj
164187712Sraj	pp = peer->procptr;
165187712Sraj	up = pp->unitptr;
166187712Sraj	if (pp->io.fd != -1)
167187712Sraj		io_closeclock(&pp->io);
168187712Sraj	if (up != NULL)
169187712Sraj		free(up);
170187712Sraj}
171187712Sraj
172187712Sraj
173187712Sraj/*
174187712Sraj * fg_poll - called by the transmit procedure
175187712Sraj */
176187712Srajstatic void
177187712Srajfg_poll(
178187712Sraj	int unit,
179187712Sraj	struct peer *peer
180187712Sraj	)
181187712Sraj{
182187712Sraj	struct refclockproc *pp;
183187712Sraj
184187712Sraj	pp = peer->procptr;
185187712Sraj
186187712Sraj	/*
187187712Sraj	 * Time to poll the clock. The FG clock responds to a
188187712Sraj	 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
189187712Sraj	 * above. If nothing is heard from the clock for two polls,
190187712Sraj	 * declare a timeout and keep going.
191187712Sraj	 */
192187712Sraj
193187712Sraj	if (write(pp->io.fd, fgdate, LENFG) != LENFG)
194187712Sraj		refclock_report(peer, CEVNT_FAULT);
195187712Sraj	else
196187712Sraj		pp->polls++;
197187712Sraj
198187712Sraj	/*
199187712Sraj	if (pp->coderecv == pp->codeproc) {
200187712Sraj		refclock_report(peer, CEVNT_TIMEOUT);
201187712Sraj		return;
202187712Sraj	}
203187712Sraj	*/
204187712Sraj
205187712Sraj	record_clock_stats(&peer->srcadr, pp->a_lastcode);
206187712Sraj
207187712Sraj	return;
208187712Sraj
209187712Sraj}
210187712Sraj
211187712Sraj/*
212187712Sraj * fg_receive - receive data from the serial interface
213187712Sraj */
214187712Srajstatic void
215187712Srajfg_receive(
216187712Sraj	struct recvbuf *rbufp
217187712Sraj	)
218187712Sraj{
219187712Sraj	struct refclockproc *pp;
220187712Sraj	struct fgunit *up;
221187712Sraj	struct peer *peer;
222187712Sraj	char *bpt;
223187712Sraj
224187712Sraj	/*
225187712Sraj	 * Initialize pointers and read the timecode and timestamp
226187712Sraj	 * We can't use gtlin function because we need bynary data in buf */
227187712Sraj
228187712Sraj	peer = rbufp->recv_peer;
229187712Sraj	pp = peer->procptr;
230187712Sraj	up = pp->unitptr;
231187712Sraj
232187712Sraj	/*
233187712Sraj	 * Below hug to implement receiving of status information
234187712Sraj	 */
235187712Sraj	if(!up->pollnum) {
236187712Sraj		up->pollnum++;
237187712Sraj		return;
238187712Sraj	}
239187712Sraj
240187712Sraj
241187712Sraj	if (rbufp->recv_length < (LENFG - 2)) {
242187712Sraj		refclock_report(peer, CEVNT_BADREPLY);
243187712Sraj		return; /* The reply is invalid discard it. */
244187712Sraj	}
245187712Sraj
246187712Sraj	/* Below I trying to find a correct reply in buffer.
247187712Sraj	 * Sometime GPS reply located in the beginnig of buffer,
248187712Sraj	 * sometime you can find it with some offset.
249187712Sraj	 */
250187712Sraj
251187712Sraj	bpt = (char *)rbufp->recv_space.X_recv_buffer;
252187712Sraj	while (*bpt != '\x10')
253187712Sraj		bpt++;
254187712Sraj
255187712Sraj#define BP2(x) ( bpt[x] & 15 )
256187712Sraj#define BP1(x) (( bpt[x] & 240 ) >> 4)
257187712Sraj
258187712Sraj	pp->year = BP1(2) * 10 + BP2(2);
259187712Sraj
260187712Sraj	if (pp->year == 94) {
261187712Sraj		refclock_report(peer, CEVNT_BADREPLY);
262187712Sraj		if (!fg_init(pp->io.fd))
263187712Sraj			refclock_report(peer, CEVNT_FAULT);
264187712Sraj		return;
265187712Sraj		 /* GPS is just powered up. The date is invalid -
266187712Sraj		 discarding it. Initilize GPS one more time */
267187712Sraj		/* Sorry - this driver will broken in 2094 ;) */
268187712Sraj	}
269187712Sraj
270187712Sraj	if (pp->year < 99)
271187712Sraj		pp->year += 100;
272187712Sraj
273187712Sraj	pp->year +=  1900;
274187712Sraj	pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
275187712Sraj
276187712Sraj/*
277187712Sraj   After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
278187712Sraj   benahour. It doubles day number for an hours in replys after 10:10:10 UTC
279187712Sraj   and doubles min every hour at HH:10:ss for a minute.
280187712Sraj   Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
281187712Sraj   Below small code to avoid such situation.
282187712Sraj*/
283187712Sraj	if (up->y2kwarn > 10)
284187712Sraj		pp->hour = BP1(6)*10 + BP2(6);
285187712Sraj	else
286187712Sraj		pp->hour = BP1(5)*10 + BP2(5);
287187712Sraj
288187712Sraj	if ((up->y2kwarn > 10) && (pp->hour == 10)) {
289187712Sraj		pp->minute = BP1(7)*10 + BP2(7);
290187712Sraj		pp->second = BP1(8)*10 + BP2(8);
291187712Sraj		pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
292187712Sraj		pp->nsec += BP1(10) * 1000;
293187712Sraj	} else {
294187712Sraj		pp->hour = BP1(5)*10 + BP2(5);
295187712Sraj		pp->minute = BP1(6)*10 + BP2(6);
296187712Sraj		pp->second = BP1(7)*10 + BP2(7);
297187712Sraj		pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
298187712Sraj		pp->nsec += BP1(9) * 1000;
299187712Sraj	}
300187712Sraj
301187712Sraj	if ((pp->hour == 10) && (pp->minute == 10)) {
302187712Sraj		up->y2kwarn++;
303187712Sraj	}
304187712Sraj
305187712Sraj	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
306187712Sraj		 "%d %d %d %d %d", pp->year, pp->day, pp->hour,
307187712Sraj		 pp->minute, pp->second);
308187712Sraj	pp->lencode = strlen(pp->a_lastcode);
309187712Sraj	/*get_systime(&pp->lastrec);*/
310187712Sraj
311187712Sraj#ifdef DEBUG
312187712Sraj	if (debug)
313187712Sraj		printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
314187712Sraj		       pp->year, pp->day, pp->hour, pp->minute, pp->second);
315187712Sraj#endif
316187712Sraj	pp->disp =  (10e-6);
317187712Sraj	pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
318187712Sraj	/* pp->leap = LEAP_NOWARNING; */
319187712Sraj
320187712Sraj	/*
321187712Sraj	 * Process the new sample in the median filter and determine the
322187712Sraj	 * timecode timestamp.
323187712Sraj	 */
324187712Sraj
325187712Sraj	if (!refclock_process(pp))
326187712Sraj		refclock_report(peer, CEVNT_BADTIME);
327187712Sraj	pp->lastref = pp->lastrec;
328187712Sraj	refclock_receive(peer);
329187712Sraj	return;
330187712Sraj}
331187712Sraj
332187712Sraj
333187712Sraj#else
334187712Srajint refclock_fg_bs;
335187712Sraj#endif /* REFCLOCK */
336187712Sraj