13 #include <glib/gstdio.h>
16 #include <libxml/parser.h>
21 #include "mapper-types.h"
22 #include "ui-common.h"
26 #include "gps-panels.h"
30 #include "gtkcompass.h"
33 #define GPS_FILTER_MAX_SKIP (10)
34 static gint track_drop_cnt=0;
37 channel_cb_error(GIOChannel * src, GIOCondition condition, gpointer data)
39 /* An error has occurred - re-connect(). */
42 _speed_excess = FALSE;
44 if (_conn_state > RCVR_OFF) {
45 gps_conn_set_state(RCVR_DOWN);
54 channel_parse_rmc(gchar * sentence)
56 /* Recommended Minimum Navigation Information C
58 * 2) Status, V=Navigation receiver warning A=Valid
63 * 7) Speed over ground, knots
64 * 8) Track made good, degrees true
66 * 10) Magnetic Variation, degrees
68 * 12) FAA mode indicator (NMEA 2.3 and later)
71 gchar *token, *dpoint, *gpsdate = NULL;
74 gboolean newly_fixed = FALSE;
75 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
80 token = strsep(&sentence, DELIM);
84 token = strsep(&sentence, DELIM);
85 /* Token is now Status. */
86 if (token && *token == 'A') {
88 if (_conn_state != RCVR_FIXED) {
90 gps_conn_set_state(RCVR_FIXED);
93 /* Data is invalid - not enough satellites?. */
94 if (_conn_state != RCVR_UP) {
95 gps_conn_set_state(RCVR_UP);
100 /* Parse the latitude. */
101 token = strsep(&sentence, DELIM);
102 if (token && *token) {
103 dpoint = strchr(token, '.');
104 if (!dpoint) /* handle buggy NMEA */
105 dpoint = token + strlen(token);
106 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
108 MACRO_PARSE_INT(tmpi, token);
109 _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
113 token = strsep(&sentence, DELIM);
114 if (token && *token == 'S')
115 _gps.lat = -_gps.lat;
117 /* Parse the longitude. */
118 token = strsep(&sentence, DELIM);
119 if (token && *token) {
120 dpoint = strchr(token, '.');
121 if (!dpoint) /* handle buggy NMEA */
122 dpoint = token + strlen(token);
123 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
125 MACRO_PARSE_INT(tmpi, token);
126 _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
130 token = strsep(&sentence, DELIM);
131 if (token && *token == 'W')
132 _gps.lon = -_gps.lon;
134 /* Parse speed over ground, knots. */
135 token = strsep(&sentence, DELIM);
136 if (token && *token) {
137 MACRO_PARSE_FLOAT(_gps.speed, token);
139 _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
140 gps_display_data_speed(info_banner.speed, _gps.speed);
144 /* Parse heading, degrees from true north. */
145 token = strsep(&sentence, DELIM);
146 if (token && *token) {
147 MACRO_PARSE_FLOAT(_gps.heading, token);
151 token = strsep(&sentence, DELIM);
152 if (token && *token && gpsdate) {
154 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
155 strcat(gpsdate, token);
156 strptime(gpsdate, "%H%M%S%d%m%y", &time);
157 _pos.time = mktime(&time) + _gmtoffset;
159 _pos.time = time(NULL);
161 /* Add new data to track only if we have a fix */
164 /* XXX: Set filter logic somewhere else */
166 if ((_conn_state == RCVR_FIXED) && (_track_store==TRUE)) {
167 if ((_gps_filter==TRUE) && (track_drop_cnt<GPS_FILTER_MAX_SKIP)) {
168 integerize_data(&_gps, &_pos);
169 if ( (_gps.hdop<_filter_hdop || _filter_hdop==0.0) &&
170 (_gps.vdop<_filter_vdop || _filter_vdop==0.0) &&
171 (fabs(_gps.heading-_gps.lheading)>_filter_angle || _filter_angle==0.0 ) &&
172 (_map_location_known==TRUE && (_map_location_dist<_filter_osm || _map_location_dist==0.0)) ) {
173 track_add(_pos.time, newly_fixed);
174 _gps.lheading=_gps.heading;
178 g_printf("*** Filtering by: [%s %s %s %s] A: %f (%d)\n",
179 _gps.hdop>_filter_hdop ? "HDOP" : "-",
180 _gps.vdop>_filter_vdop ? "VDOP" : "-",
181 (fabs(_gps.heading-_gps.lheading)<_filter_angle) ? "Angle" : "-",
182 (_map_location_known==TRUE && (_map_location_dist>_filter_osm)) ? "OSM" : "-",
183 fabs(_gps.heading-_gps.lheading), track_drop_cnt);
188 integerize_data(&_gps, &_pos);
189 track_add(_pos.time, newly_fixed);
190 _gps.lheading=_gps.heading;
195 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
199 channel_parse_gga(gchar * sentence)
201 /* GGA Global Positioning System Fix Data
212 4 = Real Time Kinematic
214 6 = estimated (dead reckoning) (2.3 feature)
215 7 = Manual input mode
217 7. Number of satellites being tracked
218 8. Horizontal dilution of position
219 9. Altitude, Meters, above mean sea level
220 10. Alt unit (meters)
221 11. Height of geoid (mean sea level) above WGS84 ellipsoid
223 13. (empty field) time in seconds since last DGPS update
224 14. (empty field) DGPS station ID number
225 15. the checksum data
228 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
233 token = strsep(&sentence, DELIM);
235 token = strsep(&sentence, DELIM);
237 token = strsep(&sentence, DELIM);
239 token = strsep(&sentence, DELIM);
241 token = strsep(&sentence, DELIM);
243 /* Parse Fix quality */
244 token = strsep(&sentence, DELIM);
246 MACRO_PARSE_INT(_gps.fixquality, token);
248 /* Skip number of satellites */
249 token = strsep(&sentence, DELIM);
251 /* Parse Horizontal dilution of position */
252 token = strsep(&sentence, DELIM);
254 MACRO_PARSE_INT(_gps.hdop, token);
257 token = strsep(&sentence, DELIM);
258 if (token && *token) {
259 MACRO_PARSE_FLOAT(_pos.altitude, token);
263 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
267 channel_parse_gsa(gchar * sentence)
269 /* GPS DOP and active satellites
270 * 1) Auto selection of 2D or 3D fix (M = manual)
271 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
272 * 3) PRNs of satellites used for fix
274 * 4) PDOP (dilution of precision)
275 * 5) Horizontal dilution of precision (HDOP)
276 * 6) Vertical dilution of precision (VDOP)
283 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
287 /* Skip Auto selection. */
288 token = strsep(&sentence, DELIM);
291 token = strsep(&sentence, DELIM);
293 MACRO_PARSE_INT(_gps.fix, token);
296 for (i = 0; i < 12; i++) {
298 token = strsep(&sentence, DELIM);
304 _gps.sat[i].fix=FALSE;
308 for (si=0;si<12;si++) {
309 if (_gps.sat[i].prn==satforfix[si])
310 _gps.sat[i].fix=TRUE;
314 token = strsep(&sentence, DELIM);
316 MACRO_PARSE_FLOAT(_gps.pdop, token);
319 token = strsep(&sentence, DELIM);
321 MACRO_PARSE_FLOAT(_gps.hdop, token);
324 token = strsep(&sentence, DELIM);
326 MACRO_PARSE_FLOAT(_gps.vdop, token);
328 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
332 channel_parse_gsv(gchar * sentence)
334 /* Must be GSV - Satellites in view
335 * 1) total number of messages
337 * 3) satellites in view
338 * 4) satellite number
339 * 5) elevation in degrees (0-90)
340 * 6) azimuth in degrees to true north (0-359)
341 * 7) SNR in dB (0-99)
342 * more satellite infos like 4)-7)
346 guint msgcnt = 0, nummsgs = 0;
347 static guint running_total = 0;
348 static guint num_sats_used = 0;
349 static guint satcnt = 0;
350 g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
352 /* Parse number of messages. */
353 token = strsep(&sentence, DELIM);
355 MACRO_PARSE_INT(nummsgs, token);
357 /* Parse message number. */
358 token = strsep(&sentence, DELIM);
360 MACRO_PARSE_INT(msgcnt, token);
362 /* Parse number of satellites in view. */
363 token = strsep(&sentence, DELIM);
364 if (token && *token) {
365 MACRO_PARSE_INT(_gps.satinview, token);
366 if (_gps.satinview > 12) /* Handle buggy NMEA. */
370 /* Loop until there are no more satellites to parse. */
371 while (sentence && satcnt < 12) {
372 /* Get token for Satellite Number. */
373 token = strsep(&sentence, DELIM);
375 _gps.sat[satcnt].prn = atoi(token);
377 /* Get token for elevation in degrees (0-90). */
378 token = strsep(&sentence, DELIM);
380 _gps.sat[satcnt].elevation = atoi(token);
382 /* Get token for azimuth in degrees to true north (0-359). */
383 token = strsep(&sentence, DELIM);
385 _gps.sat[satcnt].azimuth = atoi(token);
387 /* Get token for SNR. */
388 token = strsep(&sentence, DELIM);
389 if (token && *token && (_gps.sat[satcnt].snr = atoi(token))) {
390 /* SNR is non-zero - add to total and count as used. */
391 running_total += _gps.sat[satcnt].snr;
397 if (msgcnt == nummsgs) {
398 /* This is the last message. Calculate signal strength. */
400 if (_conn_state == RCVR_UP) {
401 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
402 BOUND(fraction, 0.0, 1.0);
403 gps_conn_set_progress(fraction);
410 /* Keep awake while they watch the progress bar. */
414 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
418 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
421 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
423 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
424 _gps_read_buf_curr, _gps_read_buf_last - _gps_read_buf_curr,
425 &bytes_read, NULL)) {
428 _gps_read_buf_curr += bytes_read;
429 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
430 while ((eol = strchr(_gps_read_buf, '\n'))) {
431 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
433 if (*_gps_read_buf == '$') {
434 /* This is the beginning of a sentence; okay to parse. */
435 *eol = '\0'; /* overwrite \n with \0 */
436 while (*sptr && *sptr != '*')
439 /* If we're at a \0 (meaning there is no checksum), or if the
440 * checksum is good, then parse the sentence. */
441 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
443 *sptr = '\0'; /* take checksum out of the buffer. */
444 if (!strncmp(_gps_read_buf + 3, "GSV", 3)) {
445 if (_conn_state == RCVR_UP)
446 channel_parse_gsv(_gps_read_buf + 7);
447 } else if (!strncmp(_gps_read_buf + 3, "RMC", 3))
448 channel_parse_rmc(_gps_read_buf + 7);
449 else if (!strncmp(_gps_read_buf + 3, "GGA", 3))
450 channel_parse_gga(_gps_read_buf + 7);
451 else if (!strncmp(_gps_read_buf + 3, "GSA", 3))
452 channel_parse_gsa(_gps_read_buf + 7);
453 else g_print("Unknown NMEA: [%s]\n", _gps_read_buf);
459 gps_display_details();
461 /* There was a checksum, and it was bad. */
463 ("%s: Bad checksum in NMEA sentence:\n%s\n",
469 /* If eol is at or after (_gps_read_buf_curr - 1) */
470 if (eol >= (_gps_read_buf_curr - 1)) {
471 /* Last read was a newline - reset read buffer */
472 _gps_read_buf_curr = _gps_read_buf;
473 *_gps_read_buf_curr = '\0';
475 /* Move the next line to the front of the buffer. */
476 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
477 /* Subtract _curr so that it's pointing at the new \0. */
478 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
483 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);