1 /* $NetBSD: refclock_fg.c,v 1.3 2003/12/04 16:23:37 drochner Exp $ */
4 * refclock_fg - clock driver for the Forum Graphic GPS datating station
11 #if defined(REFCLOCK) && defined(CLOCK_FG)
15 #include "ntp_refclock.h"
16 #include "ntp_calendar.h"
17 #include "ntp_stdlib.h"
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.
26 * Interface definitions
28 #define DEVICE "/dev/fgclock%d"
29 #define PRECISION (-10) /* precision assumed (about 1 ms) */
31 #define DESCRIPTION "Forum Graphic GPS dating station"
32 #define LENFG 26 /* timecode length */
33 #define SPEED232 B9600 /* uart speed (9600 baud) */
38 static int fg_init
P((int));
39 static int fg_start
P((int, struct peer
*));
40 static void fg_shutdown
P((int, struct peer
*));
41 static void fg_poll
P((int, struct peer
*));
42 static void fg_receive
P((struct recvbuf
*));
45 * Forum Graphic unit control structure
49 int pollnum
; /* Use peer.poll instead? */
50 int status
; /* Hug to check status information on GPS */
51 int y2kwarn
; /* Y2K bug */
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 };
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 */
76 * fg_init - Initialization of FG GPS.
84 if (write(fd
, fginit
, LENFG
) != LENFG
)
92 * fg_start - open the device and initialize data for processing
100 struct refclockproc
*pp
;
107 * Open device file for reading.
109 (void)sprintf(device
, DEVICE
, unit
);
113 printf ("starting FG with device %s\n",device
);
115 if (!(fd
= refclock_open(device
, SPEED232
, LDISC_CLK
)))
119 * Allocate and initialize unit structure
122 if (!(up
= (struct fgunit
*)
123 emalloc(sizeof(struct fgunit
)))) {
127 memset((char *)up
, 0, sizeof(struct fgunit
));
129 pp
->unitptr
= (caddr_t
)up
;
130 pp
->io
.clock_recv
= fg_receive
;
131 pp
->io
.srcclock
= (caddr_t
)peer
;
134 if (!io_addclock(&pp
->io
)) {
141 * Initialize miscellaneous variables
143 peer
->precision
= PRECISION
;
144 pp
->clockdesc
= DESCRIPTION
;
145 memcpy((char *)&pp
->refid
, REFID
, 3);
149 * Setup dating station to use GPS receiver.
150 * GPS receiver should work before this operation.
152 if(!fg_init(pp
->io
.fd
))
153 refclock_report(peer
, CEVNT_FAULT
);
160 * fg_shutdown - shut down the clock
168 struct refclockproc
*pp
;
172 up
= (struct fgunit
*)pp
->unitptr
;
173 io_closeclock(&pp
->io
);
179 * fg_poll - called by the transmit procedure
187 struct refclockproc
*pp
;
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.
198 if (write(pp
->io
.fd
, fgdate
, LENFG
) != LENFG
)
199 refclock_report(peer
, CEVNT_FAULT
);
206 if (pp->coderecv == pp->codeproc) {
207 refclock_report(peer, CEVNT_TIMEOUT);
211 peer
->burst
= NSTAGE
;
213 record_clock_stats(&peer
->srcadr
, pp
->a_lastcode
);
221 * fg_receive - receive data from the serial interface
225 struct recvbuf
*rbufp
228 struct refclockproc
*pp
;
234 * Initialize pointers and read the timecode and timestamp
235 * We can't use gtlin function because we need bynary data in buf */
237 peer
= (struct peer
*)rbufp
->recv_srcclock
;
239 up
= (struct fgunit
*)pp
->unitptr
;
242 * Below hug to implement receiving of status information
251 if (rbufp
->recv_length
< (LENFG
-2))
253 refclock_report(peer
, CEVNT_BADREPLY
);
254 return; /* The reply is invalid discard it. */
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.
262 bpt
= (char *)rbufp
->recv_space
.X_recv_buffer
;
263 while(*bpt
!= '\x10')
266 #define BP2(x) ( bpt[x] & 15 )
267 #define BP1(x) (( bpt[x] & 240 ) >> 4)
269 pp
->year
= BP1(2)*10 + BP2(2);
273 refclock_report(peer
, CEVNT_BADREPLY
);
274 if(!fg_init(pp
->io
.fd
))
275 refclock_report(peer
, CEVNT_FAULT
);
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 ;) */
286 pp
->day
= 100 * BP2(3) + 10 * BP1(4) + BP2(4);
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.
296 pp
->hour
= BP1(6)*10 + BP2(6);
298 pp
->hour
= BP1(5)*10 + BP2(5);
300 if((up
->y2kwarn
> 10) && (pp
->hour
== 10))
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;
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;
314 if((pp
->hour
== 10) && (pp
->minute
== 10))
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);*/
325 printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
326 pp
->year
, pp
->day
, pp
->hour
, pp
->minute
, pp
->second
);
329 pp
->lastrec
= rbufp
->recv_time
; /* Is it better than get_systime()? */
330 /* pp->leap = LEAP_NOWARNING; */
333 * Process the new sample in the median filter and determine the
334 * timecode timestamp.
337 if (!refclock_process(pp
))
338 refclock_report(peer
, CEVNT_BADTIME
);
339 pp
->lastref
= pp
->lastrec
;
340 refclock_receive(peer
);
347 #endif /* REFCLOCK */