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;
72 gboolean newly_fixed = FALSE;
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) {
86 gps_conn_set_state(gps, RCVR_FIXED);
89 /* Data is invalid - not enough satellites?. */
90 if (gps->io.conn != RCVR_UP) {
91 gps_conn_set_state(gps, RCVR_UP);
92 /* track_add(NULL, FALSE); */
96 /* Parse the latitude. */
97 token = strsep(&sentence, DELIM);
98 if (token && *token) {
99 dpoint = strchr(token, '.');
100 if (!dpoint) /* handle buggy NMEA */
101 dpoint = token + strlen(token);
102 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
104 MACRO_PARSE_INT(tmpi, token);
105 gps->data.lat = tmpi + (tmpd * (1.0 / 60.0));
109 token = strsep(&sentence, DELIM);
110 if (token && *token == 'S')
111 gps->data.lat = -gps->data.lat;
113 /* Parse the longitude. */
114 token = strsep(&sentence, DELIM);
115 if (token && *token) {
116 dpoint = strchr(token, '.');
117 if (!dpoint) /* handle buggy NMEA */
118 dpoint = token + strlen(token);
119 MACRO_PARSE_FLOAT(tmpd, dpoint - 2);
121 MACRO_PARSE_INT(tmpi, token);
122 gps->data.lon = tmpi + (tmpd * (1.0 / 60.0));
126 token = strsep(&sentence, DELIM);
127 if (token && *token == 'W')
128 gps->data.lon = -gps->data.lon;
130 /* Parse speed over ground, knots. */
131 token = strsep(&sentence, DELIM);
132 if (token && *token) {
133 MACRO_PARSE_FLOAT(gps->data.speed, token);
134 if (gps->data.fix > FIX_NOFIX) {
135 gps->data.maxspeed=MAX(gps->data.maxspeed, gps->data.speed);
139 /* Parse heading, degrees from true north. */
140 token = strsep(&sentence, DELIM);
141 if (token && *token) {
142 MACRO_PARSE_FLOAT(gps->data.heading, token);
146 token = strsep(&sentence, DELIM);
147 if (token && *token && gpsdate) {
149 gpsdate[6] = '\0'; /* Make sure time is 6 chars long. */
150 strcat(gpsdate, token);
151 strptime(gpsdate, "%H%M%S%d%m%y", &time);
152 gps->data.time = mktime(&time) + _gmtoffset;
154 gps->data.time = time(NULL);
157 gps_data_integerize(&gps->data);
159 if ((gps->io.conn==RCVR_FIXED) && (gps->update_location!=NULL)) {
160 gps->update_location(gps, newly_fixed);
162 gps->data.lheading=gps->data.heading;
166 gps_nmea_parse_gga(Gps *gps, gchar * sentence)
168 /* GGA Global Positioning System Fix Data
179 4 = Real Time Kinematic
181 6 = estimated (dead reckoning) (2.3 feature)
182 7 = Manual input mode
184 7. Number of satellites being tracked
185 8. Horizontal dilution of position
186 9. Altitude, Meters, above mean sea level
187 10. Alt unit (meters)
188 11. Height of geoid (mean sea level) above WGS84 ellipsoid
190 13. (empty field) time in seconds since last DGPS update
191 14. (empty field) DGPS station ID number
192 15. the checksum data
195 g_debug("%s(): %s", __PRETTY_FUNCTION__, sentence);
198 token = strsep(&sentence, DELIM);
200 token = strsep(&sentence, DELIM);
202 token = strsep(&sentence, DELIM);
204 token = strsep(&sentence, DELIM);
206 token = strsep(&sentence, DELIM);
208 /* Parse Fix quality */
209 token = strsep(&sentence, DELIM);
211 MACRO_PARSE_INT(gps->data.fixquality, token);
213 /* Skip number of satellites */
214 token = strsep(&sentence, DELIM);
216 /* Parse Horizontal dilution of position */
217 token = strsep(&sentence, DELIM);
219 MACRO_PARSE_INT(gps->data.hdop, token);
222 token = strsep(&sentence, DELIM);
223 if (token && *token) {
224 MACRO_PARSE_FLOAT(gps->data.altitude, token);
226 gps->data.altitude = NAN;
230 gps_nmea_parse_gsa(Gps *gps, gchar * sentence)
232 /* GPS DOP and active satellites
233 * 1) Auto selection of 2D or 3D fix (M = manual)
234 * 2) 3D fix - values include: 1 = no fix, 2 = 2D, 3 = 2D
235 * 3) PRNs of satellites used for fix
237 * 4) PDOP (dilution of precision)
238 * 5) Horizontal dilution of precision (HDOP)
239 * 6) Vertical dilution of precision (VDOP)
246 g_debug("%s(): %s\n", __PRETTY_FUNCTION__, sentence);
248 /* Skip Auto selection. */
249 token = strsep(&sentence, DELIM);
252 token = strsep(&sentence, DELIM);
254 MACRO_PARSE_INT(gps->data.fix, token);
256 gps->data.satinuse = 0;
257 for (i = 0; i < 12; i++) {
259 token = strsep(&sentence, DELIM);
265 gps->data.sat[i].fix=FALSE;
269 for (si=0;si<12;si++) {
270 if (gps->data.sat[i].prn==satforfix[si])
271 gps->data.sat[i].fix=TRUE;
275 token = strsep(&sentence, DELIM);
277 MACRO_PARSE_FLOAT(gps->data.pdop, token);
280 token = strsep(&sentence, DELIM);
282 MACRO_PARSE_FLOAT(gps->data.hdop, token);
285 token = strsep(&sentence, DELIM);
287 MACRO_PARSE_FLOAT(gps->data.vdop, token);
291 gps_nmea_parse_gsv(Gps *gps, gchar * sentence)
293 /* Must be GSV - Satellites in view
294 * 1) total number of messages
296 * 3) satellites in view
297 * 4) satellite number
298 * 5) elevation in degrees (0-90)
299 * 6) azimuth in degrees to true north (0-359)
300 * 7) SNR in dB (0-99)
301 * more satellite infos like 4)-7)
305 guint msgcnt = 0, nummsgs = 0;
306 static guint running_total = 0;
307 static guint num_sats_used = 0;
308 static guint satcnt = 0;
309 /* g_printf("%s(): %s\n", __PRETTY_FUNCTION__, sentence); */
311 /* Parse number of messages. */
312 token = strsep(&sentence, DELIM);
314 MACRO_PARSE_INT(nummsgs, token);
316 /* Parse message number. */
317 token = strsep(&sentence, DELIM);
319 MACRO_PARSE_INT(msgcnt, token);
321 /* Parse number of satellites in view. */
322 token = strsep(&sentence, DELIM);
323 if (token && *token) {
324 MACRO_PARSE_INT(gps->data.satinview, token);
325 if (gps->data.satinview > 12) /* Handle buggy NMEA. */
326 gps->data.satinview = 12;
329 /* Loop until there are no more satellites to parse. */
330 while (sentence && satcnt < 12) {
331 /* Get token for Satellite Number. */
332 token = strsep(&sentence, DELIM);
334 gps->data.sat[satcnt].prn = atoi(token);
336 /* Get token for elevation in degrees (0-90). */
337 token = strsep(&sentence, DELIM);
339 gps->data.sat[satcnt].elevation = atoi(token);
341 /* Get token for azimuth in degrees to true north (0-359). */
342 token = strsep(&sentence, DELIM);
344 gps->data.sat[satcnt].azimuth = atoi(token);
346 /* Get token for SNR. */
347 token = strsep(&sentence, DELIM);
348 if (token && *token && (gps->data.sat[satcnt].snr = atoi(token))) {
349 /* SNR is non-zero - add to total and count as used. */
350 running_total += gps->data.sat[satcnt].snr;
356 if (msgcnt == nummsgs) {
357 /* This is the last message. Calculate signal strength. */
359 if (gps->io.conn==RCVR_UP) {
360 gdouble fraction = running_total * sqrt(num_sats_used) / num_sats_used / 100.0;
361 BOUND(fraction, 0.0, 1.0);
362 if (gps->connection_progress!=NULL)
363 gps->connection_progress(gps, fraction);
373 gps_nmea_parse(Gps *gps)
377 g_debug("NMEA: %s", gps->io.nmea);
382 if (strlen(gps->io.nmea)<NMEA_PRLEN+1)
385 if (!strncmp(gps->io.nmea, NMEA_GSV, NMEA_PRLEN)) {
386 gps_nmea_parse_gsv(gps, gps->io.nmea + NMEA_PRLEN+1);
387 } else if (!strncmp(gps->io.nmea, NMEA_RMC, NMEA_PRLEN))
388 gps_nmea_parse_rmc(gps, gps->io.nmea + NMEA_PRLEN+1);
389 else if (!strncmp(gps->io.nmea, NMEA_GGA, NMEA_PRLEN))
390 gps_nmea_parse_gga(gps, gps->io.nmea + NMEA_PRLEN+1);
391 else if (!strncmp(gps->io.nmea, NMEA_GSA, NMEA_PRLEN))
392 gps_nmea_parse_gsa(gps, gps->io.nmea + NMEA_PRLEN+1);
393 else g_printerr("Unknown NMEA: [%s]\n", gps->io.nmea);
394 g_free(gps->io.nmea);