2 * This file is Copyright (c) 2010 by the GPSD project
3 * BSD terms apply: see the file COPYING in the distribution root for details.
9 #endif /* S_SPLINT_S */
21 /**************************************************************************
23 * Parser helpers begin here
25 **************************************************************************/
27 static void do_lat_lon(char *field[], struct gps_fix_t *out)
28 /* process a pair of latitude/longitude fields starting at field index BEGIN */
30 double lat, lon, d, m;
33 if (*(p = field[0]) != '\0') {
35 (void)sscanf(p, "%lf", &lat);
36 m = 100.0 * modf(lat / 100.0, &d);
43 if (*(p = field[2]) != '\0') {
45 (void)sscanf(p, "%lf", &lon);
46 m = 100.0 * modf(lon / 100.0, &d);
56 /**************************************************************************
58 * Scary timestamp fudging begins here
60 * Four sentences, GGA and GLL and RMC and ZDA, contain timestamps.
61 * GGA/GLL/RMC timestamps look like hhmmss.ss, with the trailing .ss part
62 * optional. RMC has a date field, in the format ddmmyy. ZDA has
63 * separate fields for day/month/year, with a 4-digit year. This
64 * means that for RMC we must supply a century and for GGA and GLL we
65 * must supply a century, year, and day. We get the missing data from
66 * a previous RMC or ZDA; century in RMC is supplied by a constant if
67 * there has been no previous ZDA.
69 **************************************************************************/
71 #define DD(s) ((int)((s)[0]-'0')*10+(int)((s)[1]-'0'))
73 static void merge_ddmmyy(char *ddmmyy, struct gps_device_t *session)
74 /* sentence supplied ddmmyy, but no century part */
76 int yy = DD(ddmmyy + 4), year = session->driver.nmea.date.tm_year;
77 int mon = DD(ddmmyy + 2);
78 int mday = DD(ddmmyy);
81 year = (CENTURY_BASE + yy) - 1900;
82 } else if (year % 100 != yy) {
84 if (year % 100 == 99 && yy == 0)
85 yy += 100; /* century change */
86 year = year / 100 * 100 + yy;
88 if ( (1 > year ) || (2200 < year ) ) {
89 gpsd_report(LOG_WARN, "merge_ddmmyy(), bad year: %d\n", year);
90 } else if ( (1 > mon ) || (12 < mon ) ) {
91 gpsd_report(LOG_WARN, "merge_ddmmyy(), malformed month: %2s\n", ddmmyy + 2);
92 } else if ( (1 > mday ) || (31 < mday ) ) {
93 gpsd_report(LOG_WARN, "merge_ddmmyy(), malformed day: %2s\n", ddmmyy);
95 gpsd_report(LOG_DATA, "merge_ddmmyy(ddmmyy) sets year %d from %s\n",
97 session->driver.nmea.date.tm_year = year;
98 session->driver.nmea.date.tm_mon = mon - 1;
99 session->driver.nmea.date.tm_mday = mday;
103 static void merge_hhmmss(char *hhmmss, struct gps_device_t *session)
104 /* update from a UTC time */
106 int old_hour = session->driver.nmea.date.tm_hour;
108 session->driver.nmea.date.tm_hour = DD(hhmmss);
109 if (session->driver.nmea.date.tm_hour < old_hour) /* midnight wrap */
110 session->driver.nmea.date.tm_mday++;
111 session->driver.nmea.date.tm_min = DD(hhmmss + 2);
112 session->driver.nmea.date.tm_sec = DD(hhmmss + 4);
113 session->driver.nmea.subseconds =
114 atof(hhmmss + 4) - session->driver.nmea.date.tm_sec;
117 static void register_fractional_time(const char *tag, const char *fld,
118 struct gps_device_t *session)
120 if (fld[0] != '\0') {
121 session->driver.nmea.last_frac_time =
122 session->driver.nmea.this_frac_time;
123 session->driver.nmea.this_frac_time = atof(fld);
124 session->driver.nmea.latch_frac_time = true;
125 gpsd_report(LOG_DATA, "%s: registers fractional time %.2f\n",
126 tag, session->driver.nmea.this_frac_time);
130 /**************************************************************************
132 * Compare GPS timestamps for equality. Depends on the fact that the
133 * timestamp granularity of GPS is 1/100th of a second. Use this to avoid
134 * naive float comparisons.
136 **************************************************************************/
138 #define GPS_TIME_EQUAL(a, b) (fabs((a) - (b)) < 0.01)
140 /**************************************************************************
142 * NMEA sentence handling begins here
144 **************************************************************************/
146 static gps_mask_t processGPRMC(int count, char *field[],
147 struct gps_device_t *session)
148 /* Recommend Minimum Course Specific GPS/TRANSIT Data */
151 * RMC,225446.33,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E,A*68
152 * 1 225446.33 Time of fix 22:54:46 UTC
153 * 2 A Status of Fix: A = Autonomous, valid;
154 * D = Differential, valid; V = invalid
155 * 3,4 4916.45,N Latitude 49 deg. 16.45 min North
156 * 5,6 12311.12,W Longitude 123 deg. 11.12 min West
157 * 7 000.5 Speed over ground, Knots
158 * 8 054.7 Course Made Good, True north
159 * 9 181194 Date of fix 18 November 1994
160 * 10,11 020.3,E Magnetic variation 20.3 deg East
161 * 12 A FAA mode indicator (NMEA 2.3 and later)
162 * A=autonomous, D=differential, E=Estimated,
163 * N=not valid, S=Simulator, M=Manual input mode
164 * *68 mandatory nmea_checksum
166 * * SiRF chipsets don't return either Mode Indicator or magnetic variation.
170 if (strcmp(field[2], "V") == 0) {
171 /* copes with Magellan EC-10X, see below */
172 if (session->gpsdata.status != STATUS_NO_FIX) {
173 session->gpsdata.status = STATUS_NO_FIX;
176 if (session->newdata.mode >= MODE_2D) {
177 session->newdata.mode = MODE_NO_FIX;
180 /* set something nz, so it won't look like an unknown sentence */
182 } else if (strcmp(field[2], "A") == 0) {
184 * The MKT3301, Royaltek RGM-3800, and possibly other
185 * devices deliver bogus time values when the navigation
186 * warning bit is set.
188 if (count > 9 && field[1][0] != '\0' && field[9][0] != '\0') {
189 merge_hhmmss(field[1], session);
190 merge_ddmmyy(field[9], session);
192 register_fractional_time(field[0], field[1], session);
194 do_lat_lon(&field[3], &session->newdata);
196 session->newdata.speed = atof(field[7]) * KNOTS_TO_MPS;
197 session->newdata.track = atof(field[8]);
198 mask |= (TRACK_IS | SPEED_IS);
200 * This copes with GPSes like the Magellan EC-10X that *only* emit
201 * GPRMC. In this case we set mode and status here so the client
202 * code that relies on them won't mistakenly believe it has never
205 if (session->gpsdata.status == STATUS_NO_FIX) {
206 session->gpsdata.status = STATUS_FIX; /* could be DGPS_FIX, we can't tell */
209 if (session->newdata.mode < MODE_2D) {
210 session->newdata.mode = MODE_2D;
215 gpsd_report(LOG_DATA,
216 "RMC: ddmmyy=%s hhmmss=%s lat=%.2f lon=%.2f "
217 "speed=%.2f track=%.2f mode=%d status=%d mask=%s\n",
219 session->newdata.latitude,
220 session->newdata.longitude,
221 session->newdata.speed,
222 session->newdata.track,
223 session->newdata.mode,
224 session->gpsdata.status, gpsd_maskdump(mask));
228 static gps_mask_t processGPGLL(int count, char *field[],
229 struct gps_device_t *session)
230 /* Geographic position - Latitude, Longitude */
232 /* Introduced in NMEA 3.0.
234 * $GPGLL,4916.45,N,12311.12,W,225444,A,A*5C
236 * 1,2: 4916.46,N Latitude 49 deg. 16.45 min. North
237 * 3,4: 12311.12,W Longitude 123 deg. 11.12 min. West
238 * 5: 225444 Fix taken at 22:54:44 UTC
240 * 7: A Autonomous mode
241 * 8: *5C Mandatory NMEA checksum
243 * 1,2 Latitude, N (North) or S (South)
244 * 3,4 Longitude, E (East) or W (West)
248 * A = Autonomous mode
249 * D = Differential Mode
250 * E = Estimated (dead-reckoning) mode
251 * M = Manual Input Mode
255 * I found a note at <http://www.secoh.ru/windows/gps/nmfqexep.txt>
256 * indicating that the Garmin 65 does not return time and status.
257 * SiRF chipsets don't return the Mode Indicator.
258 * This code copes gracefully with both quirks.
260 * Unless you care about the FAA indicator, this sentence supplies nothing
261 * that GPRMC doesn't already. But at least one Garmin GPS -- the 48
262 * actually ships updates in GPLL that aren't redundant.
264 char *status = field[7];
267 if (field[5][0] != '\0') {
268 merge_hhmmss(field[5], session);
269 register_fractional_time(field[0], field[5], session);
270 if (session->driver.nmea.date.tm_year == 0)
271 gpsd_report(LOG_WARN,
272 "can't use GLL time until after ZDA or RMC has supplied a year.\n");
277 if (strcmp(field[6], "A") == 0 && (count < 8 || *status != 'N')) {
278 int newstatus = session->gpsdata.status;
280 do_lat_lon(&field[1], &session->newdata);
282 if (count >= 8 && *status == 'D')
283 newstatus = STATUS_DGPS_FIX; /* differential */
285 newstatus = STATUS_FIX;
287 * This is a bit dodgy. Technically we shouldn't set the mode
288 * bit until we see GSA. But it may be later in the cycle,
289 * some devices like the FV-18 don't send it by default, and
290 * elsewhere in the code we want to be able to test for the
291 * presence of a valid fix with mode > MODE_NO_FIX.
293 if (session->newdata.mode < MODE_2D) {
294 session->newdata.mode = MODE_2D;
297 session->gpsdata.status = newstatus;
301 gpsd_report(LOG_DATA,
302 "GLL: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d mask=%s\n",
304 session->newdata.latitude,
305 session->newdata.longitude,
306 session->newdata.mode,
307 session->gpsdata.status, gpsd_maskdump(mask));
311 static gps_mask_t processGPGGA(int c UNUSED, char *field[],
312 struct gps_device_t *session)
313 /* Global Positioning System Fix Data */
316 * GGA,123519,4807.038,N,01131.324,E,1,08,0.9,545.4,M,46.9,M, , *42
317 * 1 123519 Fix taken at 12:35:19 UTC
318 * 2,3 4807.038,N Latitude 48 deg 07.038' N
319 * 4,5 01131.324,E Longitude 11 deg 31.324' E
320 * 6 1 Fix quality: 0 = invalid, 1 = GPS, 2 = DGPS,
321 * 3=PPS (Precise Position Service),
322 * 4=RTK (Real Time Kinematic) with fixed integers,
323 * 5=Float RTK, 6=Estimated, 7=Manual, 8=Simulator
324 * 7 08 Number of satellites being tracked
325 * 8 0.9 Horizontal dilution of position
326 * 9,10 545.4,M Altitude, Metres above mean sea level
327 * 11,12 46.9,M Height of geoid (mean sea level) above WGS84
328 * ellipsoid, in Meters
329 * (empty field) time in seconds since last DGPS update
330 * (empty field) DGPS station ID number (0000-1023)
334 session->gpsdata.status = atoi(field[6]);
336 if (session->gpsdata.status > STATUS_NO_FIX) {
339 merge_hhmmss(field[1], session);
340 register_fractional_time(field[0], field[1], session);
341 if (session->driver.nmea.date.tm_year == 0)
342 gpsd_report(LOG_WARN,
343 "can't use GGA time until after ZDA or RMC has supplied a year.\n");
347 do_lat_lon(&field[2], &session->newdata);
349 session->gpsdata.satellites_used = atoi(field[7]);
352 * SiRF chipsets up to version 2.2 report a null altitude field.
353 * See <http://www.sirf.com/Downloads/Technical/apnt0033.pdf>.
354 * If we see this, force mode to 2D at most.
356 if (altitude[0] == '\0') {
357 if (session->newdata.mode == MODE_3D) {
358 session->newdata.mode =
359 session->gpsdata.status ? MODE_2D : MODE_NO_FIX;
363 session->newdata.altitude = atof(altitude);
366 * This is a bit dodgy. Technically we shouldn't set the mode
367 * bit until we see GSA. But it may be later in the cycle,
368 * some devices like the FV-18 don't send it by default, and
369 * elsewhere in the code we want to be able to test for the
370 * presence of a valid fix with mode > MODE_NO_FIX.
372 if (session->newdata.mode < MODE_3D) {
373 session->newdata.mode = MODE_3D;
377 if (strlen(field[11]) > 0) {
378 session->gpsdata.separation = atof(field[11]);
380 session->gpsdata.separation =
381 wgs84_separation(session->newdata.latitude,
382 session->newdata.longitude);
385 gpsd_report(LOG_DATA,
386 "GGA: hhmmss=%s lat=%.2f lon=%.2f alt=%.2f mode=%d status=%d mask=%s\n",
388 session->newdata.latitude,
389 session->newdata.longitude,
390 session->newdata.altitude,
391 session->newdata.mode,
392 session->gpsdata.status, gpsd_maskdump(mask));
396 static gps_mask_t processGPGSA(int count, char *field[],
397 struct gps_device_t *session)
398 /* GPS DOP and Active Satellites */
401 * eg1. $GPGSA,A,3,,,,,,16,18,,22,24,,,3.6,2.1,2.2*3C
402 * eg2. $GPGSA,A,3,19,28,14,18,27,22,31,39,,,,,1.7,1.0,1.3*35
404 * M=Manual, forced to operate in 2D or 3D
406 * 2 = Mode: 1=Fix not available, 2=2D, 3=3D
407 * 3-14 = PRNs of satellites used in position fix (null for unused fields)
415 * One chipset called the i.Trek M3 issues GPGSA lines that look like
416 * this: "$GPGSA,A,1,,,,*32" when it has no fix. This is broken
417 * in at least two ways: it's got the wrong number of fields, and
418 * it claims to be a valid sentence (A flag) when it isn't.
419 * Alarmingly, it's possible this error may be generic to SiRFstarIII.
422 gpsd_report(LOG_DATA, "GPGSA: malformed, setting ONLINE_IS only.\n");
426 session->newdata.mode = atoi(field[2]);
428 * The first arm of this conditional ignores dead-reckoning
429 * fixes from an Antaris chipset. which returns E in field 2
430 * for a dead-reckoning estimate. Fix by Andreas Stricker.
432 if (session->newdata.mode == 0 && field[2][0] == 'E')
436 gpsd_report(LOG_PROG, "GPGSA sets mode %d\n", session->newdata.mode);
437 session->gpsdata.dop.pdop = atof(field[15]);
438 session->gpsdata.dop.hdop = atof(field[16]);
439 session->gpsdata.dop.vdop = atof(field[17]);
440 session->gpsdata.satellites_used = 0;
441 memset(session->gpsdata.used, 0, sizeof(session->gpsdata.used));
442 /* the magic 6 here counts the tag, two mode fields, and the DOP fields */
443 for (i = 0; i < count - 6; i++) {
444 int prn = atoi(field[i + 3]);
446 session->gpsdata.used[session->gpsdata.satellites_used++] =
449 mask |= DOP_IS | USED_IS;
450 gpsd_report(LOG_DATA,
451 "GPGSA: mode=%d used=%d pdop=%.2f hdop=%.2f vdop=%.2f mask=%s\n",
452 session->newdata.mode,
453 session->gpsdata.satellites_used,
454 session->gpsdata.dop.pdop,
455 session->gpsdata.dop.hdop,
456 session->gpsdata.dop.vdop, gpsd_maskdump(mask));
461 static gps_mask_t processGPGSV(int count, char *field[],
462 struct gps_device_t *session)
463 /* GPS Satellites in View */
466 * GSV,2,1,08,01,40,083,46,02,17,308,41,12,07,344,39,14,22,228,45*75
467 * 2 Number of sentences for full data
469 * 08 Total number of satellites in view
470 * 01 Satellite PRN number
471 * 40 Elevation, degrees
472 * 083 Azimuth, degrees
473 * 46 Signal-to-noise ratio in decibels
474 * <repeat for up to 4 satellites per sentence>
475 * There my be up to three GSV sentences in a data packet
479 gpsd_zero_satellites(&session->gpsdata);
480 session->gpsdata.satellites_visible = 0;
483 if (count % 4 != 0) {
484 gpsd_report(LOG_WARN, "malformed GPGSV - fieldcount %d %% 4 != 0\n",
486 gpsd_zero_satellites(&session->gpsdata);
487 session->gpsdata.satellites_visible = 0;
491 session->driver.nmea.await = atoi(field[1]);
492 if (sscanf(field[2], "%d", &session->driver.nmea.part) < 1) {
493 gpsd_zero_satellites(&session->gpsdata);
495 } else if (session->driver.nmea.part == 1)
496 gpsd_zero_satellites(&session->gpsdata);
498 for (fldnum = 4; fldnum < count;) {
499 if (session->gpsdata.satellites_visible >= MAXCHANNELS) {
500 gpsd_report(LOG_ERROR, "internal error - too many satellites!\n");
501 gpsd_zero_satellites(&session->gpsdata);
504 session->gpsdata.PRN[session->gpsdata.satellites_visible] =
505 atoi(field[fldnum++]);
506 session->gpsdata.elevation[session->gpsdata.satellites_visible] =
507 atoi(field[fldnum++]);
508 session->gpsdata.azimuth[session->gpsdata.satellites_visible] =
509 atoi(field[fldnum++]);
510 session->gpsdata.ss[session->gpsdata.satellites_visible] =
511 (float)atoi(field[fldnum++]);
513 * Incrementing this unconditionally falls afoul of chipsets like
514 * the Motorola Oncore GT+ that emit empty fields at the end of the
515 * last sentence in a GPGSV set if the number of satellites is not
518 if (session->gpsdata.PRN[session->gpsdata.satellites_visible] != 0)
519 session->gpsdata.satellites_visible++;
521 if (session->driver.nmea.part == session->driver.nmea.await
522 && atoi(field[3]) != session->gpsdata.satellites_visible)
523 gpsd_report(LOG_WARN,
524 "GPGSV field 3 value of %d != actual count %d\n",
525 atoi(field[3]), session->gpsdata.satellites_visible);
527 /* not valid data until we've seen a complete set of parts */
528 if (session->driver.nmea.part < session->driver.nmea.await) {
529 gpsd_report(LOG_PROG, "Partial satellite data (%d of %d).\n",
530 session->driver.nmea.part, session->driver.nmea.await);
534 * This sanity check catches an odd behavior of SiRFstarII receivers.
535 * When they can't see any satellites at all (like, inside a
536 * building) they sometimes cough up a hairball in the form of a
537 * GSV packet with all the azimuth entries 0 (but nonzero
538 * elevations). This behavior was observed under SiRF firmware
539 * revision 231.000.000_A2.
541 for (n = 0; n < session->gpsdata.satellites_visible; n++)
542 if (session->gpsdata.azimuth[n] != 0)
544 gpsd_report(LOG_WARN, "Satellite data no good (%d of %d).\n",
545 session->driver.nmea.part, session->driver.nmea.await);
546 gpsd_zero_satellites(&session->gpsdata);
549 session->gpsdata.skyview_time = NAN;
550 gpsd_report(LOG_DATA, "GSV: Satellite data OK (%d of %d).\n",
551 session->driver.nmea.part, session->driver.nmea.await);
555 static gps_mask_t processPGRME(int c UNUSED, char *field[],
556 struct gps_device_t *session)
557 /* Garmin Estimated Position Error */
560 * $PGRME,15.0,M,45.0,M,25.0,M*22
561 * 1 = horizontal error estimate
563 * 3 = vertical error estimate
565 * 5 = spherical error estimate
568 * * Garmin won't say, but the general belief is that these are 50% CEP.
569 * * We follow the advice at <http://gpsinformation.net/main/errors.htm>.
570 * * If this assumption changes here, it should also change in garmin.c
571 * * where we scale error estimates from Garmin binary packets, and
572 * * in libgpsd_core.c where we generate $PGRME.
575 if ((strcmp(field[2], "M") != 0) ||
576 (strcmp(field[4], "M") != 0) || (strcmp(field[6], "M") != 0)) {
577 session->newdata.epx =
578 session->newdata.epy =
579 session->newdata.epv = session->gpsdata.epe = 100;
582 session->newdata.epx = session->newdata.epy =
583 atof(field[1]) * (1 / sqrt(2)) * (GPSD_CONFIDENCE / CEP50_SIGMA);
584 session->newdata.epv =
585 atof(field[3]) * (GPSD_CONFIDENCE / CEP50_SIGMA);
586 session->gpsdata.epe =
587 atof(field[5]) * (GPSD_CONFIDENCE / CEP50_SIGMA);
588 mask = HERR_IS | VERR_IS | PERR_IS;
591 gpsd_report(LOG_DATA, "PGRME: epx=%.2f epy=%.2f epv=%.2f mask=%s\n",
592 session->newdata.epx,
593 session->newdata.epy,
594 session->newdata.epv, gpsd_maskdump(mask));
598 static gps_mask_t processGPGBS(int c UNUSED, char *field[],
599 struct gps_device_t *session)
600 /* NMEA 3.0 Estimated Position Error */
603 * $GPGBS,082941.00,2.4,1.5,3.9,25,,-43.7,27.5*65
604 * 1) UTC time of the fix associated with this sentence (hhmmss.ss)
605 * 2) Expected error in latitude (meters)
606 * 3) Expected error in longitude (meters)
607 * 4) Expected error in altitude (meters)
608 * 5) PRN of most likely failed satellite
609 * 6) Probability of missed detection for most likely failed satellite
610 * 7) Estimate of bias in meters on most likely failed satellite
611 * 8) Standard deviation of bias estimate
615 /* register fractional time for end-of-cycle detection */
616 register_fractional_time(field[0], field[1], session);
618 /* check that we're associated with the current fix */
619 if (session->driver.nmea.date.tm_hour == DD(field[1])
620 && session->driver.nmea.date.tm_min == DD(field[1] + 2)
621 && session->driver.nmea.date.tm_sec == DD(field[1] + 4)) {
622 session->newdata.epy = atof(field[2]);
623 session->newdata.epx = atof(field[3]);
624 session->newdata.epv = atof(field[4]);
625 gpsd_report(LOG_DATA, "GBS: epx=%.2f epy=%.2f epv=%.2f mask=%s\n",
626 session->newdata.epx,
627 session->newdata.epy,
628 session->newdata.epv, gpsd_maskdump(HERR_IS | VERR_IS));
629 return HERR_IS | VERR_IS;
631 gpsd_report(LOG_PROG,
632 "second in $GPGBS error estimates doesn't match.\n");
637 static gps_mask_t processGPZDA(int c UNUSED, char *field[],
638 struct gps_device_t *session)
642 * $GPZDA,160012.71,11,03,2004,-1,00*7D
643 * 1) UTC time (hours, minutes, seconds, may have fractional subsecond)
647 * 5) Local zone description, 00 to +- 13 hours
648 * 6) Local zone minutes description, apply same sign as local hours
651 * Note: some devices, like the uBlox ANTARIS 4h, are known to ship ZDAs
652 * with some fields blank under poorly-understood circumstances (probably
653 * when they don't have satellite lock yet).
657 if (field[1][0] == '\0' || field[2][0] == '\0' || field[3][0] == '\0'
658 || field[4][0] == '\0') {
659 gpsd_report(LOG_WARN, "malformed ZDA\n");
663 merge_hhmmss(field[1], session);
665 * We don't register fractional time here because want to leave
666 * ZDA out of end-of-cycle detection. Some devices sensibly emit it only
667 * when they have a fix, so watching for it can make them look
668 * like they have a variable fix reporting cycle.
670 year = atoi(field[4]);
671 mon = atoi(field[3]);
672 mday = atoi(field[2]);
673 if ( (1900 > year ) || (2200 < year ) ) {
674 gpsd_report(LOG_WARN, "malformed ZDA year: %s\n", field[4]);
675 } else if ( (1 > mon ) || (12 < mon ) ) {
676 gpsd_report(LOG_WARN, "malformed ZDA month: %s\n", field[3]);
677 } else if ( (1 > mday ) || (31 < mday ) ) {
678 gpsd_report(LOG_WARN, "malformed ZDA day: %s\n", field[2]);
681 session->driver.nmea.date.tm_year = year;
682 session->driver.nmea.date.tm_mon = mon - 1;
683 session->driver.nmea.date.tm_mday = mday;
687 gpsd_report(LOG_DATA, "ZDA: mask=%s\n", gpsd_maskdump(mask));
692 static gps_mask_t processTNTHTM(int c UNUSED, char *field[],
693 struct gps_device_t *session)
696 * Proprietary sentence for True North Technologies Magnetic Compass.
697 * This may also apply to some Honeywell units since they may have been
698 * designed by True North.
700 $PTNTHTM,14223,N,169,N,-43,N,13641,2454*15
702 HTM,x.x,a,x.x,a,x.x,a,x.x,x.x*hh<cr><lf>
704 1. True heading (compass measurement + deviation + variation)
705 2. magnetometer status character:
706 C = magnetometer calibration alarm
712 V = magnetometer voltage level alarm
714 4. pitch status character - see field 2 above
716 6. roll status character - see field 2 above
718 8. relative magnitude horizontal component of earth's magnetic field
719 *hh mandatory nmea_checksum
721 By default, angles are reported as 26-bit integers: weirdly, the
722 technical manual says either 0 to 65535 or -32768 to 32767 can
728 session->gpsdata.attitude.heading = atof(field[1]);
729 session->gpsdata.attitude.mag_st = *field[2];
730 session->gpsdata.attitude.pitch = atof(field[3]);
731 session->gpsdata.attitude.pitch_st = *field[4];
732 session->gpsdata.attitude.roll = atof(field[5]);
733 session->gpsdata.attitude.roll_st = *field[6];
734 session->gpsdata.attitude.yaw = NAN;
735 session->gpsdata.attitude.yaw_st = '\0';
736 session->gpsdata.attitude.dip = atof(field[7]);
737 session->gpsdata.attitude.mag_len = NAN;
738 session->gpsdata.attitude.mag_x = atof(field[8]);
739 session->gpsdata.attitude.mag_y = NAN;
740 session->gpsdata.attitude.mag_z = NAN;
741 session->gpsdata.attitude.acc_len = NAN;
742 session->gpsdata.attitude.acc_x = NAN;
743 session->gpsdata.attitude.acc_y = NAN;
744 session->gpsdata.attitude.acc_z = NAN;
745 session->gpsdata.attitude.gyro_x = NAN;
746 session->gpsdata.attitude.gyro_y = NAN;
749 gpsd_report(LOG_RAW, "time %.3f, heading %lf (%c).\n",
750 session->newdata.time,
751 session->gpsdata.attitude.heading,
752 session->gpsdata.attitude.mag_st);
755 #endif /* TNT_ENABLE */
757 #ifdef OCEANSERVER_ENABLE
758 static gps_mask_t processOHPR(int c UNUSED, char *field[],
759 struct gps_device_t *session)
762 * Proprietary sentence for OceanServer Magnetic Compass.
764 OHPR,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x,x.x*hh<cr><lf>
769 4. Sensor temp, degrees centigrade
771 6. Magnetic Vector Length
772 7-9. 3 axis Magnetic Field readings x,y,z
773 10. Acceleration Vector Length
774 11-13. 3 axis Acceleration Readings x,y,z
776 15-16. 2 axis Gyro Output, X,y
779 *hh mandatory nmea_checksum
784 session->gpsdata.attitude.heading = atof(field[1]);
785 session->gpsdata.attitude.mag_st = '\0';
786 session->gpsdata.attitude.pitch = atof(field[2]);
787 session->gpsdata.attitude.pitch_st = '\0';
788 session->gpsdata.attitude.roll = atof(field[3]);
789 session->gpsdata.attitude.roll_st = '\0';
790 session->gpsdata.attitude.yaw = NAN;
791 session->gpsdata.attitude.yaw_st = '\0';
792 session->gpsdata.attitude.dip = NAN;
793 session->gpsdata.attitude.temp = atof(field[4]);
794 session->gpsdata.attitude.depth = atof(field[5]) / METERS_TO_FEET;
795 session->gpsdata.attitude.mag_len = atof(field[6]);
796 session->gpsdata.attitude.mag_x = atof(field[7]);
797 session->gpsdata.attitude.mag_y = atof(field[8]);
798 session->gpsdata.attitude.mag_z = atof(field[9]);
799 session->gpsdata.attitude.acc_len = atof(field[10]);
800 session->gpsdata.attitude.acc_x = atof(field[11]);
801 session->gpsdata.attitude.acc_y = atof(field[12]);
802 session->gpsdata.attitude.acc_z = atof(field[13]);
803 session->gpsdata.attitude.gyro_x = atof(field[15]);
804 session->gpsdata.attitude.gyro_y = atof(field[16]);
805 mask |= (ALTITUDE_IS);
807 gpsd_report(LOG_RAW, "Heading %lf.\n", session->gpsdata.attitude.heading);
810 #endif /* OCEANSERVER_ENABLE */
812 #ifdef ASHTECH_ENABLE
813 static gps_mask_t processPASHR(int c UNUSED, char *field[],
814 struct gps_device_t *session)
819 if (0 == strcmp("RID", field[1])) { /* Receiver ID */
820 (void)snprintf(session->subtype, sizeof(session->subtype) - 1,
821 "%s ver %s", field[2], field[3]);
822 gpsd_report(LOG_DATA, "PASHR,RID: subtype=%s mask={}\n",
825 } else if (0 == strcmp("POS", field[1])) { /* 3D Position */
826 mask |= MODE_IS | STATUS_IS | CLEAR_IS;
827 if (0 == strlen(field[2])) {
828 /* empty first field means no 3D fix is available */
829 session->gpsdata.status = STATUS_NO_FIX;
830 session->newdata.mode = MODE_NO_FIX;
832 /* if we make it this far, we at least have a 3D fix */
833 session->newdata.mode = MODE_3D;
834 if (1 == atoi(field[2]))
835 session->gpsdata.status = STATUS_DGPS_FIX;
837 session->gpsdata.status = STATUS_FIX;
839 session->gpsdata.satellites_used = atoi(field[3]);
840 merge_hhmmss(field[4], session);
841 register_fractional_time(field[0], field[4], session);
842 do_lat_lon(&field[5], &session->newdata);
843 session->newdata.altitude = atof(field[9]);
844 session->newdata.track = atof(field[11]);
845 session->newdata.speed = atof(field[12]) / MPS_TO_KPH;
846 session->newdata.climb = atof(field[13]);
847 session->gpsdata.dop.pdop = atof(field[14]);
848 session->gpsdata.dop.hdop = atof(field[15]);
849 session->gpsdata.dop.vdop = atof(field[16]);
850 session->gpsdata.dop.tdop = atof(field[17]);
851 mask |= (TIME_IS | LATLON_IS | ALTITUDE_IS);
852 mask |= (SPEED_IS | TRACK_IS | CLIMB_IS);
854 gpsd_report(LOG_DATA,
855 "PASHR,POS: hhmmss=%s lat=%.2f lon=%.2f alt=%.f speed=%.2f track=%.2f climb=%.2f mode=%d status=%d pdop=%.2f hdop=%.2f vdop=%.2f tdop=%.2f mask=%s\n",
856 field[4], session->newdata.latitude,
857 session->newdata.longitude, session->newdata.altitude,
858 session->newdata.speed, session->newdata.track,
859 session->newdata.climb, session->newdata.mode,
860 session->gpsdata.status, session->gpsdata.dop.pdop,
861 session->gpsdata.dop.hdop, session->gpsdata.dop.vdop,
862 session->gpsdata.dop.tdop, gpsd_maskdump(mask));
864 } else if (0 == strcmp("SAT", field[1])) { /* Satellite Status */
866 n = session->gpsdata.satellites_visible = atoi(field[2]);
868 for (i = 0; i < n; i++) {
869 session->gpsdata.PRN[i] = p = atoi(field[3 + i * 5 + 0]);
870 session->gpsdata.azimuth[i] = atoi(field[3 + i * 5 + 1]);
871 session->gpsdata.elevation[i] = atoi(field[3 + i * 5 + 2]);
872 session->gpsdata.ss[i] = atof(field[3 + i * 5 + 3]);
873 if (field[3 + i * 5 + 4][0] == 'U')
874 session->gpsdata.used[u++] = p;
876 session->gpsdata.satellites_used = u;
877 gpsd_report(LOG_DATA, "PASHR,SAT: used=%d mask=%s\n",
878 session->gpsdata.satellites_used, gpsd_maskdump(mask));
879 session->gpsdata.skyview_time = NAN;
880 mask |= SATELLITE_IS | USED_IS;
884 #endif /* ASHTECH_ENABLE */
886 /**************************************************************************
888 * Entry points begin here
890 **************************************************************************/
892 /*@ -mayaliasunique @*/
893 gps_mask_t nmea_parse(char *sentence, struct gps_device_t * session)
894 /* parse an NMEA sentence, unpack it into a session structure */
896 typedef gps_mask_t(*nmea_decoder) (int count, char *f[],
897 struct gps_device_t * session);
901 int nf; /* minimum number of fields required to parse */
902 nmea_decoder decoder;
906 "PGRMC", 0, NULL}, /* ignore Garmin Sensor Config */
908 "PGRME", 7, processPGRME}, {
909 "PGRMI", 0, NULL}, /* ignore Garmin Sensor Init */
911 "PGRMO", 0, NULL}, /* ignore Garmin Sentence Enable */
913 * Basic sentences must come after the PG* ones, otherwise
914 * Garmins can get stuck in a loop that looks like this:
916 * 1. A Garmin GPS in NMEA mode is detected.
918 * 2. PGRMC is sent to reconfigure to Garmin binary mode.
919 * If successful, the GPS echoes the phrase.
921 * 3. nmea_parse() sees the echo as RMC because the talker ID is
922 * ignored, and fails to recognize the echo as PGRMC and ignore it.
924 * 4. The mode is changed back to NMEA, resulting in an infinite loop.
927 "RMC", 8, processGPRMC}, {
928 "GGA", 13, processGPGGA}, {
929 "GLL", 7, processGPGLL}, {
930 "GSA", 17, processGPGSA}, {
931 "GSV", 0, processGPGSV}, {
932 "VTG", 0, NULL}, /* ignore Velocity Track made Good */
934 "ZDA", 7, processGPZDA}, {
935 "GBS", 7, processGPGBS},
938 "PTNTHTM", 9, processTNTHTM},
939 #endif /* TNT_ENABLE */
940 #ifdef ASHTECH_ENABLE
942 "PASHR", 3, processPASHR}, /* general handler for Ashtech */
943 #endif /* ASHTECH_ENABLE */
944 #ifdef OCEANSERVER_ENABLE
946 "OHPR", 18, processOHPR},
947 #endif /* OCEANSERVER_ENABLE */
952 gps_mask_t retval = 0;
953 unsigned int i, thistag;
958 * We've had reports that on the Garmin GPS-10 the device sometimes
959 * (1:1000 or so) sends garbage packets that have a valid checksum
960 * but are like 2 successive NMEA packets merged together in one
961 * with some fields lost. Usually these are much longer than the
962 * legal limit for NMEA, so we can cope by just tossing out overlong
963 * packets. This may be a generic bug of all Garmin chipsets.
965 if (strlen(sentence) > NMEA_MAX) {
966 gpsd_report(LOG_WARN, "Overlong packet rejected.\n");
970 /*@ -usedef @*//* splint 3.1.1 seems to have a bug here */
971 /* make an editable copy of the sentence */
972 strncpy((char *)session->driver.nmea.fieldcopy, sentence, NMEA_MAX);
973 /* discard the checksum part */
974 for (p = (char *)session->driver.nmea.fieldcopy;
975 (*p != '*') && (*p >= ' ');)
978 *p++ = ','; /* otherwise we drop the last field */
982 /* split sentence copy on commas, filling the field array */
984 t = p; /* end of sentence */
985 p = (char *)session->driver.nmea.fieldcopy + 1; /* beginning of tag, 'G' not '$' */
986 /* while there is a search string and we haven't run off the buffer... */
987 while ((p != NULL) && (p <= t)) {
988 session->driver.nmea.field[count] = p; /* we have a field. record it */
990 if ((p = strchr(p, ',')) != NULL) { /* search for the next delimiter */
991 *p = '\0'; /* replace it with a NUL */
992 count++; /* bump the counters and continue */
998 /* point remaining fields at empty string, just in case */
999 for (i = (unsigned int)count;
1001 (unsigned)(sizeof(session->driver.nmea.field) /
1002 sizeof(session->driver.nmea.field[0])); i++)
1003 session->driver.nmea.field[i] = e;
1005 /* sentences handlers will tell us whren they have fractional time */
1006 session->driver.nmea.latch_frac_time = false;
1008 /* dispatch on field zero, the sentence tag */
1009 for (thistag = i = 0;
1010 i < (unsigned)(sizeof(nmea_phrase) / sizeof(nmea_phrase[0])); ++i) {
1011 s = session->driver.nmea.field[0];
1012 if (strlen(nmea_phrase[i].name) == 3)
1013 s += 2; /* skip talker ID */
1014 if (strcmp(nmea_phrase[i].name, s) == 0) {
1015 if (nmea_phrase[i].decoder != NULL
1016 && (count >= nmea_phrase[i].nf)) {
1018 (nmea_phrase[i].decoder) (count,
1019 session->driver.nmea.field,
1021 strncpy(session->gpsdata.tag, nmea_phrase[i].name, MAXTAGLEN);
1023 * Must force this to be nz, as we're gong to rely on a zero
1024 * value to mean "no previous tag" later.
1028 retval = ONLINE_IS; /* unknown sentence */
1033 /* general handler for MKT3301 vendor specifics */
1034 #ifdef MKT3301_ENABLE
1035 if (strncmp("PMTK", session->driver.nmea.field[0], 4) == 0)
1036 retval = processMKT3301(count, session->driver.nmea.field, session);
1037 #endif /* MKT3301_ENABLE */
1040 /* timestamp recording for fixes happens here */
1041 if ((retval & TIME_IS) != 0) {
1043 * WARNING: This assumes time is always field 0, and that field 0
1044 * is a timestamp whenever TIME_IS is set.
1046 session->newdata.time =
1047 (double)mkgmtime(&session->driver.nmea.date) +
1048 session->driver.nmea.subseconds;
1049 gpsd_report(LOG_DATA,
1050 "%s time (nearest sec) is %2f = %d-%d-%dT%d:%d%d\n",
1051 session->driver.nmea.field[0], session->newdata.time,
1052 1900 + session->driver.nmea.date.tm_year,
1053 session->driver.nmea.date.tm_mon + 1,
1054 session->driver.nmea.date.tm_mday,
1055 session->driver.nmea.date.tm_hour,
1056 session->driver.nmea.date.tm_min,
1057 session->driver.nmea.date.tm_sec);
1061 * The end-of-cycle detector. This code depends on just one
1062 * assumption: if a sentence with a timestamp occurs just before
1063 * start of cycle, then it is always good to trigger a reort on
1064 * that sentence in the future. For devices with a fixed cycle
1065 * this should work perfectly, locking in detection after one
1066 * cycle. Most split-cycle devices (Garmin 48, for example) will
1067 * work fine. Problems will only arise if a a sentence that
1068 * occurs just befiore timestamp increments also occurs in
1069 * mid-cycle, as in the Garmin eXplorist 210; those might jitter.
1071 if (session->driver.nmea.latch_frac_time) {
1072 gpsd_report(LOG_PROG,
1073 "%s reporting cycle started on %.2f.\n",
1074 session->driver.nmea.field[0],
1075 session->driver.nmea.this_frac_time);
1077 (session->driver.nmea.this_frac_time,
1078 session->driver.nmea.last_frac_time)) {
1079 uint lasttag = session->driver.nmea.lasttag;
1081 gpsd_report(LOG_PROG,
1082 "%s starts a reporting cycle.\n",
1083 session->driver.nmea.field[0]);
1085 * Have we seen a previously timestamped NMEA tag?
1086 * If so, designate as end-of-cycle marker.
1089 && (session->driver.nmea.cycle_enders & (1 << lasttag)) ==
1091 session->driver.nmea.cycle_enders |= (1 << lasttag);
1092 gpsd_report(LOG_PROG,
1093 "tagged %s as a cycle ender.\n",
1094 nmea_phrase[lasttag - 1].name);
1097 /* here's where we check for end-of-cycle */
1098 if (session->driver.nmea.cycle_enders & (1 << thistag)) {
1099 gpsd_report(LOG_PROG,
1100 "%s ends a reporting cycle.\n",
1101 session->driver.nmea.field[0]);
1102 retval |= REPORT_IS;
1104 session->driver.nmea.lasttag = thistag;
1107 /* we might have a reliable end-of-cycle */
1108 if (session->driver.nmea.cycle_enders != 0)
1109 session->cycle_end_reliable = true;
1114 /*@ +mayaliasunique @*/
1115 #endif /* NMEA_ENABLE */
1117 void nmea_add_checksum(char *sentence)
1118 /* add NMEA checksum to a possibly *-terminated sentence */
1120 unsigned char sum = '\0';
1121 char c, *p = sentence;
1123 if (*p == '$' || *p == '!') {
1126 gpsd_report(LOG_ERROR, "Bad NMEA sentence: '%s'\n", sentence);
1128 while (((c = *p) != '*') && (c != '\0')) {
1133 (void)snprintf(p, 5, "%02X\r\n", (unsigned)sum);
1136 ssize_t nmea_write(struct gps_device_t *session, char *buf, size_t len UNUSED)
1137 /* ship a command to the GPS, adding * and correct checksum */
1139 (void)strlcpy(session->msgbuf, buf, sizeof(session->msgbuf));
1140 if (session->msgbuf[0] == '$') {
1141 (void)strlcat(session->msgbuf, "*", sizeof(session->msgbuf));
1142 nmea_add_checksum(session->msgbuf);
1144 (void)strlcat(session->msgbuf, "\r\n", sizeof(session->msgbuf));
1145 session->msgbuflen = strlen(session->msgbuf);
1146 return gpsd_write(session, session->msgbuf, session->msgbuflen);
1149 ssize_t nmea_send(struct gps_device_t * session, const char *fmt, ...)
1155 (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
1157 return nmea_write(session, buf, strlen(buf));