2 * This file is part of mapper
4 * Copyright (C) 2007 Kaj-Michael Lang
5 * Copyright (C) 2006-2007 John Costigan.
7 * POI and GPS-Info code originally written by Cezary Jackiewicz.
9 * Default map data provided by http://www.openstreetmap.org/
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
36 #include <glib/gstdio.h>
41 #include "gps-nmea-parse.h"
45 #define NMEA_RMC "$GPRMC"
46 #define NMEA_GGA "$GPGGA"
47 #define NMEA_GSA "$GPGSA"
48 #define NMEA_GSV "$GPGSV"
49 #define NMEA_PRLEN (6)
52 gps_nmea_parse_rmc(Gps *gps, gchar *sentence)
54 /* Recommended Minimum Navigation Information C
56 * 2) Status, V=Navigation receiver warning A=Valid
61 * 7) Speed over ground, knots
62 * 8) Track made good, degrees true
64 * 10) Magnetic Variation, degrees
66 * 12) FAA mode indicator (NMEA 2.3 and later)
69 gchar *token, *dpoint, *gpsdate = NULL;
73 g_debug("%s(): %s", __PRETTY_FUNCTION__, sentence);
76 token = strsep(&sentence, DELIM);
80 token = strsep(&sentence, DELIM);
81 /* Token is now Status. */
82 if (token && *token == 'A') {
84 if (gps->io.conn != RCVR_FIXED) {
85 gps->data.newly_fixed = TRUE;
86 gps_conn_set_state(gps, RCVR_FIXED);
88 gps->data.newly_fixed = FALSE;
91 /* Data is invalid - not enough satellites?. */
92 if (gps->io.conn != RCVR_UP) {
93 gps_conn_set_state(gps, RCVR_UP);
94 /* track_add(NULL, FALSE); */
98 /* Parse the latitude. */
99 token = strsep(&sentence, DELIM);
100 if (token && *token) {
101 dpoint = strchr(token, '.');
102 if (!dpoint) /* handle buggy NMEA */
103 dpoint = token + strlen(token);
104 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
106 MACRO_PARSE_INT(tmpi, token);
107 gps->data.lat = tmpi + (tmpd * (1.0 / 60.0));
111 token = strsep(&sentence, DELIM);
112 if (token && *token == 'S')
113 gps->data.lat = -gps->data.lat;
115 /* Parse the longitude. */
116 token = strsep(&sentence, DELIM);
117 if (token && *token) {
118 dpoint = strchr(token, '.');
119 if (!dpoint) /* handle buggy NMEA */
120 dpoint = token + strlen(token);
121 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
123 MACRO_PARSE_INT(tmpi, token);
124 gps->data.lon = tmpi + (tmpd * (1.0 / 60.0));
128 token = strsep(&sentence, DELIM);
129 if (token && *token == 'W')
130 gps->data.lon = -gps->data.lon;
132 /* Parse speed over ground, knots. */
133 token = strsep(&sentence, DELIM);
134 if (token && *token) {
135 MACRO_PARSE_FLOAT(gps->data.speed, token);
136 if (gps->data.fix > FIX_NOFIX) {
137 gps->data.maxspeed=MAX(gps->data.maxspeed, gps->data.speed);
141 /* Parse heading, degrees from true north. */
142 token = strsep(&sentence, DELIM);
143 if (token && *token) {
144 MACRO_PARSE_FLOAT(gps->data.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 gps->data.time = mktime(&time) + _gmtoffset;
156 gps->data.time = time(NULL);
159 gps_data_integerize(&gps->data);
160 gps->data.lheading=gps->data.heading;
164 gps_nmea_parse_gga(Gps *gps, gchar * sentence)
166 /* GGA Global Positioning System Fix Data
177 4 = Real Time Kinematic
179 6 = estimated (dead reckoning) (2.3 feature)
180 7 = Manual input mode
182 7. Number of satellites being tracked
183 8. Horizontal dilution of position
184 9. Altitude, Meters, above mean sea level
185 10. Alt unit (meters)
186 11. Height of geoid (mean sea level) above WGS84 ellipsoid
188 13. (empty field) time in seconds since last DGPS update
189 14. (empty field) DGPS station ID number
190 15. the checksum data
193 g_debug("%s(): %s", __PRETTY_FUNCTION__, sentence);
196 token = strsep(&sentence, DELIM);
198 token = strsep(&sentence, DELIM);
200 token = strsep(&sentence, DELIM);
202 token = strsep(&sentence, DELIM);
204 token = strsep(&sentence, DELIM);
206 /* Parse Fix quality */
207 token = strsep(&sentence, DELIM);
209 MACRO_PARSE_INT(gps->data.fixquality, token);
211 /* Skip number of satellites */
212 token = strsep(&sentence, DELIM);
214 /* Parse Horizontal dilution of position */
215 token = strsep(&sentence, DELIM);
217 MACRO_PARSE_INT(gps->data.hdop, token);
220 token = strsep(&sentence, DELIM);
221 if (token && *token) {
222 MACRO_PARSE_FLOAT(gps->data.altitude, token);
224 gps->data.altitude = NAN;
228 gps_nmea_parse_gsa(Gps *gps, gchar * sentence)
230 /* GPS DOP and active satellites
231 * 1) Auto selection of 2D or 3D fix (M = manual)
232 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
233 * 3) PRNs of satellites used for fix
235 * 4) PDOP (dilution of precision)
236 * 5) Horizontal dilution of precision (HDOP)
237 * 6) Vertical dilution of precision (VDOP)
244 g_debug("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
246 /* Skip Auto selection. */
247 token = strsep(&sentence, DELIM);
250 token = strsep(&sentence, DELIM);
252 MACRO_PARSE_INT(gps->data.fix, token);
254 gps->data.satinuse = 0;
255 for (i = 0; i < 12; i++) {
257 token = strsep(&sentence, DELIM);
263 gps->data.sat[i].fix=FALSE;
267 for (si=0;si<12;si++) {
268 if (gps->data.sat[i].prn==satforfix[si])
269 gps->data.sat[i].fix=TRUE;
273 token = strsep(&sentence, DELIM);
275 MACRO_PARSE_FLOAT(gps->data.pdop, token);
278 token = strsep(&sentence, DELIM);
280 MACRO_PARSE_FLOAT(gps->data.hdop, token);
283 token = strsep(&sentence, DELIM);
285 MACRO_PARSE_FLOAT(gps->data.vdop, token);
289 gps_nmea_parse_gsv(Gps *gps, gchar * sentence)
291 /* Must be GSV - Satellites in view
292 * 1) total number of messages
294 * 3) satellites in view
295 * 4) satellite number
296 * 5) elevation in degrees (0-90)
297 * 6) azimuth in degrees to true north (0-359)
298 * 7) SNR in dB (0-99)
299 * more satellite infos like 4)-7)
303 guint msgcnt = 0, nummsgs = 0;
304 static guint running_total = 0;
305 static guint num_sats_used = 0;
306 static guint satcnt = 0;
307 /* g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence); */
309 /* Parse number of messages. */
310 token = strsep(&sentence, DELIM);
312 MACRO_PARSE_INT(nummsgs, token);
314 /* Parse message number. */
315 token = strsep(&sentence, DELIM);
317 MACRO_PARSE_INT(msgcnt, token);
319 /* Parse number of satellites in view. */
320 token = strsep(&sentence, DELIM);
321 if (token && *token) {
322 MACRO_PARSE_INT(gps->data.satinview, token);
323 if (gps->data.satinview > 12) /* Handle buggy NMEA. */
324 gps->data.satinview = 12;
327 /* Loop until there are no more satellites to parse. */
328 while (sentence && satcnt < 12) {
329 /* Get token for Satellite Number. */
330 token = strsep(&sentence, DELIM);
332 gps->data.sat[satcnt].prn = atoi(token);
334 /* Get token for elevation in degrees (0-90). */
335 token = strsep(&sentence, DELIM);
337 gps->data.sat[satcnt].elevation = atoi(token);
339 /* Get token for azimuth in degrees to true north (0-359). */
340 token = strsep(&sentence, DELIM);
342 gps->data.sat[satcnt].azimuth = atoi(token);
344 /* Get token for SNR. */
345 token = strsep(&sentence, DELIM);
346 if (token && *token && (gps->data.sat[satcnt].snr = atoi(token))) {
347 /* SNR is non-zero - add to total and count as used. */
348 running_total += gps->data.sat[satcnt].snr;
354 if (msgcnt == nummsgs) {
355 /* This is the last message. Calculate signal strength. */
357 if (gps->io.conn==RCVR_UP) {
358 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
359 BOUND(fraction, 0.0, 1.0);
360 if (gps->connection_progress!=NULL)
361 gps->connection_progress(gps, fraction);
371 gps_nmea_parse(Gps *gps)
375 g_debug("NMEA2 %d %s", gps->io.nmea_cnt, gps->io.nmea);
380 if (strlen(gps->io.nmea)<NMEA_PRLEN+1)
383 if (!strncmp(gps->io.nmea, NMEA_GSV, NMEA_PRLEN)) {
384 gps_nmea_parse_gsv(gps, gps->io.nmea + NMEA_PRLEN+1);
385 if (gps->update_satellite)
386 g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc)gps->update_satellite, gps, NULL);
388 } else if (!strncmp(gps->io.nmea, NMEA_RMC, NMEA_PRLEN)) {
389 gps_nmea_parse_rmc(gps, gps->io.nmea + NMEA_PRLEN+1);
390 if ((gps->io.conn==RCVR_FIXED) && (gps->update_location!=NULL))
391 gps->update_location(gps);
393 } else if (!strncmp(gps->io.nmea, NMEA_GGA, NMEA_PRLEN)) {
394 gps_nmea_parse_gga(gps, gps->io.nmea + NMEA_PRLEN+1);
395 if (gps->update_info)
396 g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc)gps->update_info, gps, NULL);
398 } else if (!strncmp(gps->io.nmea, NMEA_GSA, NMEA_PRLEN)) {
399 gps_nmea_parse_gsa(gps, gps->io.nmea + NMEA_PRLEN+1);
400 if (gps->update_satellite)
401 g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc)gps->update_satellite, gps, NULL);
402 if (gps->update_info)
403 g_idle_add_full(G_PRIORITY_DEFAULT, (GSourceFunc)gps->update_info, gps, NULL);
405 } else g_printerr("Unknown NMEA: [%s]\n", gps->io.nmea);