2 * This file is part of mapper
4 * Copyright (C) 2007 Kaj-Michael Lang
5 * Copyright (C) 2006-2007 John Costigan.
7 * POI and GPS-Info code originally written by Cezary Jackiewicz.
9 * Default map data provided by http://www.openstreetmap.org/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
36 #include <glib/gstdio.h>
39 #include <libxml/parser.h>
44 #include "mapper-types.h"
45 #include "ui-common.h"
49 #include "gps-panels.h"
53 #include "gtkcompass.h"
55 #define GPS_FILTER_MAX_SKIP (10)
56 static gint track_drop_cnt=0;
59 channel_cb_error(GIOChannel *src, GIOCondition condition, gpointer data)
61 /* An error has occurred - re-connect(). */
64 _speed_excess = FALSE;
66 if (_conn_state > RCVR_OFF) {
67 gps_conn_set_state(RCVR_DOWN);
75 channel_parse_rmc(gchar * sentence)
77 /* Recommended Minimum Navigation Information C
79 * 2) Status, V=Navigation receiver warning A=Valid
84 * 7) Speed over ground, knots
85 * 8) Track made good, degrees true
87 * 10) Magnetic Variation, degrees
89 * 12) FAA mode indicator (NMEA 2.3 and later)
92 gchar *token, *dpoint, *gpsdate = NULL;
95 gboolean newly_fixed = FALSE;
96 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
101 token = strsep(&sentence, DELIM);
105 token = strsep(&sentence, DELIM);
106 /* Token is now Status. */
107 if (token && *token == 'A') {
109 if (_conn_state != RCVR_FIXED) {
111 gps_conn_set_state(RCVR_FIXED);
114 /* Data is invalid - not enough satellites?. */
115 if (_conn_state != RCVR_UP) {
116 gps_conn_set_state(RCVR_UP);
121 /* Parse the latitude. */
122 token = strsep(&sentence, DELIM);
123 if (token && *token) {
124 dpoint = strchr(token, '.');
125 if (!dpoint) /* handle buggy NMEA */
126 dpoint = token + strlen(token);
127 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
129 MACRO_PARSE_INT(tmpi, token);
130 _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
134 token = strsep(&sentence, DELIM);
135 if (token && *token == 'S')
136 _gps.lat = -_gps.lat;
138 /* Parse the longitude. */
139 token = strsep(&sentence, DELIM);
140 if (token && *token) {
141 dpoint = strchr(token, '.');
142 if (!dpoint) /* handle buggy NMEA */
143 dpoint = token + strlen(token);
144 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
146 MACRO_PARSE_INT(tmpi, token);
147 _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
151 token = strsep(&sentence, DELIM);
152 if (token && *token == 'W')
153 _gps.lon = -_gps.lon;
155 /* Parse speed over ground, knots. */
156 token = strsep(&sentence, DELIM);
157 if (token && *token) {
158 MACRO_PARSE_FLOAT(_gps.speed, token);
160 _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
161 gps_display_data_speed(info_banner.speed, _gps.speed);
165 /* Parse heading, degrees from true north. */
166 token = strsep(&sentence, DELIM);
167 if (token && *token) {
168 MACRO_PARSE_FLOAT(_gps.heading, token);
172 token = strsep(&sentence, DELIM);
173 if (token && *token && gpsdate) {
175 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
176 strcat(gpsdate, token);
177 strptime(gpsdate, "%H%M%S%d%m%y", &time);
178 _pos.time = mktime(&time) + _gmtoffset;
180 _pos.time = time(NULL);
182 /* Add new data to track only if we have a fix */
185 /* XXX: Set filter logic somewhere else */
187 if ((_conn_state == RCVR_FIXED) && (_track_store==TRUE)) {
188 if ((_gps_filter==TRUE) && (track_drop_cnt<_filter_maxdrop)) {
189 gps_integerize_data(&_gps, &_pos);
190 if ( (_gps.hdop<_filter_hdop || _filter_hdop==0.0) &&
191 (_gps.vdop<_filter_vdop || _filter_vdop==0.0) &&
192 (fabs(_gps.heading-_gps.lheading)>_filter_angle || _filter_angle==0.0 ) &&
193 (_map_location_known==TRUE && (_map_location_dist<_filter_osm || _map_location_dist==0.0)) ) {
194 track_add(_pos.time, newly_fixed);
195 _gps.lheading=_gps.heading;
199 g_printf("*** Filtering by: [%s %s %s %s] A: %f (%d)\n",
200 _gps.hdop>_filter_hdop ? "HDOP" : "-",
201 _gps.vdop>_filter_vdop ? "VDOP" : "-",
202 (fabs(_gps.heading-_gps.lheading)<_filter_angle) ? "Angle" : "-",
203 (_map_location_known==TRUE && (_map_location_dist>_filter_osm)) ? "OSM" : "-",
204 fabs(_gps.heading-_gps.lheading), track_drop_cnt);
209 gps_integerize_data(&_gps, &_pos);
210 track_add(_pos.time, newly_fixed);
211 _gps.lheading=_gps.heading;
218 channel_parse_gga(gchar * sentence)
220 /* GGA Global Positioning System Fix Data
231 4 = Real Time Kinematic
233 6 = estimated (dead reckoning) (2.3 feature)
234 7 = Manual input mode
236 7. Number of satellites being tracked
237 8. Horizontal dilution of position
238 9. Altitude, Meters, above mean sea level
239 10. Alt unit (meters)
240 11. Height of geoid (mean sea level) above WGS84 ellipsoid
242 13. (empty field) time in seconds since last DGPS update
243 14. (empty field) DGPS station ID number
244 15. the checksum data
247 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
252 token = strsep(&sentence, DELIM);
254 token = strsep(&sentence, DELIM);
256 token = strsep(&sentence, DELIM);
258 token = strsep(&sentence, DELIM);
260 token = strsep(&sentence, DELIM);
262 /* Parse Fix quality */
263 token = strsep(&sentence, DELIM);
265 MACRO_PARSE_INT(_gps.fixquality, token);
267 /* Skip number of satellites */
268 token = strsep(&sentence, DELIM);
270 /* Parse Horizontal dilution of position */
271 token = strsep(&sentence, DELIM);
273 MACRO_PARSE_INT(_gps.hdop, token);
276 token = strsep(&sentence, DELIM);
277 if (token && *token) {
278 MACRO_PARSE_FLOAT(_pos.altitude, token);
284 channel_parse_gsa(gchar * sentence)
286 /* GPS DOP and active satellites
287 * 1) Auto selection of 2D or 3D fix (M = manual)
288 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
289 * 3) PRNs of satellites used for fix
291 * 4) PDOP (dilution of precision)
292 * 5) Horizontal dilution of precision (HDOP)
293 * 6) Vertical dilution of precision (VDOP)
300 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
304 /* Skip Auto selection. */
305 token = strsep(&sentence, DELIM);
308 token = strsep(&sentence, DELIM);
310 MACRO_PARSE_INT(_gps.fix, token);
313 for (i = 0; i < 12; i++) {
315 token = strsep(&sentence, DELIM);
321 _gps.sat[i].fix=FALSE;
325 for (si=0;si<12;si++) {
326 if (_gps.sat[i].prn==satforfix[si])
327 _gps.sat[i].fix=TRUE;
331 token = strsep(&sentence, DELIM);
333 MACRO_PARSE_FLOAT(_gps.pdop, token);
336 token = strsep(&sentence, DELIM);
338 MACRO_PARSE_FLOAT(_gps.hdop, token);
341 token = strsep(&sentence, DELIM);
343 MACRO_PARSE_FLOAT(_gps.vdop, token);
347 channel_parse_gsv(gchar * sentence)
349 /* Must be GSV - Satellites in view
350 * 1) total number of messages
352 * 3) satellites in view
353 * 4) satellite number
354 * 5) elevation in degrees (0-90)
355 * 6) azimuth in degrees to true north (0-359)
356 * 7) SNR in dB (0-99)
357 * more satellite infos like 4)-7)
361 guint msgcnt = 0, nummsgs = 0;
362 static guint running_total = 0;
363 static guint num_sats_used = 0;
364 static guint satcnt = 0;
365 g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
367 /* Parse number of messages. */
368 token = strsep(&sentence, DELIM);
370 MACRO_PARSE_INT(nummsgs, token);
372 /* Parse message number. */
373 token = strsep(&sentence, DELIM);
375 MACRO_PARSE_INT(msgcnt, token);
377 /* Parse number of satellites in view. */
378 token = strsep(&sentence, DELIM);
379 if (token && *token) {
380 MACRO_PARSE_INT(_gps.satinview, token);
381 if (_gps.satinview > 12) /* Handle buggy NMEA. */
385 /* Loop until there are no more satellites to parse. */
386 while (sentence && satcnt < 12) {
387 /* Get token for Satellite Number. */
388 token = strsep(&sentence, DELIM);
390 _gps.sat[satcnt].prn = atoi(token);
392 /* Get token for elevation in degrees (0-90). */
393 token = strsep(&sentence, DELIM);
395 _gps.sat[satcnt].elevation = atoi(token);
397 /* Get token for azimuth in degrees to true north (0-359). */
398 token = strsep(&sentence, DELIM);
400 _gps.sat[satcnt].azimuth = atoi(token);
402 /* Get token for SNR. */
403 token = strsep(&sentence, DELIM);
404 if (token && *token && (_gps.sat[satcnt].snr = atoi(token))) {
405 /* SNR is non-zero - add to total and count as used. */
406 running_total += _gps.sat[satcnt].snr;
412 if (msgcnt == nummsgs) {
413 /* This is the last message. Calculate signal strength. */
415 if (_conn_state == RCVR_UP) {
416 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
417 BOUND(fraction, 0.0, 1.0);
418 gps_conn_set_progress(fraction);
425 /* Keep awake while they watch the progress bar. */
431 channel_parse_buffer(gpointer data)
433 gchar *buf=(gchar *)data;
435 if (!strncmp(buf + 3, "GSV", 3)) {
436 if (_conn_state == RCVR_UP)
437 channel_parse_gsv(buf + 7);
438 } else if (!strncmp(buf + 3, "RMC", 3))
439 channel_parse_rmc(buf + 7);
440 else if (!strncmp(buf + 3, "GGA", 3))
441 channel_parse_gga(buf + 7);
442 else if (!strncmp(buf + 3, "GSA", 3))
443 channel_parse_gsa(buf + 7);
444 else g_print("Unknown NMEA: [%s]\n", buf);
452 gps_display_details();
458 channel_cb_input(GIOChannel *src, GIOCondition condition, gpointer data)
464 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
466 status=g_io_channel_read_chars(_channel, _gps_read_buf_curr, _gps_read_buf_last - _gps_read_buf_curr, &bytes_read, NULL);
469 case G_IO_STATUS_NORMAL:
470 _gps_read_buf_curr += bytes_read;
471 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
472 while ((eol = strchr(_gps_read_buf, '\n'))) {
473 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
475 if (*_gps_read_buf == '$') {
476 /* This is the beginning of a sentence; okay to parse. */
477 *eol = '\0'; /* overwrite \n with \0 */
478 while (*sptr && *sptr != '*')
481 /* If we're at a \0 (meaning there is no checksum), or if the
482 * checksum is good, then parse the sentence. */
483 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
487 *sptr = '\0'; /* take checksum out of the buffer. */
489 data=g_strdup(_gps_read_buf);
490 g_idle_add_full(G_PRIORITY_HIGH_IDLE, (GSourceFunc)channel_parse_buffer, data, NULL);
492 /* There was a checksum, and it was bad. */
493 g_printerr("%s: Bad checksum in NMEA sentence:\n%s\n", __PRETTY_FUNCTION__, _gps_read_buf);
497 /* If eol is at or after (_gps_read_buf_curr - 1) */
498 if (eol >= (_gps_read_buf_curr - 1)) {
499 /* Last read was a newline - reset read buffer */
500 _gps_read_buf_curr = _gps_read_buf;
501 *_gps_read_buf_curr = '\0';
503 /* Move the next line to the front of the buffer. */
504 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
505 /* Subtract _curr so that it's pointing at the new \0. */
506 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
510 case G_IO_STATUS_ERROR:
511 case G_IO_STATUS_EOF:
513 rcvr_connect_later();
516 case G_IO_STATUS_AGAIN:
520 g_assert_not_reached();
524 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);