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);
190 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
193 void channel_parse_gga(gchar * sentence)
195 /* GGA Global Positioning System Fix Data
206 4 = Real Time Kinematic
208 6 = estimated (dead reckoning) (2.3 feature)
209 7 = Manual input mode
211 7. Number of satellites being tracked
212 8. Horizontal dilution of position
213 9. Altitude, Meters, above mean sea level
214 10. Alt unit (meters)
215 11. Height of geoid (mean sea level) above WGS84 ellipsoid
217 13. (empty field) time in seconds since last DGPS update
218 14. (empty field) DGPS station ID number
219 15. the checksum data
222 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
227 token = strsep(&sentence, DELIM);
229 token = strsep(&sentence, DELIM);
231 token = strsep(&sentence, DELIM);
233 token = strsep(&sentence, DELIM);
235 token = strsep(&sentence, DELIM);
237 /* Parse Fix quality */
238 token = strsep(&sentence, DELIM);
240 MACRO_PARSE_INT(_gps.fixquality, token);
242 /* Skip number of satellites */
243 token = strsep(&sentence, DELIM);
245 /* Parse Horizontal dilution of position */
246 token = strsep(&sentence, DELIM);
248 MACRO_PARSE_INT(_gps.hdop, token);
251 token = strsep(&sentence, DELIM);
252 if (token && *token) {
253 MACRO_PARSE_FLOAT(_pos.altitude, token);
257 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
260 void channel_parse_gsa(gchar * sentence)
262 /* GPS DOP and active satellites
263 * 1) Auto selection of 2D or 3D fix (M = manual)
264 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
265 * 3) PRNs of satellites used for fix
267 * 4) PDOP (dilution of precision)
268 * 5) Horizontal dilution of precision (HDOP)
269 * 6) Vertical dilution of precision (VDOP)
274 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
278 /* Skip Auto selection. */
279 token = strsep(&sentence, DELIM);
282 token = strsep(&sentence, DELIM);
284 MACRO_PARSE_INT(_gps.fix, token);
287 for (i = 0; i < 12; i++) {
288 token = strsep(&sentence, DELIM);
290 _gps.satforfix[_gps.satinuse++] = atoi(token);
294 token = strsep(&sentence, DELIM);
296 MACRO_PARSE_FLOAT(_gps.pdop, token);
299 token = strsep(&sentence, DELIM);
301 MACRO_PARSE_FLOAT(_gps.hdop, token);
304 token = strsep(&sentence, DELIM);
306 MACRO_PARSE_FLOAT(_gps.vdop, token);
308 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
311 void channel_parse_gsv(gchar * sentence)
313 /* Must be GSV - Satellites in view
314 * 1) total number of messages
316 * 3) satellites in view
317 * 4) satellite number
318 * 5) elevation in degrees (0-90)
319 * 6) azimuth in degrees to true north (0-359)
320 * 7) SNR in dB (0-99)
321 * more satellite infos like 4)-7)
325 guint msgcnt = 0, nummsgs = 0;
326 static guint running_total = 0;
327 static guint num_sats_used = 0;
328 static guint satcnt = 0;
329 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
331 /* Parse number of messages. */
332 token = strsep(&sentence, DELIM);
334 MACRO_PARSE_INT(nummsgs, token);
336 /* Parse message number. */
337 token = strsep(&sentence, DELIM);
339 MACRO_PARSE_INT(msgcnt, token);
341 /* Parse number of satellites in view. */
342 token = strsep(&sentence, DELIM);
343 if (token && *token) {
344 MACRO_PARSE_INT(_gps.satinview, token);
345 if (_gps.satinview > 12) /* Handle buggy NMEA. */
349 /* Loop until there are no more satellites to parse. */
350 while (sentence && satcnt < 12) {
351 /* Get token for Satellite Number. */
352 token = strsep(&sentence, DELIM);
354 _gps_sat[satcnt].prn = atoi(token);
356 /* Get token for elevation in degrees (0-90). */
357 token = strsep(&sentence, DELIM);
359 _gps_sat[satcnt].elevation = atoi(token);
361 /* Get token for azimuth in degrees to true north (0-359). */
362 token = strsep(&sentence, DELIM);
364 _gps_sat[satcnt].azimuth = atoi(token);
366 /* Get token for SNR. */
367 token = strsep(&sentence, DELIM);
368 if (token && *token && (_gps_sat[satcnt].snr = atoi(token))) {
369 /* SNR is non-zero - add to total and count as used. */
370 running_total += _gps_sat[satcnt].snr;
376 if (msgcnt == nummsgs) {
377 /* This is the last message. Calculate signal strength. */
379 if (_conn_state == RCVR_UP) {
380 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
381 BOUND(fraction, 0.0, 1.0);
382 set_fix_progress(fraction);
389 /* Keep awake while they watch the progress bar. */
393 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
397 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
400 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
402 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
406 &bytes_read, NULL)) {
409 _gps_read_buf_curr += bytes_read;
410 *_gps_read_buf_curr = '\0'; /* append a \0 so we can read as string */
411 while ((eol = strchr(_gps_read_buf, '\n'))) {
412 gchar *sptr = _gps_read_buf + 1; /* Skip the $ */
414 if (*_gps_read_buf == '$') {
415 /* This is the beginning of a sentence; okay to parse. */
416 *eol = '\0'; /* overwrite \n with \0 */
417 while (*sptr && *sptr != '*')
420 /* If we're at a \0 (meaning there is no checksum), or if the
421 * checksum is good, then parse the sentence. */
422 if (!*sptr || csum == strtol(sptr + 1, NULL, 16)) {
424 *sptr = '\0'; /* take checksum out of the buffer. */
425 if (!strncmp(_gps_read_buf + 3, "GSV", 3)) {
426 if (_conn_state == RCVR_UP)
427 channel_parse_gsv(_gps_read_buf + 7);
428 } else if (!strncmp(_gps_read_buf + 3, "RMC", 3))
429 channel_parse_rmc(_gps_read_buf + 7);
430 else if (!strncmp(_gps_read_buf + 3, "GGA", 3))
431 channel_parse_gga(_gps_read_buf + 7);
432 else if (!strncmp(_gps_read_buf + 3, "GSA", 3))
433 channel_parse_gsa(_gps_read_buf + 7);
434 else g_print("Unknown NMEA: [%s]\n", _gps_read_buf);
438 gps_display_details();
440 /* There was a checksum, and it was bad. */
442 ("%s: Bad checksum in NMEA sentence:\n%s\n",
448 /* If eol is at or after (_gps_read_buf_curr - 1) */
449 if (eol >= (_gps_read_buf_curr - 1)) {
450 /* Last read was a newline - reset read buffer */
451 _gps_read_buf_curr = _gps_read_buf;
452 *_gps_read_buf_curr = '\0';
454 /* Move the next line to the front of the buffer. */
455 memmove(_gps_read_buf, eol + 1, _gps_read_buf_curr - eol); /* include terminating 0 */
456 /* Subtract _curr so that it's pointing at the new \0. */
457 _gps_read_buf_curr -= (eol - _gps_read_buf + 1);
462 vprintf("%s(): return TRUE\n", __PRETTY_FUNCTION__);