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"
32 #define GPS_FILTER_MAX_SKIP (10)
33 static gint track_drop_cnt=0;
36 channel_cb_error(GIOChannel * src, GIOCondition condition, gpointer data)
38 printf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
40 /* An error has occurred - re-connect(). */
43 _speed_excess = FALSE;
45 if (_conn_state > RCVR_OFF) {
46 set_conn_state(RCVR_DOWN);
51 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
56 channel_parse_rmc(gchar * sentence)
58 /* Recommended Minimum Navigation Information C
60 * 2) Status, V=Navigation receiver warning A=Valid
65 * 7) Speed over ground, knots
66 * 8) Track made good, degrees true
68 * 10) Magnetic Variation, degrees
70 * 12) FAA mode indicator (NMEA 2.3 and later)
73 gchar *token, *dpoint, *gpsdate = NULL;
76 gboolean newly_fixed = FALSE;
77 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
82 token = strsep(&sentence, DELIM);
86 token = strsep(&sentence, DELIM);
87 /* Token is now Status. */
88 if (token && *token == 'A') {
90 if (_conn_state != RCVR_FIXED) {
92 set_conn_state(RCVR_FIXED);
95 /* Data is invalid - not enough satellites?. */
96 if (_conn_state != RCVR_UP) {
97 set_conn_state(RCVR_UP);
102 /* Parse the latitude. */
103 token = strsep(&sentence, DELIM);
104 if (token && *token) {
105 dpoint = strchr(token, '.');
106 if (!dpoint) /* handle buggy NMEA */
107 dpoint = token + strlen(token);
108 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
110 MACRO_PARSE_INT(tmpi, token);
111 _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
115 token = strsep(&sentence, DELIM);
116 if (token && *token == 'S')
117 _gps.lat = -_gps.lat;
119 /* Parse the longitude. */
120 token = strsep(&sentence, DELIM);
121 if (token && *token) {
122 dpoint = strchr(token, '.');
123 if (!dpoint) /* handle buggy NMEA */
124 dpoint = token + strlen(token);
125 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
127 MACRO_PARSE_INT(tmpi, token);
128 _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
132 token = strsep(&sentence, DELIM);
133 if (token && *token == 'W')
134 _gps.lon = -_gps.lon;
136 /* Parse speed over ground, knots. */
137 token = strsep(&sentence, DELIM);
138 if (token && *token) {
139 MACRO_PARSE_FLOAT(_gps.speed, token);
141 _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
142 gps_display_data_speed(info_banner.speed, _gps.speed);
146 /* Parse heading, degrees from true north. */
147 token = strsep(&sentence, DELIM);
148 if (token && *token) {
149 MACRO_PARSE_FLOAT(_gps.heading, token);
153 token = strsep(&sentence, DELIM);
154 if (token && *token && gpsdate) {
156 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
157 strcat(gpsdate, token);
158 strptime(gpsdate, "%H%M%S%d%m%y", &time);
159 _pos.time = mktime(&time) + _gmtoffset;
161 _pos.time = time(NULL);
163 /* Add new data to track only if we have a fix */
166 /* XXX: Set filter logic somewhere else */
168 if ((_conn_state == RCVR_FIXED) && (_track_store==TRUE)) {
169 if ((_gps_filter==TRUE) && (track_drop_cnt<GPS_FILTER_MAX_SKIP)) {
170 integerize_data(&_gps, &_pos);
171 if ( (_gps.hdop<_filter_hdop || _filter_hdop==0.0) &&
172 (_gps.vdop<_filter_vdop || _filter_vdop==0.0) &&
173 (fabs(_gps.heading-_gps.lheading)>_filter_angle || _filter_angle==0.0 ) &&
174 (_map_location_known==TRUE && (_map_location_dist<_filter_osm || _map_location_dist==0.0)) ) {
175 track_add(_pos.time, newly_fixed);
176 _gps.lheading=_gps.heading;
180 g_printf("*** Filtering by: [%s %s %s %s] A: %f (%d)\n",
181 _gps.hdop>_filter_hdop ? "HDOP" : "-",
182 _gps.vdop>_filter_vdop ? "VDOP" : "-",
183 (fabs(_gps.heading-_gps.lheading)<_filter_angle) ? "Angle" : "-",
184 (_map_location_known==TRUE && (_map_location_dist>_filter_osm)) ? "OSM" : "-",
185 fabs(_gps.heading-_gps.lheading), track_drop_cnt);
190 integerize_data(&_gps, &_pos);
191 track_add(_pos.time, newly_fixed);
192 _gps.lheading=_gps.heading;
197 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
201 channel_parse_gga(gchar * sentence)
203 /* GGA Global Positioning System Fix Data
214 4 = Real Time Kinematic
216 6 = estimated (dead reckoning) (2.3 feature)
217 7 = Manual input mode
219 7. Number of satellites being tracked
220 8. Horizontal dilution of position
221 9. Altitude, Meters, above mean sea level
222 10. Alt unit (meters)
223 11. Height of geoid (mean sea level) above WGS84 ellipsoid
225 13. (empty field) time in seconds since last DGPS update
226 14. (empty field) DGPS station ID number
227 15. the checksum data
230 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
235 token = strsep(&sentence, DELIM);
237 token = strsep(&sentence, DELIM);
239 token = strsep(&sentence, DELIM);
241 token = strsep(&sentence, DELIM);
243 token = strsep(&sentence, DELIM);
245 /* Parse Fix quality */
246 token = strsep(&sentence, DELIM);
248 MACRO_PARSE_INT(_gps.fixquality, token);
250 /* Skip number of satellites */
251 token = strsep(&sentence, DELIM);
253 /* Parse Horizontal dilution of position */
254 token = strsep(&sentence, DELIM);
256 MACRO_PARSE_INT(_gps.hdop, token);
259 token = strsep(&sentence, DELIM);
260 if (token && *token) {
261 MACRO_PARSE_FLOAT(_pos.altitude, token);
265 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
269 channel_parse_gsa(gchar * sentence)
271 /* GPS DOP and active satellites
272 * 1) Auto selection of 2D or 3D fix (M = manual)
273 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
274 * 3) PRNs of satellites used for fix
276 * 4) PDOP (dilution of precision)
277 * 5) Horizontal dilution of precision (HDOP)
278 * 6) Vertical dilution of precision (VDOP)
285 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
289 /* Skip Auto selection. */
290 token = strsep(&sentence, DELIM);
293 token = strsep(&sentence, DELIM);
295 MACRO_PARSE_INT(_gps.fix, token);
298 for (i = 0; i < 12; i++) {
300 token = strsep(&sentence, DELIM);
306 _gps.sat[i].fix=FALSE;
310 for (si=0;si<12;si++) {
311 if (_gps.sat[i].prn==satforfix[si])
312 _gps.sat[i].fix=TRUE;
316 token = strsep(&sentence, DELIM);
318 MACRO_PARSE_FLOAT(_gps.pdop, token);
321 token = strsep(&sentence, DELIM);
323 MACRO_PARSE_FLOAT(_gps.hdop, token);
326 token = strsep(&sentence, DELIM);
328 MACRO_PARSE_FLOAT(_gps.vdop, token);
330 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
334 channel_parse_gsv(gchar * sentence)
336 /* Must be GSV - Satellites in view
337 * 1) total number of messages
339 * 3) satellites in view
340 * 4) satellite number
341 * 5) elevation in degrees (0-90)
342 * 6) azimuth in degrees to true north (0-359)
343 * 7) SNR in dB (0-99)
344 * more satellite infos like 4)-7)
348 guint msgcnt = 0, nummsgs = 0;
349 static guint running_total = 0;
350 static guint num_sats_used = 0;
351 static guint satcnt = 0;
352 g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
354 /* Parse number of messages. */
355 token = strsep(&sentence, DELIM);
357 MACRO_PARSE_INT(nummsgs, token);
359 /* Parse message number. */
360 token = strsep(&sentence, DELIM);
362 MACRO_PARSE_INT(msgcnt, token);
364 /* Parse number of satellites in view. */
365 token = strsep(&sentence, DELIM);
366 if (token && *token) {
367 MACRO_PARSE_INT(_gps.satinview, token);
368 if (_gps.satinview > 12) /* Handle buggy NMEA. */
372 /* Loop until there are no more satellites to parse. */
373 while (sentence && satcnt < 12) {
374 /* Get token for Satellite Number. */
375 token = strsep(&sentence, DELIM);
377 _gps.sat[satcnt].prn = atoi(token);
379 /* Get token for elevation in degrees (0-90). */
380 token = strsep(&sentence, DELIM);
382 _gps.sat[satcnt].elevation = atoi(token);
384 /* Get token for azimuth in degrees to true north (0-359). */
385 token = strsep(&sentence, DELIM);
387 _gps.sat[satcnt].azimuth = atoi(token);
389 /* Get token for SNR. */
390 token = strsep(&sentence, DELIM);
391 if (token && *token && (_gps.sat[satcnt].snr = atoi(token))) {
392 /* SNR is non-zero - add to total and count as used. */
393 running_total += _gps.sat[satcnt].snr;
399 if (msgcnt == nummsgs) {
400 /* This is the last message. Calculate signal strength. */
402 if (_conn_state == RCVR_UP) {
403 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
404 BOUND(fraction, 0.0, 1.0);
405 set_fix_progress(fraction);
412 /* Keep awake while they watch the progress bar. */
416 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
420 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
423 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
425 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
426 _gps_read_buf_curr, _gps_read_buf_last - _gps_read_buf_curr,
427 &bytes_read, NULL)) {
430 _gps_read_buf_curr += bytes_read;
431 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
432 while ((eol = strchr(_gps_read_buf, '\n'))) {
433 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
435 if (*_gps_read_buf == '$') {
436 /* This is the beginning of a sentence; okay to parse. */
437 *eol = '\0'; /* overwrite \n with \0 */
438 while (*sptr && *sptr != '*')
441 /* If we're at a \0 (meaning there is no checksum), or if the
442 * checksum is good, then parse the sentence. */
443 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
445 *sptr = '\0'; /* take checksum out of the buffer. */
446 if (!strncmp(_gps_read_buf + 3, "GSV", 3)) {
447 if (_conn_state == RCVR_UP)
448 channel_parse_gsv(_gps_read_buf + 7);
449 } else if (!strncmp(_gps_read_buf + 3, "RMC", 3))
450 channel_parse_rmc(_gps_read_buf + 7);
451 else if (!strncmp(_gps_read_buf + 3, "GGA", 3))
452 channel_parse_gga(_gps_read_buf + 7);
453 else if (!strncmp(_gps_read_buf + 3, "GSA", 3))
454 channel_parse_gsa(_gps_read_buf + 7);
455 else g_print("Unknown NMEA: [%s]\n", _gps_read_buf);
461 gps_display_details();
463 /* There was a checksum, and it was bad. */
465 ("%s: Bad checksum in NMEA sentence:\n%s\n",
471 /* If eol is at or after (_gps_read_buf_curr - 1) */
472 if (eol >= (_gps_read_buf_curr - 1)) {
473 /* Last read was a newline - reset read buffer */
474 _gps_read_buf_curr = _gps_read_buf;
475 *_gps_read_buf_curr = '\0';
477 /* Move the next line to the front of the buffer. */
478 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
479 /* Subtract _curr so that it's pointing at the new \0. */
480 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
485 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);