gpsutils.c

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

C
591
字号
/* $Id: gpsutils.c 4652 2008-01-01 16:47:10Z ckuethe $ *//* gpsutils.c -- code shared between low-level and high-level interfaces */#include <sys/types.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <math.h>#include <string.h>#include <stdarg.h>#include <time.h>#include "gpsd_config.h"#include "gpsd.h"#define MONTHSPERYEAR	12		/* months per calendar year */void gps_clear_fix(/*@out@*/struct gps_fix_t *fixp)/* stuff a fix structure with recognizable out-of-band values */{    fixp->time = NAN;    fixp->mode = MODE_NOT_SEEN;    fixp->latitude = fixp->longitude = NAN;    fixp->track = NAN;    fixp->speed = NAN;    fixp->climb = NAN;    fixp->altitude = NAN;    fixp->ept = NAN;    fixp->eph = NAN;    fixp->epv = NAN;    fixp->epd = NAN;    fixp->eps = NAN;    fixp->epc = NAN;}unsigned int gps_valid_fields(/*@in@*/struct gps_fix_t *fixp){    unsigned int valid = 0;    if (isnan(fixp->time) == 0)	valid |= TIME_SET;    if (fixp->mode != MODE_NOT_SEEN)	valid |= MODE_SET;    if (isnan(fixp->latitude) == 0 && isnan(fixp->longitude) == 0)	valid |= LATLON_SET;    if (isnan(fixp->altitude) == 0)	valid |= ALTITUDE_SET;    if (isnan(fixp->track) == 0)	valid |= TRACK_SET;    if (isnan(fixp->speed) == 0)	valid |= SPEED_SET;    if (isnan(fixp->climb) == 0)	valid |= CLIMB_SET;    if (isnan(fixp->ept) == 0)	valid |= TIMERR_SET;    if (isnan(fixp->eph) == 0)	valid |= HERR_SET;    if (isnan(fixp->epv) == 0)	valid |= VERR_SET;    if (isnan(fixp->epd) == 0)	valid |= TRACKERR_SET;    if (isnan(fixp->eps) == 0)	valid |= SPEEDERR_SET;    if (isnan(fixp->epc) == 0)	valid |= CLIMBERR_SET;    return valid;}char *gps_show_transfer(int transfer){/*@ -statictrans @*/    static char showbuf[100];    showbuf[0] = '\0';    if ((transfer & TIME_SET)!=0)	(void)strlcat(showbuf, "time,", sizeof(showbuf));    if ((transfer & LATLON_SET)!=0)	(void)strlcat(showbuf, "latlon,", sizeof(showbuf));    if ((transfer & MODE_SET)!=0)	(void)strlcat(showbuf, "mode,", sizeof(showbuf));    if ((transfer & ALTITUDE_SET)!=0)	(void)strlcat(showbuf, "altitude,", sizeof(showbuf));    if ((transfer & TRACK_SET)!=0)	(void)strlcat(showbuf, "track,", sizeof(showbuf));    if ((transfer & SPEED_SET)!=0)	(void)strlcat(showbuf, "speed,", sizeof(showbuf));    if ((transfer & CLIMB_SET)!=0)	(void)strlcat(showbuf, "climb,", sizeof(showbuf));    if ((transfer & TIMERR_SET)!=0)	(void)strlcat(showbuf, "timerr,", sizeof(showbuf));    if ((transfer & HERR_SET)!=0)	(void)strlcat(showbuf, "herr,", sizeof(showbuf));    if ((transfer & VERR_SET)!=0)	(void)strlcat(showbuf, "verr,", sizeof(showbuf));    if ((transfer & SPEEDERR_SET)!=0)	(void)strlcat(showbuf, "speederr,", sizeof(showbuf));    if ((transfer & CLIMBERR_SET)!=0)	(void)strlcat(showbuf, "climberr,", sizeof(showbuf));    if (strlen(showbuf)>0)	showbuf[strlen(showbuf)-1] = '\0';    return showbuf;/*@ +statictrans @*/}void gps_merge_fix(/*@ out @*/struct gps_fix_t *to,		   gps_mask_t transfer,		   /*@ in @*/struct gps_fix_t *from)/* merge new data into an old fix */{    if ((NULL == to) || (NULL == from))	return;    if ((transfer & TIME_SET)!=0)	to->time = from->time;    if ((transfer & LATLON_SET)!=0) {	to->latitude = from->latitude;	to->longitude = from->longitude;    }    if ((transfer & MODE_SET)!=0)	to->mode = from->mode;    if ((transfer & ALTITUDE_SET)!=0)	to->altitude = from->altitude;    if ((transfer & TRACK_SET)!=0)	to->track = from->track;    if ((transfer & SPEED_SET)!=0)	to->speed = from->speed;    if ((transfer & CLIMB_SET)!=0)	to->climb = from->climb;    if ((transfer & TIMERR_SET)!=0)	to->ept = from->ept;    if ((transfer & HERR_SET)!=0)	to->eph = from->eph;    if ((transfer & VERR_SET)!=0)	to->epv = from->epv;    if ((transfer & SPEEDERR_SET)!=0)	to->eps = from->eps;    if ((transfer & CLIMBERR_SET)!=0)	to->epc = from->epc;}double timestamp(void) {    struct timeval tv;     (void)gettimeofday(&tv, NULL);     /*@i1@*/return(tv.tv_sec + tv.tv_usec*1e-6);}time_t mkgmtime(register struct tm *t)/* struct tm to seconds since Unix epoch */{    register int year;    register time_t result;    static const int cumdays[MONTHSPERYEAR] =    {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334};    /*@ +matchanyintegral @*/    year = 1900 + t->tm_year + t->tm_mon / MONTHSPERYEAR;    result = (year - 1970) * 365 + cumdays[t->tm_mon % MONTHSPERYEAR];    result += (year - 1968) / 4;    result -= (year - 1900) / 100;    result += (year - 1600) / 400;    if ((year % 4) == 0 && ((year % 100) != 0 || (year % 400) == 0) &&	(t->tm_mon % MONTHSPERYEAR) < 2)	     result--;    result += t->tm_mday - 1;    result *= 24;    result += t->tm_hour;    result *= 60;    result += t->tm_min;    result *= 60;    result += t->tm_sec;    /*@ -matchanyintegral @*/    return (result);}double iso8601_to_unix(/*@in@*/char *isotime)/* ISO8601 UTC to Unix UTC */{    char *dp = NULL;    double usec;    struct tm tm;    /*@i1@*/dp = strptime(isotime, "%Y-%m-%dT%H:%M:%S", &tm);    if (*dp == '.')	usec = strtod(dp, NULL);    else	usec = 0;    return (double)mkgmtime(&tm) + usec;}/*@observer@*/char *unix_to_iso8601(double fixtime, /*@ out @*/char isotime[], size_t len)/* Unix UTC time to ISO8601, no timezone adjustment *//* example: 2007-12-11T23:38:51.0Z */{    struct tm when;    double integral, fractional;    time_t intfixtime;    char timestr[30];    char fractstr[10];    fractional = modf(fixtime, &integral);    intfixtime = (time_t)integral;    (void)gmtime_r(&intfixtime, &when);    (void)strftime(timestr, sizeof(timestr), "%Y-%m-%dT%H:%M:%S", &when);    (void)snprintf(fractstr, sizeof(fractstr), "%.1f", fractional);    /* add fractional part, ignore leading 0; "0.2" -> ".2" */     (void)snprintf(isotime, len, "%s%sZ", timestr, fractstr+1);    return isotime;}/* * The 'week' part of GPS dates are specified in weeks since 0000 on 06  * January 1980, with a rollover at 1024.  At time of writing the last  * rollover happened at 0000 22 August 1999.  Time-of-week is in seconds. * * This code copes with both conventional GPS weeks and the "extended" * 15-or-16-bit version with no wraparound that appears in Zodiac * chips and is supposed to appear in the Geodetic Navigation * Information (0x29) packet of SiRF chips.  Some SiRF firmware versions * (notably 231) actually ship the wrapped 10-bit week, despite what * the protocol reference claims. * * Note: This time will need to be corrected for leap seconds. */#define GPS_EPOCH	315964800		/* GPS epoch in Unix time */#define SECS_PER_WEEK	(60*60*24*7)		/* seconds per week */#define GPS_ROLLOVER	(1024*SECS_PER_WEEK)	/* rollover period */double gpstime_to_unix(int week, double tow){    double fixtime;    if (week >= 1024)	fixtime = GPS_EPOCH + (week * SECS_PER_WEEK) + tow;    else {	time_t now, last_rollover;	(void)time(&now);	last_rollover = GPS_EPOCH+((now-GPS_EPOCH)/GPS_ROLLOVER)*GPS_ROLLOVER;	/*@i@*/fixtime = last_rollover + (week * SECS_PER_WEEK) + tow;    }    return fixtime;}void unix_to_gpstime(double unixtime, /*@out@*/int *week, /*@out@*/double *tow){    unixtime -= GPS_EPOCH;    *week = (int)(unixtime / SECS_PER_WEEK);    *tow = fmod(unixtime, SECS_PER_WEEK);}#define Deg2Rad(n)	((n) * DEG_2_RAD)static double CalcRad(double lat)/* earth's radius of curvature in meters at specified latitude.*/{    const double a = 6378.137;    const double e2 = 0.081082 * 0.081082;    // the radius of curvature of an ellipsoidal Earth in the plane of a    // meridian of latitude is given by    //    // R' = a * (1 - e^2) / (1 - e^2 * (sin(lat))^2)^(3/2)    //    // where a is the equatorial radius,    // b is the polar radius, and    // e is the eccentricity of the ellipsoid = sqrt(1 - b^2/a^2)    //    // a = 6378 km (3963 mi) Equatorial radius (surface to center distance)    // b = 6356.752 km (3950 mi) Polar radius (surface to center distance)    // e = 0.081082 Eccentricity    double sc = sin(Deg2Rad(lat));    double x = a * (1.0 - e2);    double z = 1.0 - e2 * sc * sc;    double y = pow(z, 1.5);    double r = x / y;    return r * 1000.0;	// Convert to meters}double earth_distance(double lat1, double lon1, double lat2, double lon2)/* distance in meters between two points specified in degrees. */{    double x1 = CalcRad(lat1) * cos(Deg2Rad(lon1)) * sin(Deg2Rad(90-lat1));    double x2 = CalcRad(lat2) * cos(Deg2Rad(lon2)) * sin(Deg2Rad(90-lat2));    double y1 = CalcRad(lat1) * sin(Deg2Rad(lon1)) * sin(Deg2Rad(90-lat1));    double y2 = CalcRad(lat2) * sin(Deg2Rad(lon2)) * sin(Deg2Rad(90-lat2));    double z1 = CalcRad(lat1) * cos(Deg2Rad(90-lat1));    double z2 = CalcRad(lat2) * cos(Deg2Rad(90-lat2));    double a = (x1*x2 + y1*y2 + z1*z2)/pow(CalcRad((lat1+lat2)/2),2);    // a should be in [1, -1] but can sometimes fall outside it by    // a very small amount due to rounding errors in the preceding    // calculations.  This is prone to happen when the argument points    // are very close together.  Return NaN so calculations trying    // to use this will also blow up.    if (fabs(a) > 1) 	return NAN;    else	return CalcRad((lat1+lat2) / 2) * acos(a);}

⌨️ 快捷键说明

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