156746Sroberto/*
256746Sroberto * refclock_fg - clock driver for the Forum Graphic GPS datating station
356746Sroberto */
456746Sroberto
556746Sroberto#ifdef HAVE_CONFIG_H
656746Sroberto# include <config.h>
756746Sroberto#endif
856746Sroberto
956746Sroberto#if defined(REFCLOCK) && defined(CLOCK_FG)
1056746Sroberto
1156746Sroberto#include "ntpd.h"
1256746Sroberto#include "ntp_io.h"
1356746Sroberto#include "ntp_refclock.h"
1456746Sroberto#include "ntp_calendar.h"
1556746Sroberto#include "ntp_stdlib.h"
1656746Sroberto
1756746Sroberto/*
1856746Sroberto * This driver supports the Forum Graphic GPS dating station.
1956746Sroberto * More information about FG GPS is available on http://www.forumgraphic.com
2056746Sroberto * Contact das@amt.ru for any question about this driver.
2156746Sroberto */
2256746Sroberto
2356746Sroberto/*
2456746Sroberto * Interface definitions
2556746Sroberto */
2656746Sroberto#define	DEVICE		"/dev/fgclock%d"
2756746Sroberto#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
2856746Sroberto#define REFID		"GPS"
2956746Sroberto#define DESCRIPTION	"Forum Graphic GPS dating station"
3056746Sroberto#define LENFG		26	/* timecode length */
3156746Sroberto#define SPEED232        B9600   /* uart speed (9600 baud) */
3256746Sroberto
3356746Sroberto/*
3456746Sroberto * Function prototypes
3556746Sroberto */
36290001Sglebiusstatic	int 	fg_init 	(int);
37290001Sglebiusstatic	int 	fg_start 	(int, struct peer *);
38290001Sglebiusstatic	void	fg_shutdown	(int, struct peer *);
39290001Sglebiusstatic	void	fg_poll		(int, struct peer *);
40290001Sglebiusstatic	void	fg_receive	(struct recvbuf *);
4156746Sroberto
4256746Sroberto/*
4356746Sroberto * Forum Graphic unit control structure
4456746Sroberto */
4556746Sroberto
4656746Srobertostruct fgunit {
47290001Sglebius	int pollnum;	/* Use peer.poll instead? */
48290001Sglebius	int status; 	/* Hug to check status information on GPS */
49290001Sglebius	int y2kwarn;	/* Y2K bug */
5056746Sroberto};
5156746Sroberto
5256746Sroberto/*
5356746Sroberto * Queries definition
5456746Sroberto */
5556746Srobertostatic char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5656746Sroberto0, 0, 0, 0, 0, 0, 0, 0, 0 };
5756746Srobertostatic char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
5856746Sroberto0, 0, 0, 0, 0, 0, 0, 0, 0 };
5956746Sroberto
6056746Sroberto/*
6156746Sroberto * Transfer vector
6256746Sroberto */
6356746Srobertostruct  refclock refclock_fg = {
64290001Sglebius	fg_start,		/* start up driver */
65290001Sglebius	fg_shutdown,		/* shut down driver */
66290001Sglebius	fg_poll,		/* transmit poll message */
67290001Sglebius	noentry,		/* not used */
68290001Sglebius	noentry,		/* initialize driver (not used) */
69290001Sglebius	noentry,		/* not used */
70290001Sglebius	NOFLAGS			/* not used */
7156746Sroberto};
7256746Sroberto
7356746Sroberto/*
7456746Sroberto * fg_init - Initialization of FG GPS.
7556746Sroberto */
7656746Sroberto
7756746Srobertostatic int
7856746Srobertofg_init(
79290001Sglebius	int fd
80290001Sglebius	)
8156746Sroberto{
8256746Sroberto	if (write(fd, fginit, LENFG) != LENFG)
83290001Sglebius		return 0;
8456746Sroberto
85290001Sglebius	return 1;
8656746Sroberto}
8756746Sroberto
8856746Sroberto/*
8956746Sroberto * fg_start - open the device and initialize data for processing
9056746Sroberto */
9156746Srobertostatic int
9256746Srobertofg_start(
93290001Sglebius	int unit,
9456746Sroberto	struct peer *peer
9556746Sroberto	)
9656746Sroberto{
9756746Sroberto	struct refclockproc *pp;
9856746Sroberto	struct fgunit *up;
9956746Sroberto	int fd;
10056746Sroberto	char device[20];
10156746Sroberto
10256746Sroberto
10356746Sroberto	/*
10456746Sroberto	 * Open device file for reading.
10556746Sroberto	 */
106290001Sglebius	snprintf(device, sizeof(device), DEVICE, unit);
10756746Sroberto
108290001Sglebius	DPRINTF(1, ("starting FG with device %s\n",device));
109290001Sglebius
110290001Sglebius	fd = refclock_open(device, SPEED232, LDISC_CLK);
111290001Sglebius	if (fd <= 0)
112290001Sglebius		return (0);
11356746Sroberto
114290001Sglebius	/*
115290001Sglebius	 * Allocate and initialize unit structure
116290001Sglebius	 */
11756746Sroberto
118290001Sglebius	up = emalloc(sizeof(struct fgunit));
119290001Sglebius	memset(up, 0, sizeof(struct fgunit));
12056746Sroberto	pp = peer->procptr;
121290001Sglebius	pp->unitptr = up;
12256746Sroberto	pp->io.clock_recv = fg_receive;
123290001Sglebius	pp->io.srcclock = peer;
12456746Sroberto	pp->io.datalen = 0;
12556746Sroberto	pp->io.fd = fd;
12656746Sroberto 	if (!io_addclock(&pp->io)) {
127290001Sglebius		close(fd);
128290001Sglebius		pp->io.fd = -1;
129290001Sglebius		return 0;
130290001Sglebius	}
13156746Sroberto
13256746Sroberto
13356746Sroberto	/*
13456746Sroberto	 * Initialize miscellaneous variables
13556746Sroberto	 */
13656746Sroberto	peer->precision = PRECISION;
13756746Sroberto	pp->clockdesc = DESCRIPTION;
138290001Sglebius	memcpy(&pp->refid, REFID, 3);
13956746Sroberto	up->pollnum = 0;
14056746Sroberto
14156746Sroberto	/*
14256746Sroberto	 * Setup dating station to use GPS receiver.
14356746Sroberto	 * GPS receiver should work before this operation.
144290001Sglebius	 */
14556746Sroberto	if(!fg_init(pp->io.fd))
14656746Sroberto		refclock_report(peer, CEVNT_FAULT);
14756746Sroberto
14856746Sroberto	return (1);
14956746Sroberto}
15056746Sroberto
15156746Sroberto
15256746Sroberto/*
15356746Sroberto * fg_shutdown - shut down the clock
15456746Sroberto */
15556746Srobertostatic void
15656746Srobertofg_shutdown(
15756746Sroberto	int unit,
15856746Sroberto	struct peer *peer
15956746Sroberto	)
16056746Sroberto{
16156746Sroberto	struct refclockproc *pp;
16256746Sroberto	struct fgunit *up;
16356746Sroberto
16456746Sroberto	pp = peer->procptr;
165290001Sglebius	up = pp->unitptr;
166290001Sglebius	if (pp->io.fd != -1)
167290001Sglebius		io_closeclock(&pp->io);
168290001Sglebius	if (up != NULL)
169290001Sglebius		free(up);
17056746Sroberto}
17156746Sroberto
17256746Sroberto
17356746Sroberto/*
17456746Sroberto * fg_poll - called by the transmit procedure
17556746Sroberto */
17656746Srobertostatic void
17756746Srobertofg_poll(
17856746Sroberto	int unit,
17956746Sroberto	struct peer *peer
18056746Sroberto	)
18156746Sroberto{
18256746Sroberto	struct refclockproc *pp;
18356746Sroberto
18456746Sroberto	pp = peer->procptr;
18556746Sroberto
186290001Sglebius	/*
187290001Sglebius	 * Time to poll the clock. The FG clock responds to a
188290001Sglebius	 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
189290001Sglebius	 * above. If nothing is heard from the clock for two polls,
190290001Sglebius	 * declare a timeout and keep going.
191290001Sglebius	 */
19256746Sroberto
19356746Sroberto	if (write(pp->io.fd, fgdate, LENFG) != LENFG)
194290001Sglebius		refclock_report(peer, CEVNT_FAULT);
195290001Sglebius	else
196290001Sglebius		pp->polls++;
19756746Sroberto
19856746Sroberto	/*
199290001Sglebius	if (pp->coderecv == pp->codeproc) {
200290001Sglebius		refclock_report(peer, CEVNT_TIMEOUT);
201290001Sglebius		return;
202290001Sglebius	}
20356746Sroberto	*/
20456746Sroberto
205290001Sglebius	record_clock_stats(&peer->srcadr, pp->a_lastcode);
20656746Sroberto
20756746Sroberto	return;
20856746Sroberto
20956746Sroberto}
21056746Sroberto
21156746Sroberto/*
21256746Sroberto * fg_receive - receive data from the serial interface
21356746Sroberto */
21456746Srobertostatic void
21556746Srobertofg_receive(
216290001Sglebius	struct recvbuf *rbufp
217290001Sglebius	)
21856746Sroberto{
219290001Sglebius	struct refclockproc *pp;
22056746Sroberto	struct fgunit *up;
221290001Sglebius	struct peer *peer;
22256746Sroberto	char *bpt;
22356746Sroberto
224290001Sglebius	/*
225290001Sglebius	 * Initialize pointers and read the timecode and timestamp
22656746Sroberto	 * We can't use gtlin function because we need bynary data in buf */
22756746Sroberto
228290001Sglebius	peer = rbufp->recv_peer;
229290001Sglebius	pp = peer->procptr;
230290001Sglebius	up = pp->unitptr;
23156746Sroberto
23256746Sroberto	/*
233290001Sglebius	 * Below hug to implement receiving of status information
234290001Sglebius	 */
235290001Sglebius	if(!up->pollnum) {
23656746Sroberto		up->pollnum++;
23756746Sroberto		return;
23856746Sroberto	}
23956746Sroberto
24056746Sroberto
241290001Sglebius	if (rbufp->recv_length < (LENFG - 2)) {
24256746Sroberto		refclock_report(peer, CEVNT_BADREPLY);
243290001Sglebius		return; /* The reply is invalid discard it. */
24456746Sroberto	}
24556746Sroberto
24656746Sroberto	/* Below I trying to find a correct reply in buffer.
24756746Sroberto	 * Sometime GPS reply located in the beginnig of buffer,
24856746Sroberto	 * sometime you can find it with some offset.
24956746Sroberto	 */
25056746Sroberto
25156746Sroberto	bpt = (char *)rbufp->recv_space.X_recv_buffer;
252290001Sglebius	while (*bpt != '\x10')
25356746Sroberto		bpt++;
25456746Sroberto
25556746Sroberto#define BP2(x) ( bpt[x] & 15 )
25656746Sroberto#define BP1(x) (( bpt[x] & 240 ) >> 4)
25756746Sroberto
258290001Sglebius	pp->year = BP1(2) * 10 + BP2(2);
25956746Sroberto
260290001Sglebius	if (pp->year == 94) {
26156746Sroberto		refclock_report(peer, CEVNT_BADREPLY);
262290001Sglebius		if (!fg_init(pp->io.fd))
26356746Sroberto			refclock_report(peer, CEVNT_FAULT);
264290001Sglebius		return;
26556746Sroberto		 /* GPS is just powered up. The date is invalid -
26656746Sroberto		 discarding it. Initilize GPS one more time */
26756746Sroberto		/* Sorry - this driver will broken in 2094 ;) */
26856746Sroberto	}
26956746Sroberto
27056746Sroberto	if (pp->year < 99)
271290001Sglebius		pp->year += 100;
27256746Sroberto
273290001Sglebius	pp->year +=  1900;
274290001Sglebius	pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
27556746Sroberto
27656746Sroberto/*
27756746Sroberto   After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
27856746Sroberto   benahour. It doubles day number for an hours in replys after 10:10:10 UTC
27956746Sroberto   and doubles min every hour at HH:10:ss for a minute.
28056746Sroberto   Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
28156746Sroberto   Below small code to avoid such situation.
28256746Sroberto*/
283290001Sglebius	if (up->y2kwarn > 10)
284290001Sglebius		pp->hour = BP1(6)*10 + BP2(6);
28556746Sroberto	else
286290001Sglebius		pp->hour = BP1(5)*10 + BP2(5);
28756746Sroberto
288290001Sglebius	if ((up->y2kwarn > 10) && (pp->hour == 10)) {
289290001Sglebius		pp->minute = BP1(7)*10 + BP2(7);
290290001Sglebius		pp->second = BP1(8)*10 + BP2(8);
291290001Sglebius		pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
292290001Sglebius		pp->nsec += BP1(10) * 1000;
29356746Sroberto	} else {
294290001Sglebius		pp->hour = BP1(5)*10 + BP2(5);
295290001Sglebius		pp->minute = BP1(6)*10 + BP2(6);
296290001Sglebius		pp->second = BP1(7)*10 + BP2(7);
297290001Sglebius		pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
298290001Sglebius		pp->nsec += BP1(9) * 1000;
29956746Sroberto	}
300290001Sglebius
301290001Sglebius	if ((pp->hour == 10) && (pp->minute == 10)) {
30256746Sroberto		up->y2kwarn++;
30356746Sroberto	}
30456746Sroberto
305290001Sglebius	snprintf(pp->a_lastcode, sizeof(pp->a_lastcode),
306290001Sglebius		 "%d %d %d %d %d", pp->year, pp->day, pp->hour,
307290001Sglebius		 pp->minute, pp->second);
30856746Sroberto	pp->lencode = strlen(pp->a_lastcode);
309290001Sglebius	/*get_systime(&pp->lastrec);*/
31056746Sroberto
31156746Sroberto#ifdef DEBUG
312290001Sglebius	if (debug)
313290001Sglebius		printf("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
314290001Sglebius		       pp->year, pp->day, pp->hour, pp->minute, pp->second);
31556746Sroberto#endif
316290001Sglebius	pp->disp =  (10e-6);
317132451Sroberto	pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
31856746Sroberto	/* pp->leap = LEAP_NOWARNING; */
31956746Sroberto
320290001Sglebius	/*
321290001Sglebius	 * Process the new sample in the median filter and determine the
322290001Sglebius	 * timecode timestamp.
323290001Sglebius	 */
32456746Sroberto
325290001Sglebius	if (!refclock_process(pp))
326290001Sglebius		refclock_report(peer, CEVNT_BADTIME);
327290001Sglebius	pp->lastref = pp->lastrec;
32856746Sroberto	refclock_receive(peer);
32956746Sroberto	return;
33056746Sroberto}
33156746Sroberto
33256746Sroberto
33356746Sroberto#else
33456746Srobertoint refclock_fg_bs;
33556746Sroberto#endif /* REFCLOCK */
336