cleanup specfile for packaging
[profile/ivi/gpsd.git] / driver_nmea.c
1 /*
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.
4  */
5 #include <sys/types.h>
6 #include <stdio.h>
7 #ifndef S_SPLINT_S
8 #include <unistd.h>
9 #endif /* S_SPLINT_S */
10 #include <stdlib.h>
11 #include <math.h>
12 #include <string.h>
13 #include <stdarg.h>
14 #include <ctype.h>
15 #include <time.h>
16
17 #include "gpsd.h"
18 #include "timebase.h"
19
20 #ifdef NMEA_ENABLE
21 /**************************************************************************
22  *
23  * Parser helpers begin here
24  *
25  **************************************************************************/
26
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 */
29 {
30     double lat, lon, d, m;
31     char str[20], *p;
32
33     if (*(p = field[0]) != '\0') {
34         strncpy(str, p, 20);
35         (void)sscanf(p, "%lf", &lat);
36         m = 100.0 * modf(lat / 100.0, &d);
37         lat = d + m / 60.0;
38         p = field[1];
39         if (*p == 'S')
40             lat = -lat;
41         out->latitude = lat;
42     }
43     if (*(p = field[2]) != '\0') {
44         strncpy(str, p, 20);
45         (void)sscanf(p, "%lf", &lon);
46         m = 100.0 * modf(lon / 100.0, &d);
47         lon = d + m / 60.0;
48
49         p = field[3];
50         if (*p == 'W')
51             lon = -lon;
52         out->longitude = lon;
53     }
54 }
55
56 /**************************************************************************
57  *
58  * Scary timestamp fudging begins here
59  *
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.
68  *
69  **************************************************************************/
70
71 #define DD(s)   ((int)((s)[0]-'0')*10+(int)((s)[1]-'0'))
72
73 static void merge_ddmmyy(char *ddmmyy, struct gps_device_t *session)
74 /* sentence supplied ddmmyy, but no century part */
75 {
76     int yy = DD(ddmmyy + 4), year = session->driver.nmea.date.tm_year;
77     int mon = DD(ddmmyy + 2);
78     int mday = DD(ddmmyy);
79
80     if (year <= 0) {
81         year = (CENTURY_BASE + yy) - 1900;
82     } else if (year % 100 != yy) {
83         /* update year */
84         if (year % 100 == 99 && yy == 0)
85             yy += 100;          /* century change */
86         year = year / 100 * 100 + yy;
87     }
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);
94     } else {
95         gpsd_report(LOG_DATA, "merge_ddmmyy(ddmmyy) sets year %d from %s\n",
96                     year, ddmmyy);
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;
100     }
101 }
102
103 static void merge_hhmmss(char *hhmmss, struct gps_device_t *session)
104 /* update from a UTC time */
105 {
106     int old_hour = session->driver.nmea.date.tm_hour;
107
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;
115 }
116
117 static void register_fractional_time(const char *tag, const char *fld,
118                                      struct gps_device_t *session)
119 {
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);
127     }
128 }
129
130 /**************************************************************************
131  *
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.
135  *
136  **************************************************************************/
137
138 #define GPS_TIME_EQUAL(a, b) (fabs((a) - (b)) < 0.01)
139
140 /**************************************************************************
141  *
142  * NMEA sentence handling begins here
143  *
144  **************************************************************************/
145
146 static gps_mask_t processGPRMC(int count, char *field[],
147                                struct gps_device_t *session)
148 /* Recommend Minimum Course Specific GPS/TRANSIT Data */
149 {
150     /*
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
165      *
166      * * SiRF chipsets don't return either Mode Indicator or magnetic variation.
167      */
168     gps_mask_t mask = 0;
169
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;
174             mask |= STATUS_IS;
175         }
176         if (session->newdata.mode >= MODE_2D) {
177             session->newdata.mode = MODE_NO_FIX;
178             mask |= MODE_IS;
179         }
180         /* set something nz, so it won't look like an unknown sentence */
181         mask |= ONLINE_IS;
182     } else if (strcmp(field[2], "A") == 0) {
183         /*
184          * The MKT3301, Royaltek RGM-3800, and possibly other
185          * devices deliver bogus time values when the navigation
186          * warning bit is set.
187          */
188         if (count > 9 && field[1][0] != '\0' && field[9][0] != '\0') {
189             merge_hhmmss(field[1], session);
190             merge_ddmmyy(field[9], session);
191             mask |= TIME_IS;
192             register_fractional_time(field[0], field[1], session);
193         }
194         do_lat_lon(&field[3], &session->newdata);
195         mask |= LATLON_IS;
196         session->newdata.speed = atof(field[7]) * KNOTS_TO_MPS;
197         session->newdata.track = atof(field[8]);
198         mask |= (TRACK_IS | SPEED_IS);
199         /*
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
203          * received a fix.
204          */
205         if (session->gpsdata.status == STATUS_NO_FIX) {
206             session->gpsdata.status = STATUS_FIX;       /* could be DGPS_FIX, we can't tell */
207             mask |= STATUS_IS;
208         }
209         if (session->newdata.mode < MODE_2D) {
210             session->newdata.mode = MODE_2D;
211             mask |= MODE_IS;
212         }
213     }
214
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",
218                 field[9], field[1],
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));
225     return mask;
226 }
227
228 static gps_mask_t processGPGLL(int count, char *field[],
229                                struct gps_device_t *session)
230 /* Geographic position - Latitude, Longitude */
231 {
232     /* Introduced in NMEA 3.0.
233      *
234      * $GPGLL,4916.45,N,12311.12,W,225444,A,A*5C
235      *
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
239      * 6:   A            Data valid
240      * 7:   A            Autonomous mode
241      * 8:   *5C          Mandatory NMEA checksum
242      *
243      * 1,2 Latitude, N (North) or S (South)
244      * 3,4 Longitude, E (East) or W (West)
245      * 5 UTC of position
246      * 6 A=Active, V=Void
247      * 7 Mode Indicator
248      * A = Autonomous mode
249      * D = Differential Mode
250      * E = Estimated (dead-reckoning) mode
251      * M = Manual Input Mode
252      * S = Simulated Mode
253      * N = Data Not Valid
254      *
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.
259      *
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.
263      */
264     char *status = field[7];
265     gps_mask_t mask = 0;
266
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");
273         else {
274             mask = TIME_IS;
275         }
276     }
277     if (strcmp(field[6], "A") == 0 && (count < 8 || *status != 'N')) {
278         int newstatus = session->gpsdata.status;
279
280         do_lat_lon(&field[1], &session->newdata);
281         mask |= LATLON_IS;
282         if (count >= 8 && *status == 'D')
283             newstatus = STATUS_DGPS_FIX;        /* differential */
284         else
285             newstatus = STATUS_FIX;
286         /*
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.
292          */
293         if (session->newdata.mode < MODE_2D) {
294             session->newdata.mode = MODE_2D;
295             mask |= MODE_IS;
296         }
297         session->gpsdata.status = newstatus;
298         mask |= STATUS_IS;
299     }
300
301     gpsd_report(LOG_DATA,
302                 "GLL: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d mask=%s\n",
303                 field[5],
304                 session->newdata.latitude,
305                 session->newdata.longitude,
306                 session->newdata.mode,
307                 session->gpsdata.status, gpsd_maskdump(mask));
308     return mask;
309 }
310
311 static gps_mask_t processGPGGA(int c UNUSED, char *field[],
312                                struct gps_device_t *session)
313 /* Global Positioning System Fix Data */
314 {
315     /*
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)
331      */
332     gps_mask_t mask;
333
334     session->gpsdata.status = atoi(field[6]);
335     mask = STATUS_IS;
336     if (session->gpsdata.status > STATUS_NO_FIX) {
337         char *altitude;
338
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");
344         else {
345             mask |= TIME_IS;
346         }
347         do_lat_lon(&field[2], &session->newdata);
348         mask |= LATLON_IS;
349         session->gpsdata.satellites_used = atoi(field[7]);
350         altitude = field[9];
351         /*
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.
355          */
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;
360                 mask |= MODE_IS;
361             }
362         } else {
363             session->newdata.altitude = atof(altitude);
364             mask |= ALTITUDE_IS;
365             /*
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.
371              */
372             if (session->newdata.mode < MODE_3D) {
373                 session->newdata.mode = MODE_3D;
374                 mask |= MODE_IS;
375             }
376         }
377         if (strlen(field[11]) > 0) {
378             session->gpsdata.separation = atof(field[11]);
379         } else {
380             session->gpsdata.separation =
381                 wgs84_separation(session->newdata.latitude,
382                                  session->newdata.longitude);
383         }
384     }
385     gpsd_report(LOG_DATA,
386                 "GGA: hhmmss=%s lat=%.2f lon=%.2f alt=%.2f mode=%d status=%d mask=%s\n",
387                 field[1],
388                 session->newdata.latitude,
389                 session->newdata.longitude,
390                 session->newdata.altitude,
391                 session->newdata.mode,
392                 session->gpsdata.status, gpsd_maskdump(mask));
393     return mask;
394 }
395
396 static gps_mask_t processGPGSA(int count, char *field[],
397                                struct gps_device_t *session)
398 /* GPS DOP and Active Satellites */
399 {
400     /*
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
403      * 1    = Mode:
404      * M=Manual, forced to operate in 2D or 3D
405      * A=Automatic, 3D/2D
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)
408      * 15   = PDOP
409      * 16   = HDOP
410      * 17   = VDOP
411      */
412     gps_mask_t mask;
413
414     /*
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.
420      */
421     if (count < 17) {
422         gpsd_report(LOG_DATA, "GPGSA: malformed, setting ONLINE_IS only.\n");
423         mask = ONLINE_IS;
424     } else {
425         int i;
426         session->newdata.mode = atoi(field[2]);
427         /*
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.
431          */
432         if (session->newdata.mode == 0 && field[2][0] == 'E')
433             mask = 0;
434         else
435             mask = MODE_IS;
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]);
445             if (prn > 0)
446                 session->gpsdata.used[session->gpsdata.satellites_used++] =
447                     prn;
448         }
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));
457     }
458     return mask;
459 }
460
461 static gps_mask_t processGPGSV(int count, char *field[],
462                                struct gps_device_t *session)
463 /* GPS Satellites in View */
464 {
465     /*
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
468      * 1           Sentence 1 of 2
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
476      */
477     int n, fldnum;
478     if (count <= 3) {
479         gpsd_zero_satellites(&session->gpsdata);
480         session->gpsdata.satellites_visible = 0;
481         return 0;
482     }
483     if (count % 4 != 0) {
484         gpsd_report(LOG_WARN, "malformed GPGSV - fieldcount %d %% 4 != 0\n",
485                     count);
486         gpsd_zero_satellites(&session->gpsdata);
487         session->gpsdata.satellites_visible = 0;
488         return 0;
489     }
490
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);
494         return 0;
495     } else if (session->driver.nmea.part == 1)
496         gpsd_zero_satellites(&session->gpsdata);
497
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);
502             break;
503         }
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++]);
512         /*
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
516          * a multiple of 4.
517          */
518         if (session->gpsdata.PRN[session->gpsdata.satellites_visible] != 0)
519             session->gpsdata.satellites_visible++;
520     }
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);
526
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);
531         return 0;
532     }
533     /*
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.
540      */
541     for (n = 0; n < session->gpsdata.satellites_visible; n++)
542         if (session->gpsdata.azimuth[n] != 0)
543             goto sane;
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);
547     return 0;
548   sane:
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);
552     return SATELLITE_IS;
553 }
554
555 static gps_mask_t processPGRME(int c UNUSED, char *field[],
556                                struct gps_device_t *session)
557 /* Garmin Estimated Position Error */
558 {
559     /*
560      * $PGRME,15.0,M,45.0,M,25.0,M*22
561      * 1    = horizontal error estimate
562      * 2    = units
563      * 3    = vertical error estimate
564      * 4    = units
565      * 5    = spherical error estimate
566      * 6    = units
567      * *
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.
573      */
574     gps_mask_t mask;
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;
580         mask = 0;
581     } else {
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;
589     }
590
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));
595     return mask;
596 }
597
598 static gps_mask_t processGPGBS(int c UNUSED, char *field[],
599                                struct gps_device_t *session)
600 /* NMEA 3.0 Estimated Position Error */
601 {
602     /*
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
612      * 9) Checksum
613      */
614
615     /* register fractional time for end-of-cycle detection */
616     register_fractional_time(field[0], field[1], session);
617
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;
630     } else {
631         gpsd_report(LOG_PROG,
632                     "second in $GPGBS error estimates doesn't match.\n");
633         return 0;
634     }
635 }
636
637 static gps_mask_t processGPZDA(int c UNUSED, char *field[],
638                                struct gps_device_t *session)
639 /* Time & Date */
640 {
641     /*
642      * $GPZDA,160012.71,11,03,2004,-1,00*7D
643      * 1) UTC time (hours, minutes, seconds, may have fractional subsecond)
644      * 2) Day, 01 to 31
645      * 3) Month, 01 to 12
646      * 4) Year (4 digits)
647      * 5) Local zone description, 00 to +- 13 hours
648      * 6) Local zone minutes description, apply same sign as local hours
649      * 7) Checksum
650      *
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).
654      */
655     gps_mask_t mask = 0;
656
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");
660     } else {
661         int year, mon, mday;
662
663         merge_hhmmss(field[1], session);
664         /*
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.
669          */
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]);
679         } else {
680             year -= 1900;       
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;
684             mask = TIME_IS;
685         }
686     };
687     gpsd_report(LOG_DATA, "ZDA: mask=%s\n", gpsd_maskdump(mask));
688     return mask;
689 }
690
691 #ifdef TNT_ENABLE
692 static gps_mask_t processTNTHTM(int c UNUSED, char *field[],
693                                 struct gps_device_t *session)
694 {
695     /*
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.
699      
700      $PTNTHTM,14223,N,169,N,-43,N,13641,2454*15
701      
702      HTM,x.x,a,x.x,a,x.x,a,x.x,x.x*hh<cr><lf>
703      Fields in order:
704      1. True heading (compass measurement + deviation + variation)
705      2. magnetometer status character:
706      C = magnetometer calibration alarm
707      L = low alarm
708      M = low warning
709      N = normal
710      O = high warning
711      P = high alarm
712      V = magnetometer voltage level alarm
713      3. pitch angle
714      4. pitch status character - see field 2 above
715      5. roll angle
716      6. roll status character - see field 2 above
717      7. dip angle
718      8. relative magnitude horizontal component of earth's magnetic field
719      *hh          mandatory nmea_checksum
720      
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
723      occur as a range.
724      */
725     gps_mask_t mask;
726     mask = ONLINE_IS;
727
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;
747     mask |= (ATT_IS);
748
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);
753     return mask;
754 }
755 #endif /* TNT_ENABLE */
756
757 #ifdef OCEANSERVER_ENABLE
758 static gps_mask_t processOHPR(int c UNUSED, char *field[],
759                               struct gps_device_t *session)
760 {
761     /*
762      * Proprietary sentence for OceanServer Magnetic Compass.
763      
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>
765      Fields in order:
766      1. Azimuth
767      2. Pitch Angle
768      3. Roll Angle
769      4. Sensor temp, degrees centigrade
770      5. Depth (feet)
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
775      14. Reserved
776      15-16. 2 axis Gyro Output, X,y
777      17. Reserved
778      18. Reserved
779      *hh          mandatory nmea_checksum
780      */
781     gps_mask_t mask;
782     mask = ONLINE_IS;
783
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);
806
807     gpsd_report(LOG_RAW, "Heading %lf.\n", session->gpsdata.attitude.heading);
808     return mask;
809 }
810 #endif /* OCEANSERVER_ENABLE */
811
812 #ifdef ASHTECH_ENABLE
813 static gps_mask_t processPASHR(int c UNUSED, char *field[],
814                                struct gps_device_t *session)
815 {
816     gps_mask_t mask;
817     mask = 0;
818
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",
823                     session->subtype);
824         return mask;
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;
831         } else {
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;
836             else
837                 session->gpsdata.status = STATUS_FIX;
838
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);
853             mask |= DOP_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));
863         }
864     } else if (0 == strcmp("SAT", field[1])) {  /* Satellite Status */
865         int i, n, p, u;
866         n = session->gpsdata.satellites_visible = atoi(field[2]);
867         u = 0;
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;
875         }
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;
881     }
882     return mask;
883 }
884 #endif /* ASHTECH_ENABLE */
885
886 /**************************************************************************
887  *
888  * Entry points begin here
889  *
890  **************************************************************************/
891
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 */
895 {
896     typedef gps_mask_t(*nmea_decoder) (int count, char *f[],
897                                        struct gps_device_t * session);
898     static struct
899     {
900         char *name;
901         int nf;                 /* minimum number of fields required to parse */
902         nmea_decoder decoder;
903     } nmea_phrase[] = {
904         /*@ -nullassign @*/
905         {
906         "PGRMC", 0, NULL},      /* ignore Garmin Sensor Config */
907         {
908         "PGRME", 7, processPGRME}, {
909         "PGRMI", 0, NULL},      /* ignore Garmin Sensor Init */
910         {
911         "PGRMO", 0, NULL},      /* ignore Garmin Sentence Enable */
912             /*
913              * Basic sentences must come after the PG* ones, otherwise
914              * Garmins can get stuck in a loop that looks like this:
915              *
916              * 1. A Garmin GPS in NMEA mode is detected.
917              *
918              * 2. PGRMC is sent to reconfigure to Garmin binary mode.
919              *    If successful, the GPS echoes the phrase.
920              *
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.
923              *
924              * 4. The mode is changed back to NMEA, resulting in an infinite loop.
925              */
926         {
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 */
933         {
934         "ZDA", 7, processGPZDA}, {
935         "GBS", 7, processGPGBS},
936 #ifdef TNT_ENABLE
937         {
938         "PTNTHTM", 9, processTNTHTM},
939 #endif /* TNT_ENABLE */
940 #ifdef ASHTECH_ENABLE
941         {
942         "PASHR", 3, processPASHR},      /* general handler for Ashtech */
943 #endif /* ASHTECH_ENABLE */
944 #ifdef OCEANSERVER_ENABLE
945         {
946         "OHPR", 18, processOHPR},
947 #endif /* OCEANSERVER_ENABLE */
948             /*@ +nullassign @*/
949     };
950
951     int count;
952     gps_mask_t retval = 0;
953     unsigned int i, thistag;
954     char *p, *s, *e;
955     volatile char *t;
956
957     /*
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.
964      */
965     if (strlen(sentence) > NMEA_MAX) {
966         gpsd_report(LOG_WARN, "Overlong packet rejected.\n");
967         return ONLINE_IS;
968     }
969
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 >= ' ');)
976         ++p;
977     if (*p == '*')
978         *p++ = ',';             /* otherwise we drop the last field */
979     *p = '\0';
980     e = p;
981
982     /* split sentence copy on commas, filling the field array */
983     count = 0;
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 */
989         /*@ -compdef @*/
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 */
993             p++;
994         }
995         /*@ +compdef @*/
996     }
997
998     /* point remaining fields at empty string, just in case */
999     for (i = (unsigned int)count;
1000          i <
1001          (unsigned)(sizeof(session->driver.nmea.field) /
1002                     sizeof(session->driver.nmea.field[0])); i++)
1003         session->driver.nmea.field[i] = e;
1004
1005     /* sentences handlers will tell us whren they have fractional time */
1006     session->driver.nmea.latch_frac_time = false;
1007
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)) {
1017                 retval =
1018                     (nmea_phrase[i].decoder) (count,
1019                                               session->driver.nmea.field,
1020                                               session);
1021                 strncpy(session->gpsdata.tag, nmea_phrase[i].name, MAXTAGLEN);
1022                 /*
1023                  * Must force this to be nz, as we're gong to rely on a zero
1024                  * value to mean "no previous tag" later.
1025                  */
1026                 thistag = i + 1;
1027             } else
1028                 retval = ONLINE_IS;     /* unknown sentence */
1029             break;
1030         }
1031     }
1032
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 */
1038     /*@ +usedef @*/
1039
1040     /* timestamp recording for fixes happens here */
1041     if ((retval & TIME_IS) != 0) {
1042         /*
1043          * WARNING: This assumes time is always field 0, and that field 0
1044          * is a timestamp whenever TIME_IS is set.
1045          */
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);
1058     }
1059
1060     /*
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.
1070      */
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);
1076         if (!GPS_TIME_EQUAL
1077             (session->driver.nmea.this_frac_time,
1078              session->driver.nmea.last_frac_time)) {
1079             uint lasttag = session->driver.nmea.lasttag;
1080             retval |= CLEAR_IS;
1081             gpsd_report(LOG_PROG,
1082                         "%s starts a reporting cycle.\n",
1083                         session->driver.nmea.field[0]);
1084             /*
1085              * Have we seen a previously timestamped NMEA tag?
1086              * If so, designate as end-of-cycle marker.
1087              */
1088             if (lasttag > 0
1089                 && (session->driver.nmea.cycle_enders & (1 << lasttag)) ==
1090                 0) {
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);
1095             }
1096         }
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;
1103         }
1104         session->driver.nmea.lasttag = thistag;
1105     }
1106
1107     /* we might have a reliable end-of-cycle */
1108     if (session->driver.nmea.cycle_enders != 0)
1109         session->cycle_end_reliable = true;
1110
1111     return retval;
1112 }
1113
1114 /*@ +mayaliasunique @*/
1115 #endif /* NMEA_ENABLE */
1116
1117 void nmea_add_checksum(char *sentence)
1118 /* add NMEA checksum to a possibly  *-terminated sentence */
1119 {
1120     unsigned char sum = '\0';
1121     char c, *p = sentence;
1122
1123     if (*p == '$' || *p == '!') {
1124         p++;
1125     } else {
1126         gpsd_report(LOG_ERROR, "Bad NMEA sentence: '%s'\n", sentence);
1127     }
1128     while (((c = *p) != '*') && (c != '\0')) {
1129         sum ^= c;
1130         p++;
1131     }
1132     *p++ = '*';
1133     (void)snprintf(p, 5, "%02X\r\n", (unsigned)sum);
1134 }
1135
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 */
1138 {
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);
1143     } else
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);
1147 }
1148
1149 ssize_t nmea_send(struct gps_device_t * session, const char *fmt, ...)
1150 {
1151     char buf[BUFSIZ];
1152     va_list ap;
1153
1154     va_start(ap, fmt);
1155     (void)vsnprintf(buf, sizeof(buf) - 5, fmt, ap);
1156     va_end(ap);
1157     return nmea_write(session, buf, strlen(buf));
1158 }