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 gint track_drop_cnt=0;
32 channel_cb_error(GIOChannel * src, GIOCondition condition, gpointer data)
34 printf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
36 /* An error has occurred - re-connect(). */
39 _speed_excess = FALSE;
41 if (_conn_state > RCVR_OFF) {
42 set_conn_state(RCVR_DOWN);
47 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
51 void channel_parse_rmc(gchar * sentence)
53 /* Recommended Minimum Navigation Information C
55 * 2) Status, V=Navigation receiver warning A=Valid
60 * 7) Speed over ground, knots
61 * 8) Track made good, degrees true
63 * 10) Magnetic Variation, degrees
65 * 12) FAA mode indicator (NMEA 2.3 and later)
68 gchar *token, *dpoint, *gpsdate = NULL;
71 gboolean newly_fixed = FALSE;
72 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
77 token = strsep(&sentence, DELIM);
81 token = strsep(&sentence, DELIM);
82 /* Token is now Status. */
83 if (token && *token == 'A') {
85 if (_conn_state != RCVR_FIXED) {
87 set_conn_state(RCVR_FIXED);
90 /* Data is invalid - not enough satellites?. */
91 if (_conn_state != RCVR_UP) {
92 set_conn_state(RCVR_UP);
97 /* Parse the latitude. */
98 token = strsep(&sentence, DELIM);
99 if (token && *token) {
100 dpoint = strchr(token, '.');
101 if (!dpoint) /* handle buggy NMEA */
102 dpoint = token + strlen(token);
103 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
105 MACRO_PARSE_INT(tmpi, token);
106 _gps.lat = tmpi + (tmpd * (1.0 / 60.0));
110 token = strsep(&sentence, DELIM);
111 if (token && *token == 'S')
112 _gps.lat = -_gps.lat;
114 /* Parse the longitude. */
115 token = strsep(&sentence, DELIM);
116 if (token && *token) {
117 dpoint = strchr(token, '.');
118 if (!dpoint) /* handle buggy NMEA */
119 dpoint = token + strlen(token);
120 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
122 MACRO_PARSE_INT(tmpi, token);
123 _gps.lon = tmpi + (tmpd * (1.0 / 60.0));
127 token = strsep(&sentence, DELIM);
128 if (token && *token == 'W')
129 _gps.lon = -_gps.lon;
131 /* Parse speed over ground, knots. */
132 token = strsep(&sentence, DELIM);
133 if (token && *token) {
134 MACRO_PARSE_FLOAT(_gps.speed, token);
136 _gps.maxspeed = MAX(_gps.maxspeed, _gps.speed);
137 gps_display_data_speed(info_banner.speed, _gps.speed);
141 /* Parse heading, degrees from true north. */
142 token = strsep(&sentence, DELIM);
143 if (token && *token) {
144 MACRO_PARSE_FLOAT(_gps.heading, token);
148 token = strsep(&sentence, DELIM);
149 if (token && *token && gpsdate) {
151 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
152 strcat(gpsdate, token);
153 strptime(gpsdate, "%H%M%S%d%m%y", &time);
154 _pos.time = mktime(&time) + _gmtoffset;
156 _pos.time = time(NULL);
158 /* Add new data to track only if we have a fix */
161 /* XXX: Set filter logic somewhere else */
163 if ((_conn_state == RCVR_FIXED) && (_track_store==TRUE)) {
164 if ((_gps_filter==TRUE) && (track_drop_cnt<10)) {
165 integerize_data(_vel_offsetx, _vel_offsety, _pos, _gps);
166 if ( (_gps.hdop<_filter_hdop || _filter_hdop==0.0) &&
167 (_gps.vdop<_filter_vdop || _filter_vdop==0.0) &&
168 (fabs(_gps.heading-_gps.lheading)>_filter_angle || _filter_angle==0.0 ) &&
169 (_map_location_known==TRUE && (_map_location_dist<_filter_osm || _map_location_dist==0.0)) ) {
170 track_add(_pos.time, newly_fixed);
171 _gps.lheading=_gps.heading;
175 g_printf("*** Filtering by: [%s %s %s %s] (%d)\n",
176 _gps.hdop>_filter_hdop ? "HDOP" : "-",
177 _gps.vdop>_filter_vdop ? "VDOP" : "-",
178 (fabs(_gps.heading-_gps.lheading)<_filter_angle) ? "Angle" : "-",
179 (_map_location_known==TRUE && (_map_location_dist>_filter_osm)) ? "OSM" : "-", track_drop_cnt);
184 integerize_data(_vel_offsetx, _vel_offsety, _pos, _gps);
185 track_add(_pos.time, newly_fixed);
186 _gps.lheading=_gps.heading;
191 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
194 void channel_parse_gga(gchar * sentence)
196 /* GGA Global Positioning System Fix Data
207 4 = Real Time Kinematic
209 6 = estimated (dead reckoning) (2.3 feature)
210 7 = Manual input mode
212 7. Number of satellites being tracked
213 8. Horizontal dilution of position
214 9. Altitude, Meters, above mean sea level
215 10. Alt unit (meters)
216 11. Height of geoid (mean sea level) above WGS84 ellipsoid
218 13. (empty field) time in seconds since last DGPS update
219 14. (empty field) DGPS station ID number
220 15. the checksum data
223 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
228 token = strsep(&sentence, DELIM);
230 token = strsep(&sentence, DELIM);
232 token = strsep(&sentence, DELIM);
234 token = strsep(&sentence, DELIM);
236 token = strsep(&sentence, DELIM);
238 /* Parse Fix quality */
239 token = strsep(&sentence, DELIM);
241 MACRO_PARSE_INT(_gps.fixquality, token);
243 /* Skip number of satellites */
244 token = strsep(&sentence, DELIM);
246 /* Parse Horizontal dilution of position */
247 token = strsep(&sentence, DELIM);
249 MACRO_PARSE_INT(_gps.hdop, token);
252 token = strsep(&sentence, DELIM);
253 if (token && *token) {
254 MACRO_PARSE_FLOAT(_pos.altitude, token);
258 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
261 void channel_parse_gsa(gchar * sentence)
263 /* GPS DOP and active satellites
264 * 1) Auto selection of 2D or 3D fix (M = manual)
265 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
266 * 3) PRNs of satellites used for fix
268 * 4) PDOP (dilution of precision)
269 * 5) Horizontal dilution of precision (HDOP)
270 * 6) Vertical dilution of precision (VDOP)
275 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
279 /* Skip Auto selection. */
280 token = strsep(&sentence, DELIM);
283 token = strsep(&sentence, DELIM);
285 MACRO_PARSE_INT(_gps.fix, token);
288 for (i = 0; i < 12; i++) {
289 token = strsep(&sentence, DELIM);
291 _gps.satforfix[_gps.satinuse++] = atoi(token);
295 token = strsep(&sentence, DELIM);
297 MACRO_PARSE_FLOAT(_gps.pdop, token);
300 token = strsep(&sentence, DELIM);
302 MACRO_PARSE_FLOAT(_gps.hdop, token);
305 token = strsep(&sentence, DELIM);
307 MACRO_PARSE_FLOAT(_gps.vdop, token);
309 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
312 void channel_parse_gsv(gchar * sentence)
314 /* Must be GSV - Satellites in view
315 * 1) total number of messages
317 * 3) satellites in view
318 * 4) satellite number
319 * 5) elevation in degrees (0-90)
320 * 6) azimuth in degrees to true north (0-359)
321 * 7) SNR in dB (0-99)
322 * more satellite infos like 4)-7)
326 guint msgcnt = 0, nummsgs = 0;
327 static guint running_total = 0;
328 static guint num_sats_used = 0;
329 static guint satcnt = 0;
330 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
332 /* Parse number of messages. */
333 token = strsep(&sentence, DELIM);
335 MACRO_PARSE_INT(nummsgs, token);
337 /* Parse message number. */
338 token = strsep(&sentence, DELIM);
340 MACRO_PARSE_INT(msgcnt, token);
342 /* Parse number of satellites in view. */
343 token = strsep(&sentence, DELIM);
344 if (token && *token) {
345 MACRO_PARSE_INT(_gps.satinview, token);
346 if (_gps.satinview > 12) /* Handle buggy NMEA. */
350 /* Loop until there are no more satellites to parse. */
351 while (sentence && satcnt < 12) {
352 /* Get token for Satellite Number. */
353 token = strsep(&sentence, DELIM);
355 _gps_sat[satcnt].prn = atoi(token);
357 /* Get token for elevation in degrees (0-90). */
358 token = strsep(&sentence, DELIM);
360 _gps_sat[satcnt].elevation = atoi(token);
362 /* Get token for azimuth in degrees to true north (0-359). */
363 token = strsep(&sentence, DELIM);
365 _gps_sat[satcnt].azimuth = atoi(token);
367 /* Get token for SNR. */
368 token = strsep(&sentence, DELIM);
369 if (token && *token && (_gps_sat[satcnt].snr = atoi(token))) {
370 /* SNR is non-zero - add to total and count as used. */
371 running_total += _gps_sat[satcnt].snr;
377 if (msgcnt == nummsgs) {
378 /* This is the last message. Calculate signal strength. */
380 if (_conn_state == RCVR_UP) {
381 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
382 BOUND(fraction, 0.0, 1.0);
383 set_fix_progress(fraction);
390 /* Keep awake while they watch the progress bar. */
394 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
398 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
401 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
403 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
407 &bytes_read, NULL)) {
410 _gps_read_buf_curr += bytes_read;
411 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
412 while ((eol = strchr(_gps_read_buf, '\n'))) {
413 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
415 if (*_gps_read_buf == '$') {
416 /* This is the beginning of a sentence; okay to parse. */
417 *eol = '\0'; /* overwrite \n with \0 */
418 while (*sptr && *sptr != '*')
421 /* If we're at a \0 (meaning there is no checksum), or if the
422 * checksum is good, then parse the sentence. */
423 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
425 *sptr = '\0'; /* take checksum out of the buffer. */
426 if (!strncmp(_gps_read_buf + 3, "GSV", 3)) {
427 if (_conn_state == RCVR_UP)
428 channel_parse_gsv(_gps_read_buf + 7);
429 } else if (!strncmp(_gps_read_buf + 3, "RMC", 3))
430 channel_parse_rmc(_gps_read_buf + 7);
431 else if (!strncmp(_gps_read_buf + 3, "GGA", 3))
432 channel_parse_gga(_gps_read_buf + 7);
433 else if (!strncmp(_gps_read_buf + 3, "GSA", 3))
434 channel_parse_gsa(_gps_read_buf + 7);
435 else g_print("Unknown NMEA: [%s]\n", _gps_read_buf);
439 gps_display_details();
441 /* There was a checksum, and it was bad. */
443 ("%s: Bad checksum in NMEA sentence:\n%s\n",
449 /* If eol is at or after (_gps_read_buf_curr - 1) */
450 if (eol >= (_gps_read_buf_curr - 1)) {
451 /* Last read was a newline - reset read buffer */
452 _gps_read_buf_curr = _gps_read_buf;
453 *_gps_read_buf_curr = '\0';
455 /* Move the next line to the front of the buffer. */
456 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
457 /* Subtract _curr so that it's pointing at the new \0. */
458 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
463 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);