summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--driver_garmin_txt.c87
-rw-r--r--drivers.c11
-rw-r--r--packet.c36
-rw-r--r--packet_states.h3
5 files changed, 89 insertions, 50 deletions
diff --git a/configure.ac b/configure.ac
index 49032b6d..c02a337f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -509,7 +509,7 @@ dnl check for Garmin Simple Text support
AC_ARG_ENABLE(garmintxt,
AC_HELP_STRING([--enable-garmintxt],
[enable Garmin Simple Text support]),
- [ac_garmintxt=$enableval], [ac_garmintxt=no])
+ [ac_garmintxt=$enableval], [ac_garmintxt=yes])
AC_MSG_CHECKING([for Garmin Simple Text support])
if test x"$ac_garmintxt" = "xyes"; then
AC_MSG_RESULT([yes])
diff --git a/driver_garmin_txt.c b/driver_garmin_txt.c
index 13f958b3..681e819b 100644
--- a/driver_garmin_txt.c
+++ b/driver_garmin_txt.c
@@ -1,7 +1,7 @@
/* $Id$ */
/*
* Handle the Garmin simple text format supported by some Garmins.
- * Tested with the Garmin eTrex Legend.
+ * Tested with the 'Garmin eTrex Legend' device working in 'Text Out' mode.
*
* Protocol info from:
* http://gpsd.berlios.de/vendor-docs/garmin/garmin_simpletext.txt
@@ -84,7 +84,7 @@ o | ----------------------- ------- ------------------------
c | North/South velocity 4 Meters per second in tenths,
i | magnitude ("1234" = 123.4 m/s)
t | ----------------------- ------- ------------------------
-y | Vertical velocity 1 'U' (up) or 'D' (down)
+y | Vertical velocity 1 'U' or 'D' (up/down)
| direction
| ----------------------- ------- ------------------------
| Vertical velocity 4 Meters per second in hundredths,
@@ -157,8 +157,8 @@ static int gar_decode(const char *data, const size_t length, const char *prefix,
char buf[10];
float sign = 1.0;
int preflen = (int)strlen(prefix);
- int offset = 1; /* assume one character prefix (E,W,S,N,U, D, etc) */
- int intresult;
+ int offset = 1; /* assume one character prefix (E,W,S,N,U,D, etc) */
+ long int intresult;
/* splint is buggy here, thinks buf can be a null pointer */
/*@ -mustdefine -nullderef -nullpass @*/
@@ -169,7 +169,7 @@ static int gar_decode(const char *data, const size_t length, const char *prefix,
bzero(buf, (int)sizeof(buf));
(void) strncpy(buf, data, length);
- gpsd_report(LOG_RAW, "Decoded string: %s\n", buf);
+ gpsd_report(LOG_RAW+2, "Decoded string: %s\n", buf);
if (strchr(buf, '_') != NULL) {
/* value is not valid, ignore it */
@@ -206,8 +206,8 @@ static int gar_decode(const char *data, const size_t length, const char *prefix,
}
/*@ +mustdefine +nullderef +nullpass @*/
- intresult = atoi(buf+offset);
- if (intresult == 0) sign = 0.0; /* don't create negatove zero */
+ intresult = atol(buf+offset);
+ if (intresult == 0L) sign = 0.0; /* don't create negative zero */
*result = (double) intresult / dividor * sign;
@@ -219,10 +219,10 @@ static int gar_decode(const char *data, const size_t length, const char *prefix,
* -1: data error
* -2: data not valid
**************************************************************************/
-static int gar_int_decode(const char *data, const size_t length, const int min, const int max, /*@out@*/int *result)
+static int gar_int_decode(const char *data, const size_t length, const unsigned int min, const unsigned int max, /*@out@*/unsigned int *result)
{
- char buf[3];
- int res;
+ char buf[6];
+ unsigned int res;
/*@ -mustdefine @*/
if (length >= sizeof(buf)) {
@@ -232,7 +232,7 @@ static int gar_int_decode(const char *data, const size_t length, const int min,
bzero(buf, (int)sizeof(buf));
(void) strncpy(buf, data, length);
- gpsd_report(LOG_RAW, "Decoded string: %s\n", buf);
+ gpsd_report(LOG_RAW+2, "Decoded string: %s\n", buf);
if (strchr(buf, '_') != NULL) {
/* value is not valid, ignore it */
@@ -250,7 +250,7 @@ static int gar_int_decode(const char *data, const size_t length, const int min,
*result = res;
return 0; /* SUCCESS */
} else {
- gpsd_report(LOG_WARN, "Value %d out of range <%d, %d>\n", res, min, max);
+ gpsd_report(LOG_WARN, "Value %u out of range <%u, %u>\n", res, min, max);
return -1;
}
/*@ +mustdefine +nullpass @*/
@@ -275,18 +275,20 @@ gps_mask_t garmintxt_parse(struct gps_device_t *session)
gpsd_hexdump_wrapper(session->packet.outbuffer,
session->packet.outbuflen, LOG_RAW));
- if (session->packet.outbuflen < 56) {
- gpsd_report(LOG_WARN, "Message too short, rejected.\n");
+ if (session->packet.outbuflen < 54) {
+ /* trailing CR and LF can be ignored; ('@' + 54x 'DATA' + '\r\n') has length 57 */
+ gpsd_report(LOG_WARN, "Message is too short, rejected.\n");
return ONLINE_SET;
}
session->packet.type=GARMINTXT_PACKET;
- strncpy(session->gpsdata.tag, "GTXT", MAXTAGLEN); /* TAG mesage as GTXT, Garmin Simple Text Message; any better idea? */
+ /* TAG message as GTXT, Garmin Simple Text Message */
+ strncpy(session->gpsdata.tag, "GTXT", MAXTAGLEN);
mask |= CYCLE_START_SET; /* only one message, set cycle start */
do {
- int result;
+ unsigned int result;
char *buf = (char *)session->packet.outbuffer+1;
gpsd_report(LOG_PROG, "Timestamp: %.12s\n", buf);
@@ -306,62 +308,79 @@ gps_mask_t garmintxt_parse(struct gps_device_t *session)
if (0 != gar_int_decode(buf+8, 2, 0, 59, &result)) break;
session->driver.nmea.date.tm_min = result;
/* second */
- if (0 != gar_int_decode(buf+10, 2, 0, 59, &result)) break;
+ /* second value can be even 60, occasional leap second */
+ if (0 != gar_int_decode(buf+10, 2, 0, 60, &result)) break;
session->driver.nmea.date.tm_sec = result;
session->driver.nmea.subseconds = 0;
session->gpsdata.fix.time = (double)mkgmtime(&session->driver.nmea.date)+session->driver.nmea.subseconds;
mask |= TIME_SET;
} while (0);
+ /* assume that possition is unknown; if the position is known we will fix status information later */
+ session->gpsdata.fix.mode = MODE_NO_FIX;
+ session->gpsdata.status = STATUS_NO_FIX;
+ mask |= MODE_SET | STATUS_SET;
/* process position */
do {
double lat, lon;
+ unsigned int degfrag;
+ char status;
/* Latitude, [NS]ddmmmmm */
- if (0 != gar_decode((char *) session->packet.outbuffer+13, 8, "NS", 100000.0, &lat)) break;
- /* Longitude, [EW]dddmmmmm */
- if (0 != gar_decode((char *) session->packet.outbuffer+21, 9, "EW", 100000.0, &lon)) break;
+ /* decode degrees of Latitude */
+ if (0 != gar_decode((char *) session->packet.outbuffer+13, 3, "NS", 1.0, &lat)) break;
+ /* decode minutes of Latitude */
+ if (0 != gar_int_decode((char *) session->packet.outbuffer+16, 5, 0, 99999, &degfrag)) break;
+ lat += degfrag * 100.0 / 60.0 / 100000.0;
session->gpsdata.fix.latitude = lat;
+
+ /* Longitude, [EW]dddmmmmm */
+ /* decode degrees of Longitude */
+ if (0 != gar_decode((char *) session->packet.outbuffer+21, 4, "EW", 1.0, &lon)) break;
+ /* decode minutes of Longitude */
+ if (0 != gar_int_decode((char *) session->packet.outbuffer+25, 5, 0, 99999, &degfrag)) break;
+ lon += degfrag * 100.0 / 60.0 / 100000.0;
session->gpsdata.fix.longitude = lon;
+
gpsd_report(LOG_PROG, "Lat: %.5lf, Lon: %.5lf\n", lat, lon);
- mask |= LATLON_SET;
- } while (0);
/* fix mode, GPS status, [gGdDS_] */
- do {
- char status = (char)session->packet.outbuffer[30];
- gpsd_report(LOG_PROG, "GPS fix mode: %c\n", status);
+ status = (char)session->packet.outbuffer[30];
+ gpsd_report(LOG_PROG, "GPS fix mode: %c\n", status);
switch (status) {
- case 'D':
case 'G':
- case 'S': /* DEMO mode, assume 3D position */
+ case 'S': /* 'S' is DEMO mode, assume 3D position */
session->gpsdata.fix.mode = MODE_3D;
session->gpsdata.status = STATUS_FIX;
- if (status == 'D') session->gpsdata.status = STATUS_DGPS_FIX;
break;
- case 'd':
+ case 'D':
+ session->gpsdata.fix.mode = MODE_3D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
case 'g':
session->gpsdata.fix.mode = MODE_2D;
session->gpsdata.status = STATUS_FIX;
- if (status == 'd') session->gpsdata.status = STATUS_DGPS_FIX;
+ break;
+ case 'd':
+ session->gpsdata.fix.mode = MODE_2D;
+ session->gpsdata.status = STATUS_DGPS_FIX;
break;
default:
session->gpsdata.fix.mode = MODE_NO_FIX;
session->gpsdata.status = STATUS_NO_FIX;
}
- mask |= MODE_SET | STATUS_SET;
+ mask |= MODE_SET | STATUS_SET | LATLON_SET;
} while (0);
/* EPH */
do {
double eph;
if (0 != gar_decode((char *) session->packet.outbuffer+31, 3, "", 1.0, &eph)) break;
- eph = eph * (GPSD_CONFIDENCE/CEP50_SIGMA);
- session->gpsdata.fix.eph = eph;
- gpsd_report(LOG_PROG, "HERR: %.1lf\n", eph);
+ session->gpsdata.fix.eph = eph * (GPSD_CONFIDENCE/CEP50_SIGMA);
+ gpsd_report(LOG_PROG, "HERR [m]: %.1lf\n", eph);
mask |= HERR_SET;
} while (0);
diff --git a/drivers.c b/drivers.c
index 5d608c2f..b2937ef3 100644
--- a/drivers.c
+++ b/drivers.c
@@ -55,15 +55,6 @@ gps_mask_t nmea_parse_input(struct gps_device_t *session)
return 0;
} else /* session->packet.type == NMEA_PACKET) */ {
gps_mask_t st = 0;
-#ifdef GARMINTXT_ENABLE
- if (session->packet.outbuflen >= 56) {
- if ((char) *session->packet.outbuffer == '@') {
- /* Garmin Simple Text packet received; it starts with '@' is terminated with \r\n and has length 57 bytes */
- (void)gpsd_switch_driver(session, "Garmin Simple Text");
- return garmintxt_parse(session);
- }
- }
-#endif /* GARMINTXT_ENABLE */
#ifdef OCEANSERVER_ENABLE
if (strncmp((char *)session->packet.outbuffer, "$C", 2)==0 || strncmp((char *)session->packet.outbuffer, "$OHPR", 5)==0) {
@@ -935,7 +926,7 @@ static gps_mask_t garmintxt_parse_input(struct gps_device_t *session)
static const struct gps_type_t garmintxt = {
.type_name = "Garmin Simple Text", /* full name of type */
- .packet_type = RTCM2_PACKET; /* associated lexer packet type */
+ .packet_type = GARMINTXT_PACKET, /* associated lexer packet type */
.trigger = NULL, /* no recognition string */
.channels = 0, /* not used */
.probe_wakeup = NULL, /* no wakeup to be done before hunt */
diff --git a/packet.c b/packet.c
index 3f3641c5..3714fc0e 100644
--- a/packet.c
+++ b/packet.c
@@ -123,7 +123,7 @@ static void nextstate(struct gps_packet_t *lexer,
#endif /* NMEA_ENABLE */
#if defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE)
if (c == '@') {
- lexer->state = TNT_LEADER;
+ lexer->state = GTXT_LEADER;
break;
}
#endif
@@ -263,10 +263,25 @@ static void nextstate(struct gps_packet_t *lexer,
lexer->state = GROUND_STATE;
break;
#if defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE)
- case TNT_LEADER:
- lexer->state = NMEA_LEADER_END;
+ case GTXT_LEADER:
+ if (c == '\r')
+ /* stay in this state, next character should be '\n' */
+ /* in the theory we can stop search here and don't wait for '\n' */
+ lexer->state = GTXT_LEADER;
+ else if (c == '\n')
+ /* end of packet found */
+ lexer->state = GTXT_RECOGNIZED;
+#ifdef TNT_ENABLE
+ else if (c == '*')
+ /* TNT has similar structure like NMEA packet, '*' before optional checksum ends the packet */
+ /* '*' cannot be received from GARMIN working in TEXT mode, use this diference for selection */
+ /* this is not GARMIN TEXT packet, could be TNT */
+ lexer->state = NMEA_LEADER_END;
+#endif /* TNT_ENABLE */
+ else if (!isprint(c))
+ lexer->state = GROUND_STATE;
break;
-#endif
+#endif /* defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE) */
case NMEA_LEADER_END:
if (c == '\r')
lexer->state = NMEA_CR;
@@ -1351,6 +1366,19 @@ void packet_parse(struct gps_packet_t *lexer)
break;
}
#endif /* RTCM104V2_ENABLE */
+#ifdef GARMINTXT_ENABLE
+ else if (lexer->state == GTXT_RECOGNIZED) {
+ size_t packetlen = lexer->inbufptr - lexer->inbuffer;
+ if (57 <= packetlen) {
+ packet_accept(lexer, GARMINTXT_PACKET);
+ packet_discard(lexer);
+ lexer->state = GROUND_STATE;
+ break;
+ } else {
+ lexer->state = GROUND_STATE;
+ }
+ }
+#endif
} /* while */
}
#undef getword
diff --git a/packet_states.h b/packet_states.h
index c718ef7c..778770e8 100644
--- a/packet_states.h
+++ b/packet_states.h
@@ -66,8 +66,9 @@
#endif /* ZODIAC_ENABLE */
#if defined(TNT_ENABLE) || defined(GARMINTXT_ENABLE)
- TNT_LEADER, /* saw True North status leader '@' */
+ GTXT_LEADER, /* saw True North status leader '@' */
/* Garmin Simple Text starts with @ leader */
+ GTXT_RECOGNIZED, /* */
#endif
#ifdef EVERMORE_ENABLE