forked from ukyg9e5r6k7gubiekd6/gpsd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdriver_nmea0183.c
3585 lines (3346 loc) · 125 KB
/
driver_nmea0183.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* This file is Copyright (c) 2010-2018 by the GPSD project
* SPDX-License-Identifier: BSD-2-clause
*/
#include "gpsd_config.h" /* must be before all includes */
#include <ctype.h> /* for isdigit() */
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "gpsd.h"
#include "strfuncs.h"
#ifdef NMEA0183_ENABLE
#include "timespec.h"
/**************************************************************************
*
* Parser helpers begin here
*
**************************************************************************/
/* process a pair of latitude/longitude fields starting at field index BEGIN
* The input fields look like this:
* field[0]: 4404.1237962
* field[1]: N
* field[2]: 12118.8472460
* field[3]: W
* input format of lat/lon is NMEA style DDDMM.mmmmmmm
* yes, 7 digits of precision from survey grade GPS
*
* return: 0 == OK, non zero is failure.
*/
static int do_lat_lon(char *field[], struct gps_fix_t *out)
{
double d, m;
double lon;
double lat;
if ('\0' == field[0][0] ||
'\0' == field[1][0] ||
'\0' == field[2][0] ||
'\0' == field[3][0]) {
return 1;
}
lat = safe_atof(field[0]);
m = 100.0 * modf(lat / 100.0, &d);
lat = d + m / 60.0;
if ('S' == field[1][0])
lat = -lat;
lon = safe_atof(field[2]);
m = 100.0 * modf(lon / 100.0, &d);
lon = d + m / 60.0;
if ('W' == field[3][0])
lon = -lon;
if (0 == isfinite(lat) ||
0 == isfinite(lon)) {
return 2;
}
out->latitude = lat;
out->longitude = lon;
return 0;
}
/* process an FAA mode character
* return status as in session->gpsdata.status
*/
static int faa_mode(char mode)
{
int newstatus = STATUS_FIX;
switch (mode) {
case '\0': /* missing */
newstatus = STATUS_NO_FIX;
break;
case 'A': /* Autonomous */
default:
newstatus = STATUS_FIX;
break;
case 'D': /* Differential */
newstatus = STATUS_DGPS_FIX;
break;
case 'E': /* Estimated dead reckoning */
newstatus = STATUS_DR;
break;
case 'F': /* Float RTK */
newstatus = STATUS_RTK_FLT;
break;
case 'N': /* Data Not Valid */
/* already handled, for paranoia sake also here */
newstatus = STATUS_NO_FIX;
break;
case 'P': /* Precise (NMEA 4+) */
newstatus = STATUS_DGPS_FIX; /* sort of DGPS */
break;
case 'R': /* fixed RTK */
newstatus = STATUS_RTK_FIX;
break;
case 'S': /* simulator */
newstatus = STATUS_NO_FIX; /* or maybe MODE_FIX? */
break;
}
return newstatus;
}
/**************************************************************************
*
* Scary timestamp fudging begins here
*
* Four sentences, GGA and GLL and RMC and ZDA, contain timestamps.
* GGA/GLL/RMC timestamps look like hhmmss.ss, with the trailing .ss,
* or .sss, part optional.
* RMC has a date field, in the format ddmmyy. ZDA has separate fields
* for day/month/year, with a 4-digit year. This means that for RMC we
* must supply a century and for GGA and GLL we must supply a century,
* year, and day. We get the missing data from a previous RMC or ZDA;
* century in RMC is supplied from the daemon's context (initialized at
* startup time) if there has been no previous ZDA.
*
**************************************************************************/
#define DD(s) ((int)((s)[0]-'0')*10+(int)((s)[1]-'0'))
/* sentence supplied ddmmyy, but no century part
*
* return: 0 == OK, greater than zero on failure
*/
static int merge_ddmmyy(char *ddmmyy, struct gps_device_t *session)
{
int yy;
int mon;
int mday;
int year;
unsigned i; /* NetBSD complains about signed array index */
if (NULL == ddmmyy) {
return 1;
}
for (i = 0; i < 6; i++) {
/* NetBSD 6 wants the cast */
if (0 == isdigit((int)ddmmyy[i])) {
/* catches NUL and non-digits */
/* Telit HE910 can set year to "-1" (1999 - 2000) */
GPSD_LOG(LOG_WARN, &session->context->errout,
"merge_ddmmyy(%s), malformed date\n", ddmmyy);
return 2;
}
}
/* check for termination */
if ('\0' != ddmmyy[6]) {
/* missing NUL */
GPSD_LOG(LOG_WARN, &session->context->errout,
"merge_ddmmyy(%s), malformed date\n", ddmmyy);
return 3;
}
/* should be no defects left to segfault DD() */
yy = DD(ddmmyy + 4);
mon = DD(ddmmyy + 2);
mday = DD(ddmmyy);
/* check for century wrap */
if (session->nmea.date.tm_year % 100 == 99 && yy == 0)
gpsd_century_update(session, session->context->century + 100);
year = (session->context->century + yy);
/* 32 bit systems will break in 2038.
* Telix fails on GPS rollover to 2099, which 32 bit system
* can not handle. So wrap at 2080. That way 64 bit systems
* work until 2080, and 2099 gets reported as 1999.
* since GPS epoch started in 1980, allows for old NMEA to work.
*/
if (2080 <= year) {
year -= 100;
}
if ( (1 > mon ) || (12 < mon ) ) {
GPSD_LOG(LOG_WARN, &session->context->errout,
"merge_ddmmyy(%s), malformed month\n", ddmmyy);
return 4;
} else if ( (1 > mday ) || (31 < mday ) ) {
GPSD_LOG(LOG_WARN, &session->context->errout,
"merge_ddmmyy(%s), malformed day\n", ddmmyy);
return 5;
} else {
GPSD_LOG(LOG_DATA, &session->context->errout,
"merge_ddmmyy(%s) sets year %d\n",
ddmmyy, year);
session->nmea.date.tm_year = year - 1900;
session->nmea.date.tm_mon = mon - 1;
session->nmea.date.tm_mday = mday;
}
GPSD_LOG(LOG_RAW, &session->context->errout,
"merge_ddmmyy(%s) %d %d %d\n",
ddmmyy,
session->nmea.date.tm_mon,
session->nmea.date.tm_mday,
session->nmea.date.tm_year);
return 0;
}
/* decode an hhmmss.ss string into struct tm data and nsecs
*
* return: 0 == OK, otherwise failure
*/
static int decode_hhmmss(struct tm *date, long *nsec, char *hhmmss,
struct gps_device_t *session)
{
int old_hour = date->tm_hour;
int i, sublen;
if (NULL == hhmmss) {
return 1;
}
for (i = 0; i < 6; i++) {
/* NetBSD 6 wants the cast */
if (0 == isdigit((int)hhmmss[i])) {
/* catches NUL and non-digits */
GPSD_LOG(LOG_WARN, &session->context->errout,
"decode_hhmmss(%s), malformed time\n", hhmmss);
return 2;
}
}
/* don't check for termination, might have fractional seconds */
date->tm_hour = DD(hhmmss);
if (date->tm_hour < old_hour) /* midnight wrap */
date->tm_mday++;
date->tm_min = DD(hhmmss + 2);
date->tm_sec = DD(hhmmss + 4);
if ('.' == hhmmss[6] &&
/* NetBSD 6 wants the cast */
0 != isdigit((int)hhmmss[7])) {
i = atoi(hhmmss + 7);
sublen = strlen(hhmmss + 7);
*nsec = (long)i * (long)pow(10.0, 9 - sublen);
} else {
*nsec = 0;
}
return 0;
}
/* update from a UTC time
*
* return: 0 == OK, greater than zero on failure
*/
static int merge_hhmmss(char *hhmmss, struct gps_device_t *session)
{
int old_hour = session->nmea.date.tm_hour;
int i, sublen;
if (NULL == hhmmss) {
return 1;
}
for (i = 0; i < 6; i++) {
/* NetBSD 6 wants the cast */
if (0 == isdigit((int)hhmmss[i])) {
/* catches NUL and non-digits */
GPSD_LOG(LOG_WARN, &session->context->errout,
"merge_hhmmss(%s), malformed time\n", hhmmss);
return 2;
}
}
/* don't check for termination, might have fractional seconds */
session->nmea.date.tm_hour = DD(hhmmss);
if (session->nmea.date.tm_hour < old_hour) /* midnight wrap */
session->nmea.date.tm_mday++;
session->nmea.date.tm_min = DD(hhmmss + 2);
session->nmea.date.tm_sec = DD(hhmmss + 4);
session->nmea.subseconds.tv_sec = 0;
if ('.' == hhmmss[6] &&
/* NetBSD 6 wants the cast */
0 != isdigit((int)hhmmss[7])) {
i = atoi(hhmmss + 7);
sublen = strlen(hhmmss + 7);
session->nmea.subseconds.tv_nsec = (long)i *
(long)pow(10.0, 9 - sublen);
} else {
session->nmea.subseconds.tv_nsec = 0;
}
return 0;
}
static void register_fractional_time(const char *tag, const char *fld,
struct gps_device_t *session)
{
char ts_buf[TIMESPEC_LEN];
if (fld[0] != '\0') {
session->nmea.last_frac_time = session->nmea.this_frac_time;
DTOTS(&session->nmea.this_frac_time, safe_atof(fld));
session->nmea.latch_frac_time = true;
GPSD_LOG(LOG_DATA, &session->context->errout,
"%s: registers fractional time %s\n",
tag,
timespec_str(&session->nmea.this_frac_time, ts_buf,
sizeof(ts_buf)));
}
}
/**************************************************************************
*
* NMEA sentence handling begins here
*
**************************************************************************/
/* process xxVTG
* $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K
* $GPVTG,054.7,T,034.4,M,005.5,N,010.2,K,A
*
* where:
* 1,2 054.7,T True track made good (degrees)
* 3,4 034.4,M Magnetic track made good
* 5,6 005.5,N Ground speed, knots
* 7,8 010.2,K Ground speed, Kilometers per hour
* 9 A Mode Indicator (optional)
* see faa_mode() for possible mode values
*
* see also:
* https://gpsd.gitlab.io/gpsd/NMEA.html#_vtg_track_made_good_and_ground_speed
*/
static gps_mask_t processVTG(int count,
char *field[],
struct gps_device_t *session)
{
gps_mask_t mask = ONLINE_SET;
if( (field[1][0] == '\0') || (field[5][0] == '\0')){
return mask;
}
/* ignore empty/missing field, fix mode of last resort */
if ((count > 9) && ('\0' != field[9][0])) {
switch (field[9][0]) {
case 'A':
/* Autonomous, 2D or 3D fix */
/* FALL THROUGH */
case 'D':
/* Differential, 2D or 3D fix */
// MODE_SET here causes issues
// mask |= MODE_SET;
break;
case 'E':
/* Estimated, DR only */
/* FALL THROUGH */
case 'N':
/* Not Valid */
// MODE_SET here causes issues
// mask |= MODE_SET;
// nothing to use here, leave
return mask;
default:
/* Huh? */
break;
}
}
// set true track
session->newdata.track = safe_atof(field[1]);
mask |= TRACK_SET;
// set magnetic variation
if (field[3][0] != '\0'){ // ignore empty fields
session->newdata.magnetic_track = safe_atof(field[3]);
mask |= MAGNETIC_TRACK_SET;
}
session->newdata.speed = safe_atof(field[5]) * KNOTS_TO_MPS;
mask |= SPEED_SET;
GPSD_LOG(LOG_DATA, &session->context->errout,
"VTG: course(T)=%.2f, course(M)=%.2f, speed=%.2f",
session->newdata.track, session->newdata.magnetic_track,
session->newdata.speed);
return mask;
}
/* Recommend Minimum Course Specific GPS/TRANSIT Data */
static gps_mask_t processRMC(int count, char *field[],
struct gps_device_t *session)
{
/*
* RMC,225446.33,A,4916.45,N,12311.12,W,000.5,054.7,191194,020.3,E,A*68
* 1 225446.33 Time of fix 22:54:46 UTC
* 2 A Status of Fix:
* A = Autonomous, valid;
* D = Differential, valid;
* V = invalid
* 3,4 4916.45,N Latitude 49 deg. 16.45 min North
* 5,6 12311.12,W Longitude 123 deg. 11.12 min West
* 7 000.5 Speed over ground, Knots
* 8 054.7 Course Made Good, True north
* 9 181194 Date of fix ddmmyy. 18 November 1994
* 10,11 020.3,E Magnetic variation 20.3 deg East
* 12 A FAA mode indicator (NMEA 2.3 and later)
* see faa_mode() for possible mode values
* 13 V Nav Status (NMEA 4.1 and later)
* A=autonomous,
* D=differential,
* E=Estimated,
* M=Manual input mode
* N=not valid,
* S=Simulator,
* V = Valid
* *68 mandatory nmea_checksum
*
* SiRF chipsets don't return either Mode Indicator or magnetic variation.
*/
gps_mask_t mask = ONLINE_SET;
char status = field[2][0];
int newstatus;
switch (status) {
default:
/* missing */
/* FALLTHROUGH */
case 'V':
/* Invalid */
session->gpsdata.status = STATUS_NO_FIX;
session->newdata.mode = MODE_NO_FIX;
mask |= STATUS_SET | MODE_SET;
break;
case 'D':
/* Differential Fix */
/* FALLTHROUGH */
case 'A':
/* Valid Fix */
/*
* The MTK3301, Royaltek RGM-3800, and possibly other
* devices deliver bogus time values when the navigation
* warning bit is set.
*/
if ('\0' != field[1][0] &&
9 < count &&
'\0' != field[9][0]) {
if (0 == merge_hhmmss(field[1], session) &&
0 == merge_ddmmyy(field[9], session)) {
/* got a good data/time */
mask |= TIME_SET;
register_fractional_time(field[0], field[1], session);
}
}
/* else, no point to the time only case, no regressions with that */
if (0 == do_lat_lon(&field[3], &session->newdata)) {
newstatus = STATUS_FIX;
mask |= LATLON_SET;
if (MODE_2D >= session->gpsdata.fix.mode) {
/* we have at least a 2D fix */
/* might cause blinking */
session->newdata.mode = MODE_2D;
mask |= MODE_SET;
}
} else {
newstatus = STATUS_NO_FIX;
session->newdata.mode = MODE_NO_FIX;
mask |= MODE_SET;
}
if ('\0' != field[7][0]) {
session->newdata.speed = safe_atof(field[7]) * KNOTS_TO_MPS;
mask |= SPEED_SET;
}
if ('\0' != field[8][0]) {
session->newdata.track = safe_atof(field[8]);
mask |= TRACK_SET;
}
/* get magnetic variation */
if ('\0' != field[10][0] &&
'\0' != field[11][0]) {
session->newdata.magnetic_var = safe_atof(field[10]);
switch (field[11][0]) {
case 'E':
/* no change */
break;
case 'W':
session->newdata.magnetic_var = -session->newdata.magnetic_var;
break;
default:
/* huh? */
session->newdata.magnetic_var = NAN;
break;
}
if (0 == isfinite(session->newdata.magnetic_var) ||
0.09 >= fabs(session->newdata.magnetic_var)) {
/* some GPS set 0.0,E, or 0,w instead of blank */
session->newdata.magnetic_var = NAN;
} else {
mask |= MAGNETIC_TRACK_SET;
}
}
if (count >= 12) {
newstatus = faa_mode(field[12][0]);
}
/*
* This copes with GPSes like the Magellan EC-10X that *only* emit
* GPRMC. In this case we set mode and status here so the client
* code that relies on them won't mistakenly believe it has never
* received a fix.
*/
if (3 < session->gpsdata.satellites_used) {
/* 4 sats used means 3D */
session->newdata.mode = MODE_3D;
mask |= MODE_SET;
} else if (0 != isfinite(session->gpsdata.fix.altHAE) ||
0 != isfinite(session->gpsdata.fix.altMSL)) {
/* we probably have at least a 3D fix */
/* this handles old GPS that do not report 3D */
session->newdata.mode = MODE_3D;
mask |= MODE_SET;
}
session->gpsdata.status = newstatus;
}
GPSD_LOG(LOG_DATA, &session->context->errout,
"RMC: ddmmyy=%s hhmmss=%s lat=%.2f lon=%.2f "
"speed=%.2f track=%.2f mode=%d var=%.1f status=%d\n",
field[9], field[1],
session->newdata.latitude,
session->newdata.longitude,
session->newdata.speed,
session->newdata.track,
session->newdata.mode,
session->newdata.magnetic_var,
session->gpsdata.status);
return mask;
}
/* Geographic position - Latitude, Longitude */
static gps_mask_t processGLL(int count, char *field[],
struct gps_device_t *session)
{
/* Introduced in NMEA 3.0.
*
* $GPGLL,4916.45,N,12311.12,W,225444,A,A*5C
*
* 1,2: 4916.46,N Latitude 49 deg. 16.45 min. North
* 3,4: 12311.12,W Longitude 123 deg. 11.12 min. West
* 5: 225444 Fix taken at 22:54:44 UTC
* 6: A Data valid
* 7: A Autonomous mode
* 8: *5C Mandatory NMEA checksum
*
* 1,2 Latitude, N (North) or S (South)
* 3,4 Longitude, E (East) or W (West)
* 5 UTC of position
* 6 A = Active, V = Invalid data
* 7 Mode Indicator
* See faa_mode() for possible mode values.
*
* I found a note at <http://www.secoh.ru/windows/gps/nmfqexep.txt>
* indicating that the Garmin 65 does not return time and status.
* SiRF chipsets don't return the Mode Indicator.
* This code copes gracefully with both quirks.
*
* Unless you care about the FAA indicator, this sentence supplies nothing
* that GPRMC doesn't already. But at least two (Garmin GPS 48 and
* Magellan Triton 400) actually ship updates in GLL that aren't redundant.
*
*/
char *status = field[7];
gps_mask_t mask = ONLINE_SET;
if (field[5][0] != '\0') {
if (0 == merge_hhmmss(field[5], session)) {
register_fractional_time(field[0], field[5], session);
if (session->nmea.date.tm_year == 0)
GPSD_LOG(LOG_WARN, &session->context->errout,
"can't use GLL time until after ZDA or RMC"
" has supplied a year.\n");
else {
mask = TIME_SET;
}
}
}
if ('\0' == field[6][0] ||
'V' == field[6][0]) {
/* Invalid */
session->gpsdata.status = STATUS_NO_FIX;
session->newdata.mode = MODE_NO_FIX;
mask |= STATUS_SET | MODE_SET;
} else if ('A' == field[6][0] &&
(count < 8 || *status != 'N') &&
0 == do_lat_lon(&field[1], &session->newdata)) {
int newstatus;
mask |= LATLON_SET;
newstatus = STATUS_FIX;
if (count >= 8) {
newstatus = faa_mode(*status);
}
/*
* This is a bit dodgy. Technically we shouldn't set the mode
* bit until we see GSA, or similar. But it may be later in the
* cycle, some devices like the FV-18 don't send it by default,
* and elsewhere in the code we want to be able to test for the
* presence of a valid fix with mode > MODE_NO_FIX.
*/
if (0 != isfinite(session->gpsdata.fix.altHAE) ||
0 != isfinite(session->gpsdata.fix.altMSL)) {
session->newdata.mode = MODE_3D;
mask |= MODE_SET;
} else if (3 < session->gpsdata.satellites_used) {
/* 4 sats used means 3D */
session->newdata.mode = MODE_3D;
mask |= MODE_SET;
} else if (MODE_2D > session->gpsdata.fix.mode ||
(0 == isfinite(session->oldfix.altHAE) &&
0 == isfinite(session->oldfix.altMSL))) {
session->newdata.mode = MODE_2D;
mask |= MODE_SET;
}
session->gpsdata.status = newstatus;
} else {
session->gpsdata.status = STATUS_NO_FIX;
session->newdata.mode = MODE_NO_FIX;
mask |= STATUS_SET | MODE_SET;
}
GPSD_LOG(LOG_DATA, &session->context->errout,
"GLL: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d\n",
field[5],
session->newdata.latitude,
session->newdata.longitude,
session->newdata.mode,
session->gpsdata.status);
return mask;
}
/* Geographic position - Latitude, Longitude, and more */
static gps_mask_t processGNS(int count UNUSED, char *field[],
struct gps_device_t *session)
{
/* Introduced in NMEA 4.0?
*
* This mostly duplicates RMC, except for the multi GNSS mode
* indicatore.
*
* Example. Ignore the line break.
* $GPGNS,224749.00,3333.4268304,N,11153.3538273,W,D,19,0.6,406.110,
* -26.294,6.0,0138,S,*6A
*
* 1: 224749.00 UTC HHMMSS.SS. 22:47:49.00
* 2: 3333.4268304 Latitude DDMM.MMMMM. 33 deg. 33.4268304 min
* 3: N Latitude North
* 4: 12311.12 Longitude 111 deg. 53.3538273 min
* 5: W Longitude West
* 6: D FAA mode indicator
* see faa_mode() for possible mode values
* May be one to four characters.
* Char 1 = GPS
* Char 2 = GLONASS
* Char 3 = ?
* Char 4 = ?
* 7: 19 Number of Satellites used in solution
* 8: 0.6 HDOP
* 9: 406110 MSL Altitude in meters
* 10: -26.294 Geoid separation in meters
* 11: 6.0 Age of differential corrections, in seconds
* 12: 0138 Differential reference station ID
* 13: S NMEA 4.1+ Navigation status
* S = Safe
* C = Caution
* U = Unsafe
* V = Not valid for navigation
* 8: *6A Mandatory NMEA checksum
*
*/
int newstatus;
int satellites_used;
gps_mask_t mask = ONLINE_SET;
if (field[1][0] != '\0') {
if (0 == merge_hhmmss(field[1], session)) {
register_fractional_time(field[0], field[1], session);
if (session->nmea.date.tm_year == 0) {
GPSD_LOG(LOG_WARN, &session->context->errout,
"can't use GNS time until after ZDA or RMC"
" has supplied a year.\n");
} else {
mask = TIME_SET;
}
}
}
/* FAA mode: not valid, ignore
* Yes, in 2019 a GLONASS only fix may be valid, but not worth
* the confusion */
if ('\0' == field[6][0] || /* FAA mode: missing */
'N' == field[6][0]) { /* FAA mode: not valid */
session->newdata.mode = MODE_NO_FIX;
mask |= MODE_SET;
return mask;
}
/* navigation status, assume S=safe and C=caution are OK */
/* can be missing on valid fix */
if ('U' == field[13][0] || /* Unsafe */
'V' == field[13][0]) { /* not valid */
return mask;
}
satellites_used = atoi(field[7]);
if (0 == do_lat_lon(&field[2], &session->newdata)) {
mask |= LATLON_SET;
session->newdata.mode = MODE_2D;
if ('\0' != field[9][0]) {
/* altitude is MSL */
session->newdata.altMSL = safe_atof(field[9]);
if (0 != isfinite(session->newdata.altMSL)) {
mask |= ALTITUDE_SET;
if (3 < satellites_used) {
/* more than 3 sats used means 3D */
session->newdata.mode = MODE_3D;
}
}
/* only need geoid_sep if in 3D mode */
if ('\0' != field[10][0]) {
session->newdata.geoid_sep = safe_atof(field[10]);
}
/* Let gpsd_error_model() deal with geoid_sep and altHAE */
}
} else {
session->newdata.mode = MODE_NO_FIX;
mask |= MODE_SET;
}
if (field[8][0] != '\0') {
session->gpsdata.dop.hdop = safe_atof(field[8]);
}
newstatus = faa_mode(field[6][0]);
session->gpsdata.status = newstatus;
mask |= MODE_SET;
/* get DGPS stuff */
if ('\0' != field[11][0] &&
'\0' != field[12][0]) {
/* both, or neither */
session->newdata.dgps_age = safe_atof(field[11]);
session->newdata.dgps_station = atoi(field[12]);
}
GPSD_LOG(LOG_DATA, &session->context->errout,
"GNS: hhmmss=%s lat=%.2f lon=%.2f mode=%d status=%d\n",
field[1],
session->newdata.latitude,
session->newdata.longitude,
session->newdata.mode,
session->gpsdata.status);
return mask;
}
/* Global Positioning System Fix Data */
static gps_mask_t processGGA(int c UNUSED, char *field[],
struct gps_device_t *session)
{
/*
* GGA,123519,4807.038,N,01131.324,E,1,08,0.9,545.4,M,46.9,M, , *42
* 1 123519 Fix taken at 12:35:19 UTC
* 2,3 4807.038,N Latitude 48 deg 07.038' N
* 4,5 01131.324,E Longitude 11 deg 31.324' E
* 6 1 Fix quality:
* 0 = invalid,
* 1 = GPS,
* u-blox may use 1 for Estimated
* 2 = DGPS,
* 3 = PPS (Precise Position Service),
* 4 = RTK (Real Time Kinematic) with fixed integers,
* 5 = Float RTK,
* 6 = Estimated,
* 7 = Manual,
* 8 = Simulator
* 7 08 Number of satellites in use
* 8 0.9 Horizontal dilution of position
* 9,10 545.4,M Altitude, Meters MSL
* 11,12 46.9,M Height of geoid (mean sea level) above WGS84
* ellipsoid, in Meters
* 13 33 time in seconds since last DGPS update
* usually empty
* 14 1023 DGPS station ID number (0000-1023)
* usually empty
*
* Some GPS, like the SiRFstarV in NMEA mode, send both GPGSA and
* GLGPSA with identical data.
*/
gps_mask_t mask = ONLINE_SET;
int newstatus;
char last_last_gga_talker = session->nmea.last_gga_talker;
int fix;
int satellites_visible;
session->nmea.last_gga_talker = field[0][1];
if (0 == strlen(field[6])) {
/* no data is no data, assume no fix
* the test/daemon/myguide-3100.log shows lat/lon/alt but
* no status, and related RMC shows no fix. */
fix = -1;
} else {
fix = atoi(field[6]);
}
switch (fix) {
case 0: /* no fix */
newstatus = STATUS_NO_FIX;
break;
case 1:
/* could be 2D, 3D, GNSSDR */
newstatus = STATUS_FIX;
break;
case 2: /* differential */
newstatus = STATUS_DGPS_FIX;
break;
case 3:
/* GPS PPS, fix valid, could be 2D, 3D, GNSSDR */
newstatus = STATUS_PPS_FIX;
break;
case 4: /* RTK integer */
newstatus = STATUS_RTK_FIX;
break;
case 5: /* RTK float */
newstatus = STATUS_RTK_FLT;
break;
case 6:
/* dead reckoning, could be valid or invalid */
newstatus = STATUS_DR;
break;
case 7:
/* manual input, surveyed */
newstatus = STATUS_TIME;
break;
case 8:
/* simulated mode */
/* Garmin GPSMAP and Gecko sends an 8, but undocumented why */
newstatus = STATUS_SIM;
break;
default:
newstatus = -1;
break;
}
if (0 <= newstatus) {
session->gpsdata.status = newstatus;
mask = STATUS_SET;
}
/*
* There are some receivers (the Trimble Placer 450 is an example) that
* don't ship a GSA with mode 1 when they lose satellite lock. Instead
* they just keep reporting GGA and GSA on subsequent cycles with the
* timestamp not advancing and a bogus mode.
*
* On the assumption that GGA is only issued once per cycle we can
* detect this here (it would be nicer to do it on GSA but GSA has
* no timestamp).
*
* SiRFstarV breaks this assumption, sending GGA with different
* talker IDs.
*/
if ('\0' != last_last_gga_talker &&
last_last_gga_talker != session->nmea.last_gga_talker) {
/* skip the time check */
session->nmea.latch_mode = 0;
} else {
session->nmea.latch_mode = strncmp(field[1],
session->nmea.last_gga_timestamp,
sizeof(session->nmea.last_gga_timestamp))==0;
}
if (session->nmea.latch_mode) {
session->gpsdata.status = STATUS_NO_FIX;
session->newdata.mode = MODE_NO_FIX;
GPSD_LOG(LOG_PROG, &session->context->errout,
"xxGGA: latch mode\n");
} else
(void)strlcpy(session->nmea.last_gga_timestamp, field[1],
sizeof(session->nmea.last_gga_timestamp));
/* satellites_visible is used as an accumulator in xxGSV
* so if we set it here we break xxGSV
* Some GPS, like SiRFstarV NMEA, report per GNSS used
* counts in GPGGA and GLGGA.
* session->gpsdata.satellites_visible = atoi(field[7]);
*/
satellites_visible = atoi(field[7]);
if (0 == merge_hhmmss(field[1], session)) {
register_fractional_time(field[0], field[1], session);
if (session->nmea.date.tm_year == 0)
GPSD_LOG(LOG_WARN, &session->context->errout,
"can't use GGA time until after ZDA or RMC"
" has supplied a year.\n");
else {
mask |= TIME_SET;
}
}
if (0 == do_lat_lon(&field[2], &session->newdata)) {
session->newdata.mode = MODE_2D;
mask |= LATLON_SET;
if ('\0' != field[11][0]) {
session->newdata.geoid_sep = safe_atof(field[11]);
} else {
session->newdata.geoid_sep = wgs84_separation(
session->newdata.latitude, session->newdata.longitude);
}
/*
* SiRF chipsets up to version 2.2 report a null altitude field.
* See <http://www.sirf.com/Downloads/Technical/apnt0033.pdf>.
* If we see this, force mode to 2D at most.
*/
if ('\0' != field[9][0]) {
/* altitude is MSL */
session->newdata.altMSL = safe_atof(field[9]);
/* Let gpsd_error_model() deal with altHAE */
mask |= ALTITUDE_SET;
/*
* This is a bit dodgy. Technically we shouldn't set the mode
* bit until we see GSA. But it may be later in the cycle,
* some devices like the FV-18 don't send it by default, and
* elsewhere in the code we want to be able to test for the
* presence of a valid fix with mode > MODE_NO_FIX.
*
* Use satellites_visible as double check on MODE_3D
*/
if (4 <= satellites_visible) {
session->newdata.mode = MODE_3D;
}
}
if (3 > satellites_visible) {
session->newdata.mode = MODE_NO_FIX;
}
} else {
session->newdata.mode = MODE_NO_FIX;
}
mask |= MODE_SET;
if ('\0' != field[8][0]) {
/* why not to newdata? */
session->gpsdata.dop.hdop = safe_atof(field[8]);
}
/* get DGPS stuff */
if ('\0' != field[13][0] &&
'\0' != field[14][0]) {
/* both, or neither */
double age;
int station;
age = safe_atof(field[13]);
station = atoi(field[14]);
if (0.09 < age ||
0 < station) {
/* ignore both zeros */
session->newdata.dgps_age = age;
session->newdata.dgps_station = station;
}
}
GPSD_LOG(LOG_DATA, &session->context->errout,
"GGA: hhmmss=%s lat=%.2f lon=%.2f altMSL=%.2f mode=%d status=%d\n",
field[1],
session->newdata.latitude,
session->newdata.longitude,
session->newdata.altMSL,
session->newdata.mode,
session->gpsdata.status);
return mask;
}
static gps_mask_t processGST(int count, char *field[],
struct gps_device_t *session)
/* GST - GPS Pseudorange Noise Statistics */
{
/*
* GST,hhmmss.ss,x,x,x,x,x,x,x,*hh
* 1 UTC time of associated GGA fix
* 2 Total RMS standard deviation of ranges inputs to the nav solution
* 3 Standard deviation (meters) of semi-major axis of error ellipse
* 4 Standard deviation (meters) of semi-minor axis of error ellipse
* 5 Orientation of semi-major axis of error ellipse (true north degrees)