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 */
3656746Srobertostatic	int 	fg_init 		P((int));
3756746Srobertostatic	int 	fg_start 		P((int, struct peer *));
3856746Srobertostatic	void	fg_shutdown		P((int, struct peer *));
3956746Srobertostatic	void	fg_poll		P((int, struct peer *));
4056746Srobertostatic  void    fg_receive     P((struct recvbuf *));
4156746Sroberto
4256746Sroberto/*
4356746Sroberto * Forum Graphic unit control structure
4456746Sroberto */
4556746Sroberto
4656746Srobertostruct fgunit {
4756746Sroberto       int pollnum;	/* Use peer.poll instead? */
4856746Sroberto       int status; 	/* Hug to check status information on GPS */
4956746Sroberto       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 = {
6456746Sroberto	fg_start,              /* start up driver */
6556746Sroberto	fg_shutdown,           /* shut down driver */
6656746Sroberto	fg_poll,               /* transmit poll message */
6756746Sroberto	noentry,                /* not used */
6856746Sroberto	noentry,                /* initialize driver (not used) */
6956746Sroberto	noentry,                /* not used */
7056746Sroberto	NOFLAGS                 /* not used */
7156746Sroberto};
7256746Sroberto
7356746Sroberto/*
7456746Sroberto * fg_init - Initialization of FG GPS.
7556746Sroberto */
7656746Sroberto
7756746Srobertostatic int
7856746Srobertofg_init(
7956746Sroberto       int fd
8056746Sroberto       )
8156746Sroberto{
8256746Sroberto	if (write(fd, fginit, LENFG) != LENFG)
8356746Sroberto                return 0;
8456746Sroberto
8556746Sroberto	return (1);
8656746Sroberto
8756746Sroberto}
8856746Sroberto
8956746Sroberto/*
9056746Sroberto * fg_start - open the device and initialize data for processing
9156746Sroberto */
9256746Srobertostatic int
9356746Srobertofg_start(
9456746Sroberto     	int unit,
9556746Sroberto	struct peer *peer
9656746Sroberto	)
9756746Sroberto{
9856746Sroberto	struct refclockproc *pp;
9956746Sroberto	struct fgunit *up;
10056746Sroberto	int fd;
10156746Sroberto	char device[20];
10256746Sroberto
10356746Sroberto
10456746Sroberto	/*
10556746Sroberto	 * Open device file for reading.
10656746Sroberto	 */
10756746Sroberto	(void)sprintf(device, DEVICE, unit);
10856746Sroberto
10956746Sroberto#ifdef DEBUG
11056746Sroberto	if (debug)
11156746Sroberto		printf ("starting FG with device %s\n",device);
11256746Sroberto#endif
11356746Sroberto	 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
11456746Sroberto                return (0);
11556746Sroberto
11656746Sroberto        /*
11756746Sroberto         * Allocate and initialize unit structure
11856746Sroberto         */
11956746Sroberto
12056746Sroberto	if (!(up = (struct fgunit *)
12156746Sroberto              emalloc(sizeof(struct fgunit)))) {
12256746Sroberto                (void) close(fd);
12356746Sroberto                return (0);
12456746Sroberto        }
12556746Sroberto	memset((char *)up, 0, sizeof(struct fgunit));
12656746Sroberto	pp = peer->procptr;
12756746Sroberto	pp->unitptr = (caddr_t)up;
12856746Sroberto	pp->io.clock_recv = fg_receive;
12956746Sroberto	pp->io.srcclock = (caddr_t)peer;
13056746Sroberto	pp->io.datalen = 0;
13156746Sroberto	pp->io.fd = fd;
13256746Sroberto 	if (!io_addclock(&pp->io)) {
13356746Sroberto                (void) close(fd);
13456746Sroberto                return (0);
13556746Sroberto        }
13656746Sroberto
13756746Sroberto
13856746Sroberto	/*
13956746Sroberto	 * Initialize miscellaneous variables
14056746Sroberto	 */
14156746Sroberto	peer->precision = PRECISION;
14256746Sroberto	pp->clockdesc = DESCRIPTION;
14356746Sroberto	memcpy((char *)&pp->refid, REFID, 3);
14456746Sroberto	up->pollnum = 0;
14556746Sroberto
14656746Sroberto	/*
14756746Sroberto	 * Setup dating station to use GPS receiver.
14856746Sroberto	 * GPS receiver should work before this operation.
14956746Sroberto         */
15056746Sroberto	if(!fg_init(pp->io.fd))
15156746Sroberto		refclock_report(peer, CEVNT_FAULT);
15256746Sroberto
15356746Sroberto	return (1);
15456746Sroberto}
15556746Sroberto
15656746Sroberto
15756746Sroberto/*
15856746Sroberto * fg_shutdown - shut down the clock
15956746Sroberto */
16056746Srobertostatic void
16156746Srobertofg_shutdown(
16256746Sroberto	int unit,
16356746Sroberto	struct peer *peer
16456746Sroberto	)
16556746Sroberto{
16656746Sroberto	struct refclockproc *pp;
16756746Sroberto	struct fgunit *up;
16856746Sroberto
16956746Sroberto	pp = peer->procptr;
17056746Sroberto	up = (struct fgunit *)pp->unitptr;
17156746Sroberto        io_closeclock(&pp->io);
17256746Sroberto	free(up);
17356746Sroberto}
17456746Sroberto
17556746Sroberto
17656746Sroberto/*
17756746Sroberto * fg_poll - called by the transmit procedure
17856746Sroberto */
17956746Srobertostatic void
18056746Srobertofg_poll(
18156746Sroberto	int unit,
18256746Sroberto	struct peer *peer
18356746Sroberto	)
18456746Sroberto{
18556746Sroberto	struct refclockproc *pp;
18656746Sroberto
18756746Sroberto	pp = peer->procptr;
18856746Sroberto
18956746Sroberto	 /*
19056746Sroberto         * Time to poll the clock. The FG clock responds to a
19156746Sroberto         * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
19256746Sroberto         * above. If nothing is heard from the clock for two polls,
19356746Sroberto         * declare a timeout and keep going.
19456746Sroberto         */
19556746Sroberto
19656746Sroberto	if (write(pp->io.fd, fgdate, LENFG) != LENFG)
19756746Sroberto                refclock_report(peer, CEVNT_FAULT);
19856746Sroberto        else
19956746Sroberto                pp->polls++;
20056746Sroberto
20156746Sroberto        if (peer->burst > 0)
20256746Sroberto                return;
20356746Sroberto	/*
20456746Sroberto        if (pp->coderecv == pp->codeproc) {
20556746Sroberto                refclock_report(peer, CEVNT_TIMEOUT);
20656746Sroberto                return;
20756746Sroberto        }
20856746Sroberto	*/
20956746Sroberto        peer->burst = NSTAGE;
21056746Sroberto
21156746Sroberto        record_clock_stats(&peer->srcadr, pp->a_lastcode);
21256746Sroberto
21356746Sroberto
21456746Sroberto	return;
21556746Sroberto
21656746Sroberto}
21756746Sroberto
21856746Sroberto/*
21956746Sroberto * fg_receive - receive data from the serial interface
22056746Sroberto */
22156746Srobertostatic void
22256746Srobertofg_receive(
22356746Sroberto        struct recvbuf *rbufp
22456746Sroberto        )
22556746Sroberto{
22656746Sroberto        struct refclockproc *pp;
22756746Sroberto	struct fgunit *up;
22856746Sroberto        struct peer *peer;
22956746Sroberto	char *bpt;
23056746Sroberto
23156746Sroberto        /*
23256746Sroberto         * Initialize pointers and read the timecode and timestamp
23356746Sroberto	 * We can't use gtlin function because we need bynary data in buf */
23456746Sroberto
23556746Sroberto        peer = (struct peer *)rbufp->recv_srcclock;
23656746Sroberto        pp = peer->procptr;
23756746Sroberto        up = (struct fgunit *)pp->unitptr;
23856746Sroberto
23956746Sroberto	/*
24056746Sroberto         * Below hug to implement receiving of status information
24156746Sroberto         */
24256746Sroberto	if(!up->pollnum)
24356746Sroberto	{
24456746Sroberto		up->pollnum++;
24556746Sroberto		return;
24656746Sroberto	}
24756746Sroberto
24856746Sroberto
24956746Sroberto	if (rbufp->recv_length < (LENFG-2))
25056746Sroberto	{
25156746Sroberto		refclock_report(peer, CEVNT_BADREPLY);
25256746Sroberto            	return; /* The reply is invalid discard it. */
25356746Sroberto	}
25456746Sroberto
25556746Sroberto	/* Below I trying to find a correct reply in buffer.
25656746Sroberto	 * Sometime GPS reply located in the beginnig of buffer,
25756746Sroberto	 * sometime you can find it with some offset.
25856746Sroberto	 */
25956746Sroberto
26056746Sroberto	bpt = (char *)rbufp->recv_space.X_recv_buffer;
26156746Sroberto	while(*bpt != '')
26256746Sroberto		bpt++;
26356746Sroberto
26456746Sroberto#define BP2(x) ( bpt[x] & 15 )
26556746Sroberto#define BP1(x) (( bpt[x] & 240 ) >> 4)
26656746Sroberto
26756746Sroberto        pp->year = BP1(2)*10 + BP2(2);
26856746Sroberto
26956746Sroberto	if(pp->year == 94)
27056746Sroberto	{
27156746Sroberto		refclock_report(peer, CEVNT_BADREPLY);
27256746Sroberto		if(!fg_init(pp->io.fd))
27356746Sroberto			refclock_report(peer, CEVNT_FAULT);
27456746Sroberto            	return;
27556746Sroberto		 /* GPS is just powered up. The date is invalid -
27656746Sroberto		 discarding it. Initilize GPS one more time */
27756746Sroberto		/* Sorry - this driver will broken in 2094 ;) */
27856746Sroberto	}
27956746Sroberto
28056746Sroberto	if (pp->year < 99)
28156746Sroberto                pp->year += 100;
28256746Sroberto
28356746Sroberto        pp->year +=  1900;
28456746Sroberto        pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
28556746Sroberto
28656746Sroberto/*
28756746Sroberto   After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
28856746Sroberto   benahour. It doubles day number for an hours in replys after 10:10:10 UTC
28956746Sroberto   and doubles min every hour at HH:10:ss for a minute.
29056746Sroberto   Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
29156746Sroberto   Below small code to avoid such situation.
29256746Sroberto*/
29356746Sroberto	if(up->y2kwarn > 10)
29456746Sroberto        	pp->hour = BP1(6)*10 + BP2(6);
29556746Sroberto	else
29656746Sroberto        	pp->hour = BP1(5)*10 + BP2(5);
29756746Sroberto
29856746Sroberto	if((up->y2kwarn > 10) && (pp->hour == 10))
29956746Sroberto	{
30056746Sroberto        	pp->minute = BP1(7)*10 + BP2(7);
30156746Sroberto        	pp->second = BP1(8)*10 + BP2(8);
302132451Sroberto        	pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
303132451Sroberto        	pp->nsec += BP1(10) * 1000;
30456746Sroberto	} else {
30556746Sroberto        	pp->hour = BP1(5)*10 + BP2(5);
30656746Sroberto        	pp->minute = BP1(6)*10 + BP2(6);
30756746Sroberto        	pp->second = BP1(7)*10 + BP2(7);
308132451Sroberto        	pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
309132451Sroberto        	pp->nsec += BP1(9) * 1000;
31056746Sroberto	}
31156746Sroberto
31256746Sroberto	if((pp->hour == 10) && (pp->minute == 10))
31356746Sroberto	{
31456746Sroberto		up->y2kwarn++;
31556746Sroberto	}
31656746Sroberto
31756746Sroberto	sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second);
31856746Sroberto	pp->lencode = strlen(pp->a_lastcode);
31956746Sroberto        /*get_systime(&pp->lastrec);*/
32056746Sroberto
32156746Sroberto#ifdef DEBUG
32256746Sroberto        if (debug)
32356746Sroberto                printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
32456746Sroberto                         pp->year, pp->day, pp->hour, pp->minute, pp->second);
32556746Sroberto#endif
32656746Sroberto        pp->disp =  (10e-6);
327132451Sroberto	pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
32856746Sroberto	/* pp->leap = LEAP_NOWARNING; */
32956746Sroberto
33056746Sroberto        /*
33156746Sroberto         * Process the new sample in the median filter and determine the
33256746Sroberto         * timecode timestamp.
33356746Sroberto         */
33456746Sroberto
33556746Sroberto        if (!refclock_process(pp))
33656746Sroberto                refclock_report(peer, CEVNT_BADTIME);
337132451Sroberto        pp->lastref = pp->lastrec;
33856746Sroberto	refclock_receive(peer);
33956746Sroberto	return;
34056746Sroberto}
34156746Sroberto
34256746Sroberto
34356746Sroberto#else
34456746Srobertoint refclock_fg_bs;
34556746Sroberto#endif /* REFCLOCK */
346