349 lines
7.8 KiB
C
349 lines
7.8 KiB
C
|
/*
|
|||
|
* refclock_fg - clock driver for the Forum Graphic GPS datating station
|
|||
|
*/
|
|||
|
|
|||
|
#ifdef HAVE_CONFIG_H
|
|||
|
# include <config.h>
|
|||
|
#endif
|
|||
|
|
|||
|
#if defined(REFCLOCK) && defined(CLOCK_FG)
|
|||
|
|
|||
|
#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
|
|||
|
* "<DLE>D<DLE><CR>" 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 */
|