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"
29 #define GPS_FILTER_MAX_SKIP (10)
30 static gint track_drop_cnt=0;
33 channel_cb_error(GIOChannel * src, GIOCondition condition, gpointer data)
35 printf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
37 /* An error has occurred - re-connect(). */
40 _speed_excess = FALSE;
42 if (_conn_state > RCVR_OFF) {
43 set_conn_state(RCVR_DOWN);
48 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
53 channel_parse_rmc(gchar * sentence)
55 /* Recommended Minimum Navigation Information C
57 * 2) Status, V=Navigation receiver warning A=Valid
62 * 7) Speed over ground, knots
63 * 8) Track made good, degrees true
65 * 10) Magnetic Variation, degrees
67 * 12) FAA mode indicator (NMEA 2.3 and later)
70 gchar *token, *dpoint, *gpsdate = NULL;
73 gboolean newly_fixed = FALSE;
74 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
79 token = strsep(&sentence, DELIM);
83 token = strsep(&sentence, DELIM);
84 /* Token is now Status. */
85 if (token && *token == 'A') {
87 if (_conn_state != RCVR_FIXED) {
89 set_conn_state(RCVR_FIXED);
92 /* Data is invalid - not enough satellites?. */
93 if (_conn_state != RCVR_UP) {
94 set_conn_state(RCVR_UP);
99 /* Parse the latitude. */
100 token = strsep(&sentence, DELIM);
101 if (token && *token) {
102 dpoint = strchr(token, '.');
103 if (!dpoint) /* handle buggy NMEA */
104 dpoint = token + strlen(token);
105 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
107 MACRO_PARSE_INT(tmpi, token);
108 _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
112 token = strsep(&sentence, DELIM);
113 if (token && *token == 'S')
114 _gps.lat = -_gps.lat;
116 /* Parse the longitude. */
117 token = strsep(&sentence, DELIM);
118 if (token && *token) {
119 dpoint = strchr(token, '.');
120 if (!dpoint) /* handle buggy NMEA */
121 dpoint = token + strlen(token);
122 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
124 MACRO_PARSE_INT(tmpi, token);
125 _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
129 token = strsep(&sentence, DELIM);
130 if (token && *token == 'W')
131 _gps.lon = -_gps.lon;
133 /* Parse speed over ground, knots. */
134 token = strsep(&sentence, DELIM);
135 if (token && *token) {
136 MACRO_PARSE_FLOAT(_gps.speed, token);
138 _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
139 gps_display_data_speed(info_banner.speed, _gps.speed);
143 /* Parse heading, degrees from true north. */
144 token = strsep(&sentence, DELIM);
145 if (token && *token) {
146 MACRO_PARSE_FLOAT(_gps.heading, token);
150 token = strsep(&sentence, DELIM);
151 if (token && *token && gpsdate) {
153 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
154 strcat(gpsdate, token);
155 strptime(gpsdate, "%H%M%S%d%m%y", &time);
156 _pos.time = mktime(&time) + _gmtoffset;
158 _pos.time = time(NULL);
160 /* Add new data to track only if we have a fix */
163 /* XXX: Set filter logic somewhere else */
165 if ((_conn_state == RCVR_FIXED) && (_track_store==TRUE)) {
166 if ((_gps_filter==TRUE) && (track_drop_cnt<GPS_FILTER_MAX_SKIP)) {
167 integerize_data(_vel_offsetx, _vel_offsety, _pos, _gps);
168 if ( (_gps.hdop<_filter_hdop || _filter_hdop==0.0) &&
169 (_gps.vdop<_filter_vdop || _filter_vdop==0.0) &&
170 (fabs(_gps.heading-_gps.lheading)>_filter_angle || _filter_angle==0.0 ) &&
171 (_map_location_known==TRUE && (_map_location_dist<_filter_osm || _map_location_dist==0.0)) ) {
172 track_add(_pos.time, newly_fixed);
173 _gps.lheading=_gps.heading;
177 g_printf("*** Filtering by: [%s %s %s %s] A: %f (%d)\n",
178 _gps.hdop>_filter_hdop ? "HDOP" : "-",
179 _gps.vdop>_filter_vdop ? "VDOP" : "-",
180 (fabs(_gps.heading-_gps.lheading)<_filter_angle) ? "Angle" : "-",
181 (_map_location_known==TRUE && (_map_location_dist>_filter_osm)) ? "OSM" : "-",
182 (fabs(_gps.heading-_gps.lheading)<_filter_angle),
188 integerize_data(_vel_offsetx, _vel_offsety, _pos, _gps);
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)
281 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
285 /* Skip Auto selection. */
286 token = strsep(&sentence, DELIM);
289 token = strsep(&sentence, DELIM);
291 MACRO_PARSE_INT(_gps.fix, token);
294 for (i = 0; i < 12; i++) {
295 token = strsep(&sentence, DELIM);
297 _gps.satforfix[_gps.satinuse++] = atoi(token);
301 token = strsep(&sentence, DELIM);
303 MACRO_PARSE_FLOAT(_gps.pdop, token);
306 token = strsep(&sentence, DELIM);
308 MACRO_PARSE_FLOAT(_gps.hdop, token);
311 token = strsep(&sentence, DELIM);
313 MACRO_PARSE_FLOAT(_gps.vdop, token);
315 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
319 channel_parse_gsv(gchar * sentence)
321 /* Must be GSV - Satellites in view
322 * 1) total number of messages
324 * 3) satellites in view
325 * 4) satellite number
326 * 5) elevation in degrees (0-90)
327 * 6) azimuth in degrees to true north (0-359)
328 * 7) SNR in dB (0-99)
329 * more satellite infos like 4)-7)
333 guint msgcnt = 0, nummsgs = 0;
334 static guint running_total = 0;
335 static guint num_sats_used = 0;
336 static guint satcnt = 0;
337 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
339 /* Parse number of messages. */
340 token = strsep(&sentence, DELIM);
342 MACRO_PARSE_INT(nummsgs, token);
344 /* Parse message number. */
345 token = strsep(&sentence, DELIM);
347 MACRO_PARSE_INT(msgcnt, token);
349 /* Parse number of satellites in view. */
350 token = strsep(&sentence, DELIM);
351 if (token && *token) {
352 MACRO_PARSE_INT(_gps.satinview, token);
353 if (_gps.satinview > 12) /* Handle buggy NMEA. */
357 /* Loop until there are no more satellites to parse. */
358 while (sentence && satcnt < 12) {
359 /* Get token for Satellite Number. */
360 token = strsep(&sentence, DELIM);
362 _gps_sat[satcnt].prn = atoi(token);
364 /* Get token for elevation in degrees (0-90). */
365 token = strsep(&sentence, DELIM);
367 _gps_sat[satcnt].elevation = atoi(token);
369 /* Get token for azimuth in degrees to true north (0-359). */
370 token = strsep(&sentence, DELIM);
372 _gps_sat[satcnt].azimuth = atoi(token);
374 /* Get token for SNR. */
375 token = strsep(&sentence, DELIM);
376 if (token && *token && (_gps_sat[satcnt].snr = atoi(token))) {
377 /* SNR is non-zero - add to total and count as used. */
378 running_total += _gps_sat[satcnt].snr;
384 if (msgcnt == nummsgs) {
385 /* This is the last message. Calculate signal strength. */
387 if (_conn_state == RCVR_UP) {
388 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
389 BOUND(fraction, 0.0, 1.0);
390 set_fix_progress(fraction);
397 /* Keep awake while they watch the progress bar. */
401 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
405 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
408 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
410 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
414 &bytes_read, NULL)) {
417 _gps_read_buf_curr += bytes_read;
418 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
419 while ((eol = strchr(_gps_read_buf, '\n'))) {
420 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
422 if (*_gps_read_buf == '$') {
423 /* This is the beginning of a sentence; okay to parse. */
424 *eol = '\0'; /* overwrite \n with \0 */
425 while (*sptr && *sptr != '*')
428 /* If we're at a \0 (meaning there is no checksum), or if the
429 * checksum is good, then parse the sentence. */
430 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
432 *sptr = '\0'; /* take checksum out of the buffer. */
433 if (!strncmp(_gps_read_buf + 3, "GSV", 3)) {
434 if (_conn_state == RCVR_UP)
435 channel_parse_gsv(_gps_read_buf + 7);
436 } else if (!strncmp(_gps_read_buf + 3, "RMC", 3))
437 channel_parse_rmc(_gps_read_buf + 7);
438 else if (!strncmp(_gps_read_buf + 3, "GGA", 3))
439 channel_parse_gga(_gps_read_buf + 7);
440 else if (!strncmp(_gps_read_buf + 3, "GSA", 3))
441 channel_parse_gsa(_gps_read_buf + 7);
442 else g_print("Unknown NMEA: [%s]\n", _gps_read_buf);
448 gps_display_details();
450 /* There was a checksum, and it was bad. */
452 ("%s: Bad checksum in NMEA sentence:\n%s\n",
458 /* If eol is at or after (_gps_read_buf_curr - 1) */
459 if (eol >= (_gps_read_buf_curr - 1)) {
460 /* Last read was a newline - reset read buffer */
461 _gps_read_buf_curr = _gps_read_buf;
462 *_gps_read_buf_curr = '\0';
464 /* Move the next line to the front of the buffer. */
465 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
466 /* Subtract _curr so that it's pointing at the new \0. */
467 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
472 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);