diff options
author | Eric S. Raymond <esr@thyrsus.com> | 2009-09-15 16:51:18 +0000 |
---|---|---|
committer | Eric S. Raymond <esr@thyrsus.com> | 2009-09-15 16:51:18 +0000 |
commit | dd81945a419ef97e09578b552d0fe967d08a1cee (patch) | |
tree | cafe7942e01df91f8145f471ac025d92b897f435 /pseudonmea.c | |
parent | 03eb120c76a479fff6d0733ba95eca4782c042e6 (diff) | |
download | gpsd-dd81945a419ef97e09578b552d0fe967d08a1cee.tar.gz |
Split libgpsd_core.c in half.
The code for dumping pseudo-NMEA needs an overhaul and may end up at a
different layer of the architecture. All regression tests pass.
Diffstat (limited to 'pseudonmea.c')
-rw-r--r-- | pseudonmea.c | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/pseudonmea.c b/pseudonmea.c new file mode 100644 index 00000000..fa4e307d --- /dev/null +++ b/pseudonmea.c @@ -0,0 +1,241 @@ +#include <sys/time.h> +#include <sys/ioctl.h> +#ifndef S_SPLINT_S +#include <sys/socket.h> +#include <unistd.h> +#endif /* S_SPLINT_S */ +#include <sys/time.h> +#include <stdlib.h> +#include <stdio.h> +#include <math.h> +#ifndef S_SPLINT_S +#include <netdb.h> +#endif /* S_SPLINT_S */ +#include <string.h> +#include <errno.h> +#include <fcntl.h> + +#include "gpsd_config.h" +#include "gpsd.h" + +/* + * 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 angle) +/* decimal degrees to GPS-style, degrees first followed by minutes */ +{ + double fraction, integer; + fraction = modf(angle, &integer); + return floor(angle) * 100 + fraction * 60; +} + +/*@ -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.dop.hdop)) + (void)strlcat(bufp, ",", len); + else + (void)snprintf(bufp+strlen(bufp), len-strlen(bufp), + "%.2f,",session->gpsdata.dop.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; + } +#define ZEROIZE(x) (isnan(x)!=0 ? 0.0 : x) + /*@ -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'), + ZEROIZE(session->gpsdata.fix.speed * MPS_TO_KNOTS), + ZEROIZE(session->gpsdata.fix.track), + tm.tm_mday, + tm.tm_mon, + tm.tm_year); + /*@ +usedef @*/ +#undef ZEROIZE + nmea_add_checksum(bufp); +} + +static void gpsd_binary_satellite_dump(struct gps_device_t *session, + char bufp[], size_t len) +{ + int i; + char *bufp2 = bufp; + bufp[0] = '\0'; + + for( i = 0 ; i < session->gpsdata.satellites; i++ ) { + if (i % 4 == 0) { + bufp += strlen(bufp); + bufp2 = bufp; + len -= snprintf(bufp, len, + "$GPGSV,%d,%d,%02d", + ((session->gpsdata.satellites-1) / 4) + 1, + (i / 4) + 1, + session->gpsdata.satellites); + } + bufp += strlen(bufp); + if (i < session->gpsdata.satellites) + len -= snprintf(bufp, len, + ",%02d,%02d,%03d,%02.0f", + session->gpsdata.PRN[i], + session->gpsdata.elevation[i], + session->gpsdata.azimuth[i], + session->gpsdata.ss[i]); + if (i % 4 == 3 || i == session->gpsdata.satellites-1) { + nmea_add_checksum(bufp2); + len -= 5; + } + } + +#ifdef ZODIAC_ENABLE + if (session->packet.type == ZODIAC_PACKET && session->driver.zodiac.Zs[0] != 0) { + bufp += strlen(bufp); + bufp2 = bufp; + (void)strlcpy(bufp, "$PRWIZCH", len); + for (i = 0; i < ZODIAC_CHANNELS; i++) { + len -= snprintf(bufp+strlen(bufp), len, + ",%02u,%X", + session->driver.zodiac.Zs[i], + session->driver.zodiac.Zv[i] & 0x0f); + } + nmea_add_checksum(bufp2); + } +#endif /* ZODIAC_ENABLE */ +} + +static void gpsd_binary_quality_dump(struct gps_device_t *session, + char bufp[], size_t len) +{ + int i, j; + char *bufp2 = bufp; + + (void)snprintf(bufp, len-strlen(bufp), + "$GPGSA,%c,%d,", 'A', session->gpsdata.fix.mode); + j = 0; + for (i = 0; i < session->device_type->channels; i++) { + if (session->gpsdata.used[i]) { + bufp += strlen(bufp); + (void)snprintf(bufp, len-strlen(bufp), + "%02d,", session->gpsdata.used[i]); + j++; + } + } + for (i = j; i < session->device_type->channels; i++) { + bufp += strlen(bufp); + (void)strlcpy(bufp, ",", len); + } + bufp += strlen(bufp); +#define ZEROIZE(x) (isnan(x)!=0 ? 0.0 : x) + if (session->gpsdata.fix.mode == MODE_NO_FIX) + (void)strlcat(bufp, ",,,", len); + else + (void)snprintf(bufp, len-strlen(bufp), + "%.1f,%.1f,%.1f*", + ZEROIZE(session->gpsdata.dop.pdop), + ZEROIZE(session->gpsdata.dop.hdop), + ZEROIZE(session->gpsdata.dop.vdop)); + nmea_add_checksum(bufp2); + bufp += strlen(bufp); + if (finite(session->gpsdata.fix.epx) + && finite(session->gpsdata.fix.epy) + && finite(session->gpsdata.fix.epv) + && finite(session->gpsdata.epe)) { + struct tm tm; + time_t intfixtime; + + 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); + } + (void)snprintf(bufp, len-strlen(bufp), + "$GPGBS,%02d%02d%02d,%.2f,M,%.2f,M,%.2f,M", + tm.tm_hour, tm.tm_min, tm.tm_sec, + ZEROIZE(session->gpsdata.fix.epx), + ZEROIZE(session->gpsdata.fix.epy), + ZEROIZE(session->gpsdata.fix.epv)); + nmea_add_checksum(bufp); + } +#undef ZEROIZE +} + +void gpsd_binary_dump(struct gps_device_t *session, + char bufp[], size_t len) +{ + if ((session->gpsdata.set & LATLON_SET) != 0) { + gpsd_position_fix_dump(session, bufp, len); + gpsd_transit_fix_dump(session, bufp + strlen(bufp), len - strlen(bufp)); + } + if ((session->gpsdata.set & (DOP_SET | ERR_SET)) != 0) + gpsd_binary_quality_dump(session, bufp+strlen(bufp), len-strlen(bufp)); + if ((session->gpsdata.set & SATELLITE_SET) != 0) + gpsd_binary_satellite_dump(session,bufp+strlen(bufp),len-strlen(bufp)); +} + +/* pseudonmea.c ends here */ + + |