📄 refclock_nmea.c
字号:
/* * refclock_nmea.c - clock driver for an NMEA GPS CLOCK * Michael Petry Jun 20, 1994 * based on refclock_heathn.c */#ifdef HAVE_CONFIG_H#include <config.h>#endif#if defined(SYS_WINNT)#undef close#define close closesocket#endif#if defined(REFCLOCK) && defined(CLOCK_NMEA)#include <stdio.h>#include <ctype.h>#include "ntpd.h"#include "ntp_io.h"#include "ntp_unixtime.h"#include "ntp_refclock.h"#include "ntp_stdlib.h"#ifdef HAVE_PPSAPI# include "ppsapi_timepps.h"#endif /* HAVE_PPSAPI *//* * This driver supports the NMEA GPS Receiver with * * Protype was refclock_trak.c, Thanks a lot. * * The receiver used spits out the NMEA sentences for boat navigation. * And you thought it was an information superhighway. Try a raging river * filled with rapids and whirlpools that rip away your data and warp time. * * If HAVE_PPSAPI is defined code to use the PPSAPI will be compiled in. * On startup if initialization of the PPSAPI fails, it will fall back * to the "normal" timestamps. * * The PPSAPI part of the driver understands fudge flag2 and flag3. If * flag2 is set, it will use the clear edge of the pulse. If flag3 is * set, kernel hardpps is enabled. * * GPS sentences other than RMC (the default) may be enabled by setting * the relevent bits of 'mode' in the server configuration line * server 127.127.20.x mode X * * bit 0 - enables RMC (1) * bit 1 - enables GGA (2) * bit 2 - enables GLL (4) * multiple sentences may be selected *//* * Definitions */#ifdef SYS_WINNT# define DEVICE "COM%d:" /* COM 1 - 3 supported */#else# define DEVICE "/dev/gps%d" /* name of radio device */#endif#define SPEED232 B4800 /* uart speed (4800 bps) */#define PRECISION (-9) /* precision assumed (about 2 ms) */#define PPS_PRECISION (-20) /* precision assumed (about 1 us) */#define REFID "GPS\0" /* reference id */#define DESCRIPTION "NMEA GPS Clock" /* who we are */#define NANOSECOND 1000000000 /* one second (ns) */#define RANGEGATE 500000 /* range gate (ns) */#define LENNMEA 75 /* min timecode length *//* * Tables to compute the ddd of year form icky dd/mm timecode. Viva la * leap. */static int day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};static int day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};/* * Unit control structure */struct nmeaunit { int pollcnt; /* poll message counter */ int polled; /* Hand in a sample? */ l_fp tstamp; /* timestamp of last poll */#ifdef HAVE_PPSAPI struct timespec ts; /* last timestamp */ pps_params_t pps_params; /* pps parameters */ pps_info_t pps_info; /* last pps data */ pps_handle_t handle; /* pps handlebars */#endif /* HAVE_PPSAPI */};/* * Function prototypes */static int nmea_start P((int, struct peer *));static void nmea_shutdown P((int, struct peer *));#ifdef HAVE_PPSAPIstatic void nmea_control P((int, struct refclockstat *, struct refclockstat *, struct peer *));static int nmea_ppsapi P((struct peer *, int, int));static int nmea_pps P((struct nmeaunit *, l_fp *));#endif /* HAVE_PPSAPI */static void nmea_receive P((struct recvbuf *));static void nmea_poll P((int, struct peer *));static void gps_send P((int, const char *, struct peer *));static char *field_parse P((char *, int));/* * Transfer vector */struct refclock refclock_nmea = { nmea_start, /* start up driver */ nmea_shutdown, /* shut down driver */ nmea_poll, /* transmit poll message */#ifdef HAVE_PPSAPI nmea_control, /* fudge control */#else noentry, /* fudge control */#endif /* HAVE_PPSAPI */ noentry, /* initialize driver */ noentry, /* buginfo */ NOFLAGS /* not used */};/* * nmea_start - open the GPS devices and initialize data for processing */static intnmea_start( int unit, struct peer *peer ){ register struct nmeaunit *up; struct refclockproc *pp; int fd; char device[20]; /* * Open serial port. Use CLK line discipline, if available. */ (void)sprintf(device, DEVICE, unit); fd = refclock_open(device, SPEED232, LDISC_CLK); if (fd <= 0) {#ifdef HAVE_READLINK /* nmead support added by Jon Miner (cp_n18@yahoo.com) * * See http://home.hiwaay.net/~taylorc/gps/nmea-server/ * for information about nmead * * To use this, you need to create a link from /dev/gpsX to * the server:port where nmead is running. Something like this: * * ln -s server:port /dev/gps1 */ char buffer[80]; char *nmea_host; int nmea_port; int len; struct hostent *he; struct protoent *p; struct sockaddr_in so_addr; if ((len = readlink(device,buffer,sizeof(buffer))) == -1) return(0); buffer[len] = 0; if ((nmea_host = strtok(buffer,":")) == NULL) return(0); nmea_port = atoi(strtok(NULL,":")); if ((he = gethostbyname(nmea_host)) == NULL) return(0); if ((p = getprotobyname("ip")) == NULL) return(0); so_addr.sin_family = AF_INET; so_addr.sin_port = htons(nmea_port); so_addr.sin_addr = *((struct in_addr *) he->h_addr); if ((fd = socket(PF_INET,SOCK_STREAM,p->p_proto)) == -1) return(0); if (connect(fd,(struct sockaddr *)&so_addr,SOCKLEN(&so_addr)) == -1) { close(fd); return (0); }#else return (0);#endif } /* * Allocate and initialize unit structure */ up = (struct nmeaunit *)emalloc(sizeof(struct nmeaunit)); if (up == NULL) { (void) close(fd); return (0); } memset((char *)up, 0, sizeof(struct nmeaunit)); pp = peer->procptr; pp->io.clock_recv = nmea_receive; pp->io.srcclock = (caddr_t)peer; pp->io.datalen = 0; pp->io.fd = fd; if (!io_addclock(&pp->io)) { (void) close(fd); free(up); return (0); } pp->unitptr = (caddr_t)up; /* * Initialize miscellaneous variables */ peer->precision = PRECISION; pp->clockdesc = DESCRIPTION; memcpy((char *)&pp->refid, REFID, 4); up->pollcnt = 2; gps_send(pp->io.fd,"$PMOTG,RMC,0000*1D\r\n", peer);#ifdef HAVE_PPSAPI /* * Start the PPSAPI interface if it is there. Default to use * the assert edge and do not enable the kernel hardpps. */ if (time_pps_create(fd, &up->handle) < 0) { up->handle = 0; msyslog(LOG_ERR, "refclock_nmea: time_pps_create failed: %m"); return (1); } return(nmea_ppsapi(peer, 0, 0));#else return (1);#endif /* HAVE_PPSAPI */}/* * nmea_shutdown - shut down a GPS clock */static voidnmea_shutdown( int unit, struct peer *peer ){ register struct nmeaunit *up; struct refclockproc *pp; pp = peer->procptr; up = (struct nmeaunit *)pp->unitptr;#ifdef HAVE_PPSAPI if (up->handle != 0) time_pps_destroy(up->handle);#endif /* HAVE_PPSAPI */ io_closeclock(&pp->io); free(up);}#ifdef HAVE_PPSAPI/* * nmea_control - fudge control */static voidnmea_control( int unit, /* unit (not used */ struct refclockstat *in, /* input parameters (not uded) */ struct refclockstat *out, /* output parameters (not used) */ struct peer *peer /* peer structure pointer */ ){ struct refclockproc *pp; pp = peer->procptr; nmea_ppsapi(peer, pp->sloppyclockflag & CLK_FLAG2, pp->sloppyclockflag & CLK_FLAG3);}/* * Initialize PPSAPI */intnmea_ppsapi( struct peer *peer, /* peer structure pointer */ int enb_clear, /* clear enable */ int enb_hardpps /* hardpps enable */ ){ struct refclockproc *pp; struct nmeaunit *up; int capability; pp = peer->procptr; up = (struct nmeaunit *)pp->unitptr; if (time_pps_getcap(up->handle, &capability) < 0) { msyslog(LOG_ERR, "refclock_nmea: time_pps_getcap failed: %m"); return (0); } memset(&up->pps_params, 0, sizeof(pps_params_t)); if (enb_clear) up->pps_params.mode = capability & PPS_CAPTURECLEAR; else up->pps_params.mode = capability & PPS_CAPTUREASSERT; if (!up->pps_params.mode) { msyslog(LOG_ERR, "refclock_nmea: invalid capture edge %d", !enb_clear); return (0); } up->pps_params.mode |= PPS_TSFMT_TSPEC; if (time_pps_setparams(up->handle, &up->pps_params) < 0) { msyslog(LOG_ERR, "refclock_nmea: time_pps_setparams failed: %m"); return (0); } if (enb_hardpps) { if (time_pps_kcbind(up->handle, PPS_KC_HARDPPS, up->pps_params.mode & ~PPS_TSFMT_TSPEC, PPS_TSFMT_TSPEC) < 0) { msyslog(LOG_ERR, "refclock_nmea: time_pps_kcbind failed: %m"); return (0); } pps_enable = 1; } peer->precision = PPS_PRECISION;#if DEBUG if (debug) { time_pps_getparams(up->handle, &up->pps_params); printf( "refclock_ppsapi: capability 0x%x version %d mode 0x%x kern %d\n", capability, up->pps_params.api_version, up->pps_params.mode, enb_hardpps); }#endif return (1);}/* * Get PPSAPI timestamps. * * Return 0 on failure and 1 on success. */static intnmea_pps( struct nmeaunit *up, l_fp *tsptr ){ pps_info_t pps_info; struct timespec timeout, ts; double dtemp; l_fp tstmp; /* * Convert the timespec nanoseconds field to ntp l_fp units. */ if (up->handle == 0) return (0); timeout.tv_sec = 0; timeout.tv_nsec = 0; memcpy(&pps_info, &up->pps_info, sizeof(pps_info_t)); if (time_pps_fetch(up->handle, PPS_TSFMT_TSPEC, &up->pps_info, &timeout) < 0) return (0); if (up->pps_params.mode & PPS_CAPTUREASSERT) { if (pps_info.assert_sequence == up->pps_info.assert_sequence) return (0); ts = up->pps_info.assert_timestamp; } else if (up->pps_params.mode & PPS_CAPTURECLEAR) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -