refclock_fg.c revision 56746
1/*
2 * refclock_fg - clock driver for the Forum Graphic GPS datating station
3 */
4
5#ifdef HAVE_CONFIG_H
6# include <config.h>
7#endif
8
9#if defined(REFCLOCK) && defined(CLOCK_FG)
10
11#include <time.h>
12
13#include "ntpd.h"
14#include "ntp_io.h"
15#include "ntp_refclock.h"
16#include "ntp_calendar.h"
17#include "ntp_stdlib.h"
18
19/*
20 * This driver supports the Forum Graphic GPS dating station.
21 * More information about FG GPS is available on http://www.forumgraphic.com
22 * Contact das@amt.ru for any question about this driver.
23 */
24
25/*
26 * Interface definitions
27 */
28#define	DEVICE		"/dev/fgclock%d"
29#define	PRECISION	(-10)	/* precision assumed (about 1 ms) */
30#define REFID		"GPS"
31#define DESCRIPTION	"Forum Graphic GPS dating station"
32#define LENFG		26	/* timecode length */
33#define SPEED232        B9600   /* uart speed (9600 baud) */
34
35/*
36 * Function prototypes
37 */
38static	int 	fg_init 		P((int));
39static	int 	fg_start 		P((int, struct peer *));
40static	void	fg_shutdown		P((int, struct peer *));
41static	void	fg_poll		P((int, struct peer *));
42static  void    fg_receive     P((struct recvbuf *));
43
44/*
45 * Forum Graphic unit control structure
46 */
47
48struct fgunit {
49       int pollnum;	/* Use peer.poll instead? */
50       int status; 	/* Hug to check status information on GPS */
51       int y2kwarn;		/* Y2K bug */
52};
53
54/*
55 * Queries definition
56 */
57static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
580, 0, 0, 0, 0, 0, 0, 0, 0 };
59static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
600, 0, 0, 0, 0, 0, 0, 0, 0 };
61
62/*
63 * Transfer vector
64 */
65struct  refclock refclock_fg = {
66	fg_start,              /* start up driver */
67	fg_shutdown,           /* shut down driver */
68	fg_poll,               /* transmit poll message */
69	noentry,                /* not used */
70	noentry,                /* initialize driver (not used) */
71	noentry,                /* not used */
72	NOFLAGS                 /* not used */
73};
74
75/*
76 * fg_init - Initialization of FG GPS.
77 */
78
79static int
80fg_init(
81       int fd
82       )
83{
84	if (write(fd, fginit, LENFG) != LENFG)
85                return 0;
86
87	return (1);
88
89}
90
91/*
92 * fg_start - open the device and initialize data for processing
93 */
94static int
95fg_start(
96     	int unit,
97	struct peer *peer
98	)
99{
100	struct refclockproc *pp;
101	struct fgunit *up;
102	int fd;
103	char device[20];
104
105
106	/*
107	 * Open device file for reading.
108	 */
109	(void)sprintf(device, DEVICE, unit);
110
111#ifdef DEBUG
112	if (debug)
113		printf ("starting FG with device %s\n",device);
114#endif
115	 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
116                return (0);
117
118        /*
119         * Allocate and initialize unit structure
120         */
121
122	if (!(up = (struct fgunit *)
123              emalloc(sizeof(struct fgunit)))) {
124                (void) close(fd);
125                return (0);
126        }
127	memset((char *)up, 0, sizeof(struct fgunit));
128	pp = peer->procptr;
129	pp->unitptr = (caddr_t)up;
130	pp->io.clock_recv = fg_receive;
131	pp->io.srcclock = (caddr_t)peer;
132	pp->io.datalen = 0;
133	pp->io.fd = fd;
134 	if (!io_addclock(&pp->io)) {
135                (void) close(fd);
136                return (0);
137        }
138
139
140	/*
141	 * Initialize miscellaneous variables
142	 */
143	peer->precision = PRECISION;
144	pp->clockdesc = DESCRIPTION;
145	memcpy((char *)&pp->refid, REFID, 3);
146	up->pollnum = 0;
147
148	/*
149	 * Setup dating station to use GPS receiver.
150	 * GPS receiver should work before this operation.
151         */
152	if(!fg_init(pp->io.fd))
153		refclock_report(peer, CEVNT_FAULT);
154
155	return (1);
156}
157
158
159/*
160 * fg_shutdown - shut down the clock
161 */
162static void
163fg_shutdown(
164	int unit,
165	struct peer *peer
166	)
167{
168	struct refclockproc *pp;
169	struct fgunit *up;
170
171	pp = peer->procptr;
172	up = (struct fgunit *)pp->unitptr;
173        io_closeclock(&pp->io);
174	free(up);
175}
176
177
178/*
179 * fg_poll - called by the transmit procedure
180 */
181static void
182fg_poll(
183	int unit,
184	struct peer *peer
185	)
186{
187	struct refclockproc *pp;
188
189	pp = peer->procptr;
190
191	 /*
192         * Time to poll the clock. The FG clock responds to a
193         * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
194         * above. If nothing is heard from the clock for two polls,
195         * declare a timeout and keep going.
196         */
197
198	if (write(pp->io.fd, fgdate, LENFG) != LENFG)
199                refclock_report(peer, CEVNT_FAULT);
200        else
201                pp->polls++;
202
203        if (peer->burst > 0)
204                return;
205	/*
206        if (pp->coderecv == pp->codeproc) {
207                refclock_report(peer, CEVNT_TIMEOUT);
208                return;
209        }
210	*/
211        peer->burst = NSTAGE;
212
213        record_clock_stats(&peer->srcadr, pp->a_lastcode);
214
215
216	return;
217
218}
219
220/*
221 * fg_receive - receive data from the serial interface
222 */
223static void
224fg_receive(
225        struct recvbuf *rbufp
226        )
227{
228        struct refclockproc *pp;
229	struct fgunit *up;
230        struct peer *peer;
231	char *bpt;
232
233        /*
234         * Initialize pointers and read the timecode and timestamp
235	 * We can't use gtlin function because we need bynary data in buf */
236
237        peer = (struct peer *)rbufp->recv_srcclock;
238        pp = peer->procptr;
239        up = (struct fgunit *)pp->unitptr;
240
241	/*
242         * Below hug to implement receiving of status information
243         */
244	if(!up->pollnum)
245	{
246		up->pollnum++;
247		return;
248	}
249
250
251	if (rbufp->recv_length < (LENFG-2))
252	{
253		refclock_report(peer, CEVNT_BADREPLY);
254            	return; /* The reply is invalid discard it. */
255	}
256
257	/* Below I trying to find a correct reply in buffer.
258	 * Sometime GPS reply located in the beginnig of buffer,
259	 * sometime you can find it with some offset.
260	 */
261
262	bpt = (char *)rbufp->recv_space.X_recv_buffer;
263	while(*bpt != '')
264		bpt++;
265
266#define BP2(x) ( bpt[x] & 15 )
267#define BP1(x) (( bpt[x] & 240 ) >> 4)
268
269        pp->year = BP1(2)*10 + BP2(2);
270
271	if(pp->year == 94)
272	{
273		refclock_report(peer, CEVNT_BADREPLY);
274		if(!fg_init(pp->io.fd))
275			refclock_report(peer, CEVNT_FAULT);
276            	return;
277		 /* GPS is just powered up. The date is invalid -
278		 discarding it. Initilize GPS one more time */
279		/* Sorry - this driver will broken in 2094 ;) */
280	}
281
282	if (pp->year < 99)
283                pp->year += 100;
284
285        pp->year +=  1900;
286        pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
287
288/*
289   After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
290   benahour. It doubles day number for an hours in replys after 10:10:10 UTC
291   and doubles min every hour at HH:10:ss for a minute.
292   Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
293   Below small code to avoid such situation.
294*/
295	if(up->y2kwarn > 10)
296        	pp->hour = BP1(6)*10 + BP2(6);
297	else
298        	pp->hour = BP1(5)*10 + BP2(5);
299
300	if((up->y2kwarn > 10) && (pp->hour == 10))
301	{
302        	pp->minute = BP1(7)*10 + BP2(7);
303        	pp->second = BP1(8)*10 + BP2(8);
304        	pp->msec = BP1(9)*10 + BP2(9);
305        	pp->usec = BP1(10);
306	} else {
307        	pp->hour = BP1(5)*10 + BP2(5);
308        	pp->minute = BP1(6)*10 + BP2(6);
309        	pp->second = BP1(7)*10 + BP2(7);
310        	pp->msec = BP1(8)*10 + BP2(8);
311        	pp->usec = BP1(9);
312	}
313
314	if((pp->hour == 10) && (pp->minute == 10))
315	{
316		up->y2kwarn++;
317	}
318
319	sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second);
320	pp->lencode = strlen(pp->a_lastcode);
321        /*get_systime(&pp->lastrec);*/
322
323#ifdef DEBUG
324        if (debug)
325                printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
326                         pp->year, pp->day, pp->hour, pp->minute, pp->second);
327#endif
328
329        if (peer->stratum <= 1)
330                peer->refid = pp->refid;
331        pp->disp =  (10e-6);
332	pp->lastrec = rbufp->recv_time; /* Is it better then get_systime()? */
333	/* pp->leap = LEAP_NOWARNING; */
334
335        /*
336         * Process the new sample in the median filter and determine the
337         * timecode timestamp.
338         */
339
340        if (!refclock_process(pp))
341                refclock_report(peer, CEVNT_BADTIME);
342
343	refclock_receive(peer);
344	return;
345}
346
347
348#else
349int refclock_fg_bs;
350#endif /* REFCLOCK */
351