xref: /netbsd/external/bsd/ntp/dist/ntpd/refclock_fg.c (revision 6550d01e)
1 /*	$NetBSD: refclock_fg.c,v 1.1.1.1 2009/12/13 16:55:48 kardel Exp $	*/
2 
3 /*
4  * refclock_fg - clock driver for the Forum Graphic GPS datating station
5  */
6 
7 #ifdef HAVE_CONFIG_H
8 # include <config.h>
9 #endif
10 
11 #if defined(REFCLOCK) && defined(CLOCK_FG)
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  */
38 static	int 	fg_init 	(int);
39 static	int 	fg_start 	(int, struct peer *);
40 static	void	fg_shutdown	(int, struct peer *);
41 static	void	fg_poll		(int, struct peer *);
42 static  void    fg_receive	(struct recvbuf *);
43 
44 /*
45  * Forum Graphic unit control structure
46  */
47 
48 struct 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  */
57 static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
58 0, 0, 0, 0, 0, 0, 0, 0, 0 };
59 static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
60 0, 0, 0, 0, 0, 0, 0, 0, 0 };
61 
62 /*
63  * Transfer vector
64  */
65 struct  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 
79 static int
80 fg_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  */
94 static int
95 fg_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  */
162 static void
163 fg_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  */
181 static void
182 fg_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  */
223 static void
224 fg_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->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
305         	pp->nsec += BP1(10) * 1000;
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->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
311         	pp->nsec += BP1(9) * 1000;
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         pp->disp =  (10e-6);
329 	pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
330 	/* pp->leap = LEAP_NOWARNING; */
331 
332         /*
333          * Process the new sample in the median filter and determine the
334          * timecode timestamp.
335          */
336 
337         if (!refclock_process(pp))
338                 refclock_report(peer, CEVNT_BADTIME);
339         pp->lastref = pp->lastrec;
340 	refclock_receive(peer);
341 	return;
342 }
343 
344 
345 #else
346 int refclock_fg_bs;
347 #endif /* REFCLOCK */
348