/* $NetBSD: refclock_fg.c,v 1.1.1.1 2000/03/29 12:38:53 simonb Exp $ */ /* * refclock_fg - clock driver for the Forum Graphic GPS datating station */ #ifdef HAVE_CONFIG_H # include #endif #if defined(REFCLOCK) && defined(CLOCK_FG) #include #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" #include "ntp_calendar.h" #include "ntp_stdlib.h" /* * This driver supports the Forum Graphic GPS dating station. * More information about FG GPS is available on http://www.forumgraphic.com * Contact das@amt.ru for any question about this driver. */ /* * Interface definitions */ #define DEVICE "/dev/fgclock%d" #define PRECISION (-10) /* precision assumed (about 1 ms) */ #define REFID "GPS" #define DESCRIPTION "Forum Graphic GPS dating station" #define LENFG 26 /* timecode length */ #define SPEED232 B9600 /* uart speed (9600 baud) */ /* * Function prototypes */ static int fg_init P((int)); static int fg_start P((int, struct peer *)); static void fg_shutdown P((int, struct peer *)); static void fg_poll P((int, struct peer *)); static void fg_receive P((struct recvbuf *)); /* * Forum Graphic unit control structure */ struct fgunit { int pollnum; /* Use peer.poll instead? */ int status; /* Hug to check status information on GPS */ int y2kwarn; /* Y2K bug */ }; /* * Queries definition */ static char fginit[] = { 0x10, 0x48, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; static char fgdate[] = { 0x10, 0x44, 0x10, 0x0D, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; /* * Transfer vector */ struct refclock refclock_fg = { fg_start, /* start up driver */ fg_shutdown, /* shut down driver */ fg_poll, /* transmit poll message */ noentry, /* not used */ noentry, /* initialize driver (not used) */ noentry, /* not used */ NOFLAGS /* not used */ }; /* * fg_init - Initialization of FG GPS. */ static int fg_init( int fd ) { if (write(fd, fginit, LENFG) != LENFG) return 0; return (1); } /* * fg_start - open the device and initialize data for processing */ static int fg_start( int unit, struct peer *peer ) { struct refclockproc *pp; struct fgunit *up; int fd; char device[20]; /* * Open device file for reading. */ (void)sprintf(device, DEVICE, unit); #ifdef DEBUG if (debug) printf ("starting FG with device %s\n",device); #endif if (!(fd = refclock_open(device, SPEED232, LDISC_CLK))) return (0); /* * Allocate and initialize unit structure */ if (!(up = (struct fgunit *) emalloc(sizeof(struct fgunit)))) { (void) close(fd); return (0); } memset((char *)up, 0, sizeof(struct fgunit)); pp = peer->procptr; pp->unitptr = (caddr_t)up; pp->io.clock_recv = fg_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { (void) close(fd); return (0); } /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 3); up->pollnum = 0; /* * Setup dating station to use GPS receiver. * GPS receiver should work before this operation. */ if(!fg_init(pp->io.fd)) refclock_report(peer, CEVNT_FAULT); return (1); } /* * fg_shutdown - shut down the clock */ static void fg_shutdown( int unit, struct peer *peer ) { struct refclockproc *pp; struct fgunit *up; pp = peer->procptr; up = (struct fgunit *)pp->unitptr; io_closeclock(&pp->io); free(up); } /* * fg_poll - called by the transmit procedure */ static void fg_poll( int unit, struct peer *peer ) { struct refclockproc *pp; pp = peer->procptr; /* * Time to poll the clock. The FG clock responds to a * "D" by returning a timecode in the format specified * above. If nothing is heard from the clock for two polls, * declare a timeout and keep going. */ if (write(pp->io.fd, fgdate, LENFG) != LENFG) refclock_report(peer, CEVNT_FAULT); else pp->polls++; if (peer->burst > 0) return; /* if (pp->coderecv == pp->codeproc) { refclock_report(peer, CEVNT_TIMEOUT); return; } */ peer->burst = NSTAGE; record_clock_stats(&peer->srcadr, pp->a_lastcode); return; } /* * fg_receive - receive data from the serial interface */ static void fg_receive( struct recvbuf *rbufp ) { struct refclockproc *pp; struct fgunit *up; struct peer *peer; char *bpt; /* * Initialize pointers and read the timecode and timestamp * We can't use gtlin function because we need bynary data in buf */ peer = (struct peer *)rbufp->recv_srcclock; pp = peer->procptr; up = (struct fgunit *)pp->unitptr; /* * Below hug to implement receiving of status information */ if(!up->pollnum) { up->pollnum++; return; } if (rbufp->recv_length < (LENFG-2)) { refclock_report(peer, CEVNT_BADREPLY); return; /* The reply is invalid discard it. */ } /* Below I trying to find a correct reply in buffer. * Sometime GPS reply located in the beginnig of buffer, * sometime you can find it with some offset. */ bpt = (char *)rbufp->recv_space.X_recv_buffer; while(*bpt != '') bpt++; #define BP2(x) ( bpt[x] & 15 ) #define BP1(x) (( bpt[x] & 240 ) >> 4) pp->year = BP1(2)*10 + BP2(2); if(pp->year == 94) { refclock_report(peer, CEVNT_BADREPLY); if(!fg_init(pp->io.fd)) refclock_report(peer, CEVNT_FAULT); return; /* GPS is just powered up. The date is invalid - discarding it. Initilize GPS one more time */ /* Sorry - this driver will broken in 2094 ;) */ } if (pp->year < 99) pp->year += 100; pp->year += 1900; pp->day = 100 * BP2(3) + 10 * BP1(4) + BP2(4); /* After Jan, 10 2000 Forum Graphic GPS receiver had a very strange benahour. It doubles day number for an hours in replys after 10:10:10 UTC and doubles min every hour at HH:10:ss for a minute. Hope it is a problem of my unit only and not a Y2K problem of FG GPS. Below small code to avoid such situation. */ if(up->y2kwarn > 10) pp->hour = BP1(6)*10 + BP2(6); else pp->hour = BP1(5)*10 + BP2(5); if((up->y2kwarn > 10) && (pp->hour == 10)) { pp->minute = BP1(7)*10 + BP2(7); pp->second = BP1(8)*10 + BP2(8); pp->msec = BP1(9)*10 + BP2(9); pp->usec = BP1(10); } else { pp->hour = BP1(5)*10 + BP2(5); pp->minute = BP1(6)*10 + BP2(6); pp->second = BP1(7)*10 + BP2(7); pp->msec = BP1(8)*10 + BP2(8); pp->usec = BP1(9); } if((pp->hour == 10) && (pp->minute == 10)) { up->y2kwarn++; } sprintf(pp->a_lastcode, "%d %d %d %d %d", pp->year, pp->day, pp->hour, pp->minute, pp->second); pp->lencode = strlen(pp->a_lastcode); /*get_systime(&pp->lastrec);*/ #ifdef DEBUG if (debug) printf ("fg: time is %04d/%03d %02d:%02d:%02d UTC\n", pp->year, pp->day, pp->hour, pp->minute, pp->second); #endif if (peer->stratum <= 1) peer->refid = pp->refid; pp->disp = (10e-6); pp->lastrec = rbufp->recv_time; /* Is it better then get_systime()? */ /* pp->leap = LEAP_NOWARNING; */ /* * Process the new sample in the median filter and determine the * timecode timestamp. */ if (!refclock_process(pp)) refclock_report(peer, CEVNT_BADTIME); refclock_receive(peer); return; } #else int refclock_fg_bs; #endif /* REFCLOCK */