summaryrefslogtreecommitdiff
path: root/pseudonmea.c
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2009-09-15 16:51:18 +0000
committerEric S. Raymond <esr@thyrsus.com>2009-09-15 16:51:18 +0000
commitdd81945a419ef97e09578b552d0fe967d08a1cee (patch)
treecafe7942e01df91f8145f471ac025d92b897f435 /pseudonmea.c
parent03eb120c76a479fff6d0733ba95eca4782c042e6 (diff)
downloadgpsd-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.c241
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 */
+
+