libgpsd_core.c

来自「gpsd, a popular GPS daemon.」· C语言 代码 · 共 864 行 · 第 1/2 页

C
864
字号
/* $Id: libgpsd_core.c 4664 2008-01-21 00:37:35Z garyemiller $ *//* libgpsd_core.c -- direct access to GPSes on serial or USB devices. */#include <sys/time.h>#include <sys/ioctl.h>#include <sys/socket.h>#include <sys/time.h>#include <unistd.h>#include <stdlib.h>#include <stdio.h>#include <math.h>#include <netdb.h>#include <string.h>#include <errno.h>#include <fcntl.h>#include "gpsd_config.h"#include "gpsd.h"#if defined(PPS_ENABLE) && defined(TIOCMIWAIT)#ifndef S_SPLINT_S#include <pthread.h>	/* pacifies OpenBSD's compiler */#endif#endifint gpsd_switch_driver(struct gps_device_t *session, char* type_name){    struct gps_type_t **dp;    gpsd_report(LOG_PROG, "switch_driver(%s) called...\n", type_name);    if (session->device_type != NULL && 	strcmp(session->device_type->type_name, type_name) == 0) {#ifdef ALLOW_RECONFIGURE	gpsd_report(LOG_PROG, "Reconfiguring for %s...\n", session->device_type->type_name);	if (session->enable_reconfigure 	    	&& session->device_type->configurator != NULL)	    session->device_type->configurator(session, 0);#endif /* ALLOW_RECONFIGURE */	return 0;    }    /*@ -compmempass @*/    for (dp = gpsd_drivers; *dp; dp++)	if (strcmp((*dp)->type_name, type_name) == 0) {	    gpsd_report(LOG_PROG, "selecting %s driver...\n", (*dp)->type_name);	    gpsd_assert_sync(session);	    /*@i@*/session->device_type = *dp;	    if (!session->context->readonly && session->device_type->probe_subtype != NULL)		session->device_type->probe_subtype(session, session->packet.counter = 0);#ifdef ALLOW_RECONFIGURE	    if (session->enable_reconfigure 			&& session->device_type->configurator != NULL) {		gpsd_report(LOG_PROG, "configuring for %s...\n", session->device_type->type_name);		session->device_type->configurator(session, 0);	    }#endif /* ALLOW_RECONFIGURE */	    return 1;	}    gpsd_report(LOG_ERROR, "invalid GPS type \"%s\".\n", type_name);    return 0;    /*@ +compmempass @*/}void gpsd_init(struct gps_device_t *session, struct gps_context_t *context, char *device)/* initialize GPS polling */{    /*@ -mayaliasunique @*/    (void)strlcpy(session->gpsdata.gps_device, device, PATH_MAX);    /*@ -mustfreeonly @*/    session->device_type = NULL;	/* start by hunting packets */    session->rtcmtime = 0;    /*@ -temptrans @*/    session->context = context;    /*@ +temptrans @*/    /*@ +mayaliasunique @*/    /*@ +mustfreeonly @*/    gps_clear_fix(&session->gpsdata.fix);    session->gpsdata.set &=~ (FIX_SET | DOP_SET);    session->gpsdata.hdop = NAN;    session->gpsdata.vdop = NAN;    session->gpsdata.pdop = NAN;    session->gpsdata.tdop = NAN;    session->gpsdata.gdop = NAN;    session->gpsdata.epe = NAN;    session->mag_var = NAN;    /* tty-level initialization */    gpsd_tty_init(session);    /* necessary in case we start reading in the middle of a GPGSV sequence */    gpsd_zero_satellites(&session->gpsdata);    /* initialize things for the packet parser */    packet_reset(&session->packet);}void gpsd_deactivate(struct gps_device_t *session)/* temporarily release the GPS device */{#ifdef NTPSHM_ENABLE    (void)ntpshm_free(session->context, session->shmindex);    session->shmindex = -1;# ifdef PPS_ENABLE    (void)ntpshm_free(session->context, session->shmTimeP);    session->shmTimeP = -1;# endif /* PPS_ENABLE */#ifdef ALLOW_RECONFIGURE    if (session->enable_reconfigure 	&& session->device_type != NULL	&& session->device_type->revert != NULL) {	session->device_type->revert(session);	session->enable_reconfigure = false;    }#endif /* ALLOW_RECONFIGURE */    if (session->device_type!=NULL) {	if (session->back_to_nmea && session->device_type->mode_switcher!=NULL)	    session->device_type->mode_switcher(session, 0);	if (session->device_type->wrapup!=NULL)	    session->device_type->wrapup(session);    }#endif /* NTPSHM_ENABLE */    gpsd_report(LOG_INF, "closing GPS=%s (%d)\n", 		session->gpsdata.gps_device, session->gpsdata.gps_fd);    (void)gpsd_close(session);}#if defined(PPS_ENABLE) && defined(TIOCMIWAIT)static void *gpsd_ppsmonitor(void *arg){    struct gps_device_t *session = (struct gps_device_t *)arg;    int cycle,duration, state = 0, laststate = -1, unchanged = 0;    struct timeval tv;    struct timeval pulse[2] = {{0,0},{0,0}};    int pps_device = TIOCM_CAR;#if defined(PPS_ON_CTS)    pps_device = TIOCM_CTS;#endif    gpsd_report(LOG_PROG, "Create Thread gpsd_ppsmonitor\n");    /* wait for status change on the device's carrier-detect line */    while (ioctl(session->gpsdata.gps_fd, TIOCMIWAIT, pps_device) == 0) {	(void)gettimeofday(&tv,NULL);	/*@ +ignoresigns */	if (ioctl(session->gpsdata.gps_fd, TIOCMGET, &state) != 0)	    break;	/*@ -ignoresigns */	state = (int)((state & pps_device) != 0);	if (state == laststate) {	    if (++unchanged == 10) {		gpsd_report(LOG_WARN, "TIOCMIWAIT returns unchanged state, ppsmonitor terminates\n");		break;	    }	} else {            gpsd_report(LOG_RAW, "pps-detect (%s) on %s changed to %d\n",                        ((pps_device==TIOCM_CAR) ? "DCD" : "CTS"),                           session->gpsdata.gps_device, state);	    laststate = state;	    unchanged = 0;	}	/*@ +boolint @*/	if ( session->context->fixcnt > 3 ) {	    /* Garmin doc says PPS is valid after four good fixes. */	    /*	     * The PPS pulse is normally a short pulse with a frequency of	     * 1 Hz, and the UTC second is defined by the front edge. But we	     * don't know the polarity of the pulse (different receivers	     * emit different polarities). The duration variable is used to	     * determine which way the pulse is going. The code assumes	     * that the UTC second is changing when the signal has not	     * been changing for at least 800ms, i.e. it assumes the duty	     * cycle is at most 20%.	     *	     * Some GPS instead output a square wave that is 0.5 Hz and each	     * edge denotes the start of a second.	     *             * A few stupid GPS, like the Furuno GPSClock, output a 1.0 Hz              * square wave where the leading edge is the start of a second	     *	     * 5Hz GPS (Garmin 18-5Hz) pulses at 5Hz. Set the pulse length to 	     * 40ms which gives a 160ms pulse before going high.             *	     */#define timediff(x, y)	(int)((x.tv_sec-y.tv_sec)*1000000+x.tv_usec-y.tv_usec)	    cycle = timediff(tv, pulse[state]);	    duration = timediff(tv, pulse[state == 0]);#undef timediff	    if (cycle > 199000 && cycle < 201000 ) {		/* 5Hz cycle */		/* looks like 5hz PPS pulse */	        if (duration > 45000)		    (void)ntpshm_pps(session, &tv);		gpsd_report(LOG_RAW, "5Hz PPS pulse. cycle: %d, duration: %d\n",			cycle, duration);	    } else if (cycle > 999000 && cycle < 1001000 ) {		/* looks like PPS pulse or square wave */                if (duration > 499000 && duration < 501000 		  && session->driver.nmea.ignore_trailing_edge) {                    /* looks like 1.0 Hz square wave, ignore trailing edge */                    if (state == 1) {                         (void)ntpshm_pps(session, &tv);                    }                } else {                    /* looks like PPS pulse */                    (void)ntpshm_pps(session, &tv);		}		gpsd_report(LOG_RAW, "PPS pulse. cycle: %d, duration: %d\n",			cycle, duration);	    } else if (cycle > 1999000 && cycle < 2001000) {		/* looks like 0.5 Hz square wave */		(void)ntpshm_pps(session, &tv);                gpsd_report(LOG_RAW, "PPS square wave. cycle: %d, duration: %d\n",			cycle, duration);	    } else {                gpsd_report(LOG_INF, "PPS pulse rejected.  cycle: %d, duration: %d\n",			cycle, duration);	    }	} else {                gpsd_report(LOG_INF, "PPS pulse rejected. No fix.\n");	}	/*@ -boolint @*/	pulse[state] = tv;    }    return NULL;}#endif /* PPS_ENABLE *//*@ -branchstate @*/int gpsd_activate(struct gps_device_t *session, bool reconfigurable)/* acquire a connection to the GPS device */{    if (gpsd_open(session) < 0)	return -1;    else {#ifdef NON_NMEA_ENABLE	struct gps_type_t **dp;	/*@ -mustfreeonly @*/	for (dp = gpsd_drivers; *dp; dp++) {	    (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);  /* toss stale data */	    if ((*dp)->probe_detect!=NULL && (*dp)->probe_detect(session)!=0) {		gpsd_report(LOG_PROG, "probe found %s driver...\n", (*dp)->type_name);		session->device_type = *dp;		gpsd_assert_sync(session);		goto foundit;	    } 	}	/*@ +mustfreeonly @*/	gpsd_report(LOG_PROG, "no probe matched...\n");    foundit:#ifdef ALLOW_RECONFIGURE	session->enable_reconfigure = reconfigurable;#endif /* ALLOW_RECONFIGURE */#endif /* NON_NMEA_ENABLE */	session->gpsdata.online = timestamp();#ifdef SIRF_ENABLE	session->driver.sirf.satcounter = 0;#endif /* SIRF_ENABLE */	session->packet.char_counter = 0;	session->packet.retry_counter = 0;	gpsd_report(LOG_INF, "gpsd_activate(%d): opened GPS (%d)\n", reconfigurable, session->gpsdata.gps_fd);	// session->gpsdata.online = 0;	session->gpsdata.fix.mode = MODE_NOT_SEEN;	session->gpsdata.status = STATUS_NO_FIX;	session->gpsdata.fix.track = NAN;	session->gpsdata.separation = NAN;	session->mag_var = NAN;	/* clear driver subtype field and private data union */	session->subtype[0] = '\0';	memset(&session->driver, '\0', sizeof(session->driver));	/* if we know the device type, probe for subtype and configure it */	if (session->device_type != NULL) {	    if (session->device_type->probe_subtype !=NULL)		session->device_type->probe_subtype(session, session->packet.counter = 0);#ifdef ALLOW_RECONFIGURE	    if (reconfigurable) {		if (session->device_type->configurator != NULL)		    session->device_type->configurator(session, session->packet.counter);	    }#endif /* ALLOW_RECONFIGURE */	}    }    return session->gpsdata.gps_fd;}/*@ +branchstate @*/void ntpd_link_activate(struct gps_device_t *session){#if defined(PPS_ENABLE) && defined(TIOCMIWAIT)    pthread_t pt;#endif /* defined(PPS_ENABLE) && defined(TIOCMIWAIT) */#ifdef NTPSHM_ENABLE    /* If we are talking to ntpd, allocate a shared-memory segment for "NMEA" time data */    if (session->context->enable_ntpshm)        session->shmindex = ntpshm_alloc(session->context);#if defined(PPS_ENABLE) && defined(TIOCMIWAIT)    /* If we also have the 1pps capability, allocate a shared-memory segment for     * the 1pps time data and launch a thread to capture the 1pps transitions     */    if (session->shmindex >= 0 && session->context->shmTimePPS)        if ((session->shmTimeP = ntpshm_alloc(session->context)) >= 0)            /*@i1@*/(void)pthread_create(&pt,NULL,gpsd_ppsmonitor,(void *)session);#endif /* defined(PPS_ENABLE) && defined(TIOCMIWAIT) */#endif /* NTPSHM_ENABLE */}char /*@observer@*/ *gpsd_id(/*@in@*/struct gps_device_t *session)/* full ID of the device for reports, including subtype */{    static char buf[128];    if ((session == NULL) || (session->device_type == NULL) || 	(session->device_type->type_name == NULL))	return "unknown,";    (void)strlcpy(buf, session->device_type->type_name, sizeof(buf));    if (session->subtype[0] != '\0') {	(void)strlcat(buf, " ", sizeof(buf));	(void)strlcat(buf, session->subtype, sizeof(buf));    }    return(buf);}#if defined(BINARY_ENABLE) || defined(RTCM_ENABLE) || defined(NTRIP_ENABLE)/* * Support for generic binary drivers.  These functions dump NMEA for passing * to the client in raw mode.  They assume that (a) the public gps.h structure  * members are in a valid state, (b) that the private members hours, minutes,  * and seconds have also been filled in, (c) that if the private member * mag_var is not NAN it is a magnetic variation in degrees that should be * passed on, and (d) if the private member separation does not have the * value NAN, it is a valid WGS84 geoidal separation in  * meters for the fix. */static double degtodm(double a){    double m, t;    m = modf(a, &t);    t = floor(a) * 100 + m * 60;    return t;}/*@ -mustdefine @*/void gpsd_position_fix_dump(struct gps_device_t *session,			    /*@out@*/char bufp[], size_t len){    struct tm tm;    time_t intfixtime;    intfixtime = (time_t)session->gpsdata.fix.time;    (void)gmtime_r(&intfixtime, &tm);    if (session->gpsdata.fix.mode > 1) {	(void)snprintf(bufp, len,		"$GPGGA,%02d%02d%02d,%09.4f,%c,%010.4f,%c,%d,%02d,",		tm.tm_hour,		tm.tm_min,		tm.tm_sec,		degtodm(fabs(session->gpsdata.fix.latitude)),		((session->gpsdata.fix.latitude > 0) ? 'N' : 'S'),		degtodm(fabs(session->gpsdata.fix.longitude)),		((session->gpsdata.fix.longitude > 0) ? 'E' : 'W'),		session->gpsdata.status,		session->gpsdata.satellites_used);	if (isnan(session->gpsdata.hdop))	    (void)strlcat(bufp, ",", len);	else	    (void)snprintf(bufp+strlen(bufp), len-strlen(bufp),			   "%.2f,",session->gpsdata.hdop);	if (isnan(session->gpsdata.fix.altitude))	    (void)strlcat(bufp, ",", len);	else	    (void)snprintf(bufp+strlen(bufp), len-strlen(bufp), 			   "%.2f,M,", session->gpsdata.fix.altitude);	if (isnan(session->gpsdata.separation))	    (void)strlcat(bufp, ",", len);	else	    (void)snprintf(bufp+strlen(bufp), len-strlen(bufp), 			   "%.3f,M,", session->gpsdata.separation);	if (isnan(session->mag_var)) 	    (void)strlcat(bufp, ",", len);	else {	    (void)snprintf(bufp+strlen(bufp),			   len-strlen(bufp),			   "%3.2f,", fabs(session->mag_var));	    (void)strlcat(bufp, (session->mag_var > 0) ? "E": "W", len);	}	nmea_add_checksum(bufp);    }}/*@ +mustdefine @*/static void gpsd_transit_fix_dump(struct gps_device_t *session,				  char bufp[], size_t len){    struct tm tm;    time_t intfixtime;    tm.tm_mday = tm.tm_mon = tm.tm_year = tm.tm_hour = tm.tm_min = tm.tm_sec = 0;    if (isnan(session->gpsdata.fix.time)==0) {	intfixtime = (time_t)session->gpsdata.fix.time;	(void)gmtime_r(&intfixtime, &tm);	tm.tm_mon++;	tm.tm_year %= 100;    }    /*@ -usedef @*/    (void)snprintf(bufp, len,	    "$GPRMC,%02d%02d%02d,%c,%09.4f,%c,%010.4f,%c,%.4f,%.3f,%02d%02d%02d,,",	    tm.tm_hour, 	    tm.tm_min, 	    tm.tm_sec,	    session->gpsdata.status ? 'A' : 'V',	    degtodm(fabs(session->gpsdata.fix.latitude)),	    ((session->gpsdata.fix.latitude > 0) ? 'N' : 'S'),	    degtodm(fabs(session->gpsdata.fix.longitude)),	    ((session->gpsdata.fix.longitude > 0) ? 'E' : 'W'),#define ZEROIZE(x)	(isnan(x)!=0 ? 0.0 : x)  	    ZEROIZE(session->gpsdata.fix.speed * MPS_TO_KNOTS),	    ZEROIZE(session->gpsdata.fix.track),#undef ZEROIZE

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?