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