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)
283 vprintf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
287 /* Skip Auto selection. */
288 token = strsep(&sentence, DELIM);
291 token = strsep(&sentence, DELIM);
293 MACRO_PARSE_INT(_gps.fix, token);
296 for (i = 0; i < 12; i++) {
297 token = strsep(&sentence, DELIM);
299 _gps.satforfix[_gps.satinuse++] = atoi(token);
303 token = strsep(&sentence, DELIM);
305 MACRO_PARSE_FLOAT(_gps.pdop, token);
308 token = strsep(&sentence, DELIM);
310 MACRO_PARSE_FLOAT(_gps.hdop, token);
313 token = strsep(&sentence, DELIM);
315 MACRO_PARSE_FLOAT(_gps.vdop, token);
317 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
321 channel_parse_gsv(gchar * sentence)
323 /* Must be GSV - Satellites in view
324 * 1) total number of messages
326 * 3) satellites in view
327 * 4) satellite number
328 * 5) elevation in degrees (0-90)
329 * 6) azimuth in degrees to true north (0-359)
330 * 7) SNR in dB (0-99)
331 * more satellite infos like 4)-7)
335 guint msgcnt = 0, nummsgs = 0;
336 static guint running_total = 0;
337 static guint num_sats_used = 0;
338 static guint satcnt = 0;
339 g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
341 /* Parse number of messages. */
342 token = strsep(&sentence, DELIM);
344 MACRO_PARSE_INT(nummsgs, token);
346 /* Parse message number. */
347 token = strsep(&sentence, DELIM);
349 MACRO_PARSE_INT(msgcnt, token);
351 /* Parse number of satellites in view. */
352 token = strsep(&sentence, DELIM);
353 if (token && *token) {
354 MACRO_PARSE_INT(_gps.satinview, token);
355 if (_gps.satinview > 12) /* Handle buggy NMEA. */
359 /* Loop until there are no more satellites to parse. */
360 while (sentence && satcnt < 12) {
361 /* Get token for Satellite Number. */
362 token = strsep(&sentence, DELIM);
364 _gps.sat[satcnt].prn = atoi(token);
366 /* Get token for elevation in degrees (0-90). */
367 token = strsep(&sentence, DELIM);
369 _gps.sat[satcnt].elevation = atoi(token);
371 /* Get token for azimuth in degrees to true north (0-359). */
372 token = strsep(&sentence, DELIM);
374 _gps.sat[satcnt].azimuth = atoi(token);
376 /* Get token for SNR. */
377 token = strsep(&sentence, DELIM);
378 if (token && *token && (_gps.sat[satcnt].snr = atoi(token))) {
379 /* SNR is non-zero - add to total and count as used. */
380 running_total += _gps.sat[satcnt].snr;
386 if (msgcnt == nummsgs) {
387 /* This is the last message. Calculate signal strength. */
389 if (_conn_state == RCVR_UP) {
390 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
391 BOUND(fraction, 0.0, 1.0);
392 set_fix_progress(fraction);
399 /* Keep awake while they watch the progress bar. */
403 g_printf("%s(): return\n", __PRETTY_FUNCTION__);
407 channel_cb_input(GIOChannel * src, GIOCondition condition, gpointer data)
410 vprintf("%s(%d)\n", __PRETTY_FUNCTION__, condition);
412 if (G_IO_STATUS_NORMAL == g_io_channel_read_chars(_channel,
413 _gps_read_buf_curr, _gps_read_buf_last - _gps_read_buf_curr,
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__);