2 * refclock_fg - clock driver for the Forum Graphic GPS datating station
9 #if defined(REFCLOCK) && defined(CLOCK_FG)
13 #include "ntp_refclock.h"
14 #include "ntp_calendar.h"
15 #include "ntp_stdlib.h"
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.
24 * Interface definitions
26 #define DEVICE "/dev/fgclock%d"
27 #define PRECISION (-10) /* precision assumed (about 1 ms) */
29 #define DESCRIPTION "Forum Graphic GPS dating station"
30 #define LENFG 26 /* timecode length */
31 #define SPEED232 B9600 /* uart speed (9600 baud) */
36 static int fg_init P((int));
37 static int fg_start P((int, struct peer *));
38 static void fg_shutdown P((int, struct peer *));
39 static void fg_poll P((int, struct peer *));
40 static void fg_receive P((struct recvbuf *));
43 * Forum Graphic unit control structure
47 int pollnum; /* Use peer.poll instead? */
48 int status; /* Hug to check status information on GPS */
49 int y2kwarn; /* Y2K bug */
55 static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
56 0, 0, 0, 0, 0, 0, 0, 0, 0 };
57 static char fgdate[] = { 0x10, 0x44, 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 };
63 struct 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 */
74 * fg_init - Initialization of FG GPS.
82 if (write(fd, fginit, LENFG) != LENFG)
90 * fg_start - open the device and initialize data for processing
98 struct refclockproc *pp;
105 * Open device file for reading.
107 (void)sprintf(device, DEVICE, unit);
111 printf ("starting FG with device %s\n",device);
113 if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
117 * Allocate and initialize unit structure
120 if (!(up = (struct fgunit *)
121 emalloc(sizeof(struct fgunit)))) {
125 memset((char *)up, 0, sizeof(struct fgunit));
127 pp->unitptr = (caddr_t)up;
128 pp->io.clock_recv = fg_receive;
129 pp->io.srcclock = (caddr_t)peer;
132 if (!io_addclock(&pp->io)) {
139 * Initialize miscellaneous variables
141 peer->precision = PRECISION;
142 pp->clockdesc = DESCRIPTION;
143 memcpy((char *)&pp->refid, REFID, 3);
147 * Setup dating station to use GPS receiver.
148 * GPS receiver should work before this operation.
150 if(!fg_init(pp->io.fd))
151 refclock_report(peer, CEVNT_FAULT);
158 * fg_shutdown - shut down the clock
166 struct refclockproc *pp;
170 up = (struct fgunit *)pp->unitptr;
171 io_closeclock(&pp->io);
177 * fg_poll - called by the transmit procedure
185 struct refclockproc *pp;
190 * Time to poll the clock. The FG clock responds to a
191 * "<DLE>D<DLE><CR>" by returning a timecode in the format specified
192 * above. If nothing is heard from the clock for two polls,
193 * declare a timeout and keep going.
196 if (write(pp->io.fd, fgdate, LENFG) != LENFG)
197 refclock_report(peer, CEVNT_FAULT);
204 if (pp->coderecv == pp->codeproc) {
205 refclock_report(peer, CEVNT_TIMEOUT);
209 peer->burst = NSTAGE;
211 record_clock_stats(&peer->srcadr, pp->a_lastcode);
219 * fg_receive - receive data from the serial interface
223 struct recvbuf *rbufp
226 struct refclockproc *pp;
232 * Initialize pointers and read the timecode and timestamp
233 * We can't use gtlin function because we need bynary data in buf */
235 peer = (struct peer *)rbufp->recv_srcclock;
237 up = (struct fgunit *)pp->unitptr;
240 * Below hug to implement receiving of status information
249 if (rbufp->recv_length < (LENFG-2))
251 refclock_report(peer, CEVNT_BADREPLY);
252 return; /* The reply is invalid discard it. */
255 /* Below I trying to find a correct reply in buffer.
256 * Sometime GPS reply located in the beginnig of buffer,
257 * sometime you can find it with some offset.
260 bpt = (char *)rbufp->recv_space.X_recv_buffer;
264 #define BP2(x) ( bpt[x] & 15 )
265 #define BP1(x) (( bpt[x] & 240 ) >> 4)
267 pp->year = BP1(2)*10 + BP2(2);
271 refclock_report(peer, CEVNT_BADREPLY);
272 if(!fg_init(pp->io.fd))
273 refclock_report(peer, CEVNT_FAULT);
275 /* GPS is just powered up. The date is invalid -
276 discarding it. Initilize GPS one more time */
277 /* Sorry - this driver will broken in 2094 ;) */
284 pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4);
287 After Jan, 10 2000 Forum Graphic GPS receiver had a very strange
288 benahour. It doubles day number for an hours in replys after 10:10:10 UTC
289 and doubles min every hour at HH:10:ss for a minute.
290 Hope it is a problem of my unit only and not a Y2K problem of FG GPS.
291 Below small code to avoid such situation.
294 pp->hour = BP1(6)*10 + BP2(6);
296 pp->hour = BP1(5)*10 + BP2(5);
298 if((up->y2kwarn > 10) && (pp->hour == 10))
300 pp->minute = BP1(7)*10 + BP2(7);
301 pp->second = BP1(8)*10 + BP2(8);
302 pp->nsec = (BP1(9)*10 + BP2(9)) * 1000000;
303 pp->nsec += BP1(10) * 1000;
305 pp->hour = BP1(5)*10 + BP2(5);
306 pp->minute = BP1(6)*10 + BP2(6);
307 pp->second = BP1(7)*10 + BP2(7);
308 pp->nsec = (BP1(8)*10 + BP2(8)) * 1000000;
309 pp->nsec += BP1(9) * 1000;
312 if((pp->hour == 10) && (pp->minute == 10))
317 sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second);
318 pp->lencode = strlen(pp->a_lastcode);
319 /*get_systime(&pp->lastrec);*/
323 printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n",
324 pp->year, pp->day, pp->hour, pp->minute, pp->second);
327 pp->lastrec = rbufp->recv_time; /* Is it better than get_systime()? */
328 /* pp->leap = LEAP_NOWARNING; */
331 * Process the new sample in the median filter and determine the
332 * timecode timestamp.
335 if (!refclock_process(pp))
336 refclock_report(peer, CEVNT_BADTIME);
337 pp->lastref = pp->lastrec;
338 refclock_receive(peer);
345 #endif /* REFCLOCK */