1 /*
2 *
3 * XASTIR, Amateur Station Tracking and Information Reporting
4 * Copyright (C) 1999,2000 Frank Giannandrea
5 * Copyright (C) 2000-2019 The Xastir Group
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * Look at the README for more information on the program.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif // HAVE_CONFIG_H
27
28 #include "snprintf.h"
29
30 #include <stdio.h>
31 #include <ctype.h>
32 #include <Xm/XmAll.h>
33
34 // The following files support setting the system time from the GPS
35 #if TIME_WITH_SYS_TIME
36 // Define needed by some versions of Linux in order to define
37 // strptime()
38 #ifndef __USE_XOPEN
39 #define __USE_XOPEN
40 #endif
41 #include <sys/time.h>
42 #include <time.h>
43 #else // TIME_WITH_SYS_TIME
44 #if HAVE_SYS_TIME_H
45 #include <sys/time.h>
46 #else // HAVE_SYS_TIME_H
47 #include <time.h>
48 #endif // HAVE_SYS_TIME_H
49 #endif // TIME_WITH_SYS_TIME
50
51 #include <stdlib.h>
52 #include <unistd.h>
53 #include <sys/types.h>
54
55 #include "gps.h"
56 #include "main.h"
57 #include "interface.h"
58 #include "lang.h"
59 #include "util.h"
60
61 // Must be last include file
62 #include "leak_detection.h"
63
64
65
66 char gps_gprmc[MAX_GPS_STRING+1];
67 char gps_gpgga[MAX_GPS_STRING+1];
68 char gps_sats[4] = "";
69 char gps_alt[8] = "";
70 char gps_spd[10] = "";
71 char gps_sunit[2] = "";
72 char gps_cse[10] = "";
73 int gps_valid = 0; // 0=invalid, 1=valid, 2=2D Fix, 3=3D Fix
74
75 int gps_stop_now;
76
77
78
79
80
81 // This function is destructive to its first parameter
82 //
83 // GPRMC,UTC-Time,status(A/V),lat,N/S,lon,E/W,SOG,COG,UTC-Date,Mag-Var,E/W[*CHK]
84 // GPRMC,hhmmss[.sss],{A|V},ddmm.mm[mm],{N|S},dddmm.mm[mm],{E|W},[dd]d.d[ddddd],[dd]d.d[d],ddmmyy,[ddd.d],[{E|W}][,A|D|E|N|S][*CHK]
85 //
86 // The last field before the checksum is entirely optional, and in
87 // fact first appeared in NMEA 2.3 (fairly recently). Most GPS's do
88 // not currently put out that field. The field may be null or
89 // nonexistent including the comma. Only "A" or "D" are considered
90 // to be active and reliable fixes if this field is present.
91 // Fix-Quality:
92 // A: Autonomous
93 // D: Differential
94 // E: Estimated
95 // N: Not Valid
96 // S: Simulator
97 //
98 // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62
99 // $GPRMC,104748.821,A,4301.1492,N,08803.0374,W,0.085048,102.36,010605,,*1A
100 // $GPRMC,104749.821,A,4301.1492,N,08803.0377,W,0.054215,74.60,010605,,*2D
101 //
decode_gps_rmc(char * data,char * long_pos,int long_pos_length,char * lat_pos,int lat_pos_length,char * spd,char * unit,int unit_length,char * cse,time_t * stim,int * status)102 int decode_gps_rmc( char *data,
103 char *long_pos,
104 int long_pos_length,
105 char *lat_pos,
106 int lat_pos_length,
107 char *spd,
108 char *unit,
109 int unit_length,
110 char *cse,
111 time_t *stim,
112 int *status)
113 {
114
115 char *temp_ptr;
116 char *temp_ptr2;
117 char temp_data[MAX_LINE_SIZE+1]; // Big in case we get concatenated packets (it happens!)
118 char sampletime[7]; // We ignore fractional seconds
119 char long_pos_x[11];
120 char long_ew;
121 char lat_pos_y[10];
122 char lat_ns;
123 char speed[10];
124 char speed_unit;
125 char course[8];
126 char sampledate[7];
127
128 #ifdef HAVE_STRPTIME
129 char sampledatime[15];
130 char *tzp;
131 char tzn[512];
132 struct tm stm;
133 #endif // HAVE_STRPTIME
134
135
136 // We should check for a minimum line length before parsing,
137 // and check for end of input while tokenizing.
138
139 if ( (data == NULL) || (strlen(data) < 37) )
140 {
141 return(0); // Not enough data to parse position from.
142 }
143
144 if (!(isRMC(data))) // No GxRMC found
145 {
146 return(0);
147 }
148
149 if(strchr(data,',') == NULL) // No comma found
150 {
151 return(0);
152 }
153
154 (void)strtok(data,","); // get GxRMC and skip it
155 temp_ptr=strtok(NULL,","); // get time
156
157 if (temp_ptr == NULL) // No comma found
158 {
159 return(0);
160 }
161
162 xastir_snprintf(sampletime,
163 sizeof(sampletime),
164 "%s",
165 temp_ptr);
166
167 temp_ptr=strtok(NULL,","); // get fix status
168
169 if (temp_ptr == NULL) // No comma found
170 {
171 return(0);
172 }
173
174 if (temp_ptr[0] == 'A')
175 {
176 *status = 1;
177 }
178 else
179 {
180 *status = 0;
181 }
182
183 xastir_snprintf(temp_data,
184 sizeof(temp_data),
185 "%s",
186 temp_ptr);
187 temp_data[2] = '\0';
188
189 if (temp_data[0] != 'A') // V is a warning but we can get good data still ?
190 {
191 return(0); // Didn't find 'A' in the proper spot
192 }
193
194 temp_ptr=strtok(NULL,","); // get latitude
195
196 if (temp_ptr == NULL)
197 {
198 return(0); // Doesn't look like latitude
199 }
200
201 // Newer GPS'es appear not to zero-fill on the left. Check for
202 // the decimal point in all the possible places.
203 if (temp_ptr[1] != '.'
204 && temp_ptr[2] != '.'
205 && temp_ptr[3] != '.'
206 && temp_ptr[4] != '.')
207 {
208 return(0); // Doesn't look like latitude
209 }
210
211 // Note: Starlink Invicta shows "lllll.ll" format for latitude in
212 // the GPRMC sentence, which would mean we'd need another term in
213 // the above, and would need to terminate at [10] below (making sure
214 // we extended the field another char as well to handle it). I'm
215 // hoping it was a typo in the Starlink Invicta spec, as latitude
216 // never requires three digits for degrees.
217
218 xastir_snprintf(lat_pos_y,
219 sizeof(lat_pos_y),
220 "%s",
221 temp_ptr);
222 lat_pos_y[9] = '\0';
223
224 // Note that some GPS's put out latitude with extra precision, such as 4801.1234
225
226 // Check for comma char, replace with '\0'
227 temp_ptr2 = strstr(lat_pos_y, ",");
228 if (temp_ptr2)
229 {
230 temp_ptr2[0] = '\0';
231 }
232
233 temp_ptr=strtok(NULL,","); // get N-S
234
235 if (temp_ptr == NULL) // No comma found
236 {
237 return(0);
238 }
239
240 xastir_snprintf(temp_data,
241 sizeof(temp_data),
242 "%s",
243 temp_ptr);
244 temp_data[1] = '\0';
245
246 lat_ns=toupper((int)temp_data[0]);
247
248 if(lat_ns != 'N' && lat_ns != 'S')
249 {
250 return(0); // Doesn't look like latitude
251 }
252
253 temp_ptr=strtok(NULL,","); // get long
254
255 if (temp_ptr == NULL)
256 {
257 return(0); // Doesn't look like longitude
258 }
259
260 // Newer GPS'es appear not to zero-fill on the left. Check for
261 // the decimal point in all the possible places.
262 if (temp_ptr[1] != '.'
263 && temp_ptr[2] != '.'
264 && temp_ptr[3] != '.'
265 && temp_ptr[4] != '.'
266 && temp_ptr[5] != '.')
267 {
268 return(0); // Doesn't look like longitude
269 }
270
271
272 xastir_snprintf(long_pos_x,
273 sizeof(long_pos_x),
274 "%s",
275 temp_ptr);
276 long_pos_x[10] = '\0';
277
278 // Note that some GPS's put out longitude with extra precision, such as 12201.1234
279
280 // Check for comma char, replace with '\0'
281 temp_ptr2 = strstr(long_pos_x, ",");
282 if (temp_ptr2)
283 {
284 temp_ptr2[0] = '\0';
285 }
286
287 temp_ptr=strtok(NULL,","); // get E-W
288
289 if (temp_ptr == NULL) // No comma found
290 {
291 return(0);
292 }
293
294 xastir_snprintf(temp_data,
295 sizeof(temp_data),
296 "%s",
297 temp_ptr);
298 temp_data[1] = '\0';
299
300 long_ew=toupper((int)temp_data[0]);
301
302 if (long_ew != 'E' && long_ew != 'W')
303 {
304 return(0); // Doesn't look like longitude
305 }
306
307 temp_ptr=strtok(NULL,","); // Get speed
308
309 if (temp_ptr == 0) // No comma found
310 {
311 return(0);
312 }
313
314 xastir_snprintf(speed,
315 sizeof(speed),
316 "%s",
317 temp_ptr);
318 speed[9] = '\0';
319
320 speed_unit='K';
321 temp_ptr=strtok(NULL,","); // Get course
322
323 if (temp_ptr == NULL) // No comma found
324 {
325 return(0);
326 }
327
328 xastir_snprintf(course,
329 sizeof(course),
330 "%s",
331 temp_ptr);
332 course[7] = '\0';
333
334 temp_ptr=strtok(NULL,","); // get date of fix
335
336 if (temp_ptr == NULL) // No comma found
337 {
338 return(0);
339 }
340
341 xastir_snprintf(sampledate,
342 sizeof(sampledate),
343 "%s",
344 temp_ptr);
345 sampledate[6] = '\0';
346
347
348 // Data is good
349 xastir_snprintf(long_pos, long_pos_length, "%s%c", long_pos_x,long_ew);
350 xastir_snprintf(lat_pos, lat_pos_length, "%s%c", lat_pos_y, lat_ns);
351 xastir_snprintf(spd, 10, "%s", speed);
352 xastir_snprintf(unit, unit_length, "%c", speed_unit);
353 xastir_snprintf(cse, 10, "%s", course);
354
355 #ifdef HAVE_STRPTIME
356 // Translate date/time into time_t GPS time is in UTC. First,
357 // save existing TZ Then set conversion to UTC, then set back to
358 // existing TZ.
359 tzp=getenv("TZ");
360 if ( tzp == NULL )
361 {
362 tzp = "";
363 }
364 xastir_snprintf(tzn,
365 sizeof(tzn),
366 "TZ=%s",
367 tzp);
368 putenv("TZ=UTC");
369 tzset();
370 xastir_snprintf(sampledatime,
371 sizeof(sampledatime),
372 "%s%s",
373 sampledate,
374 sampletime);
375 (void)strptime(sampledatime, "%d%m%y%H%M%S", &stm);
376 *stim=mktime(&stm);
377 putenv(tzn);
378 tzset();
379 #endif // HAVE_STRPTIME
380
381 //fprintf(stderr,"Speed %s\n",spd);
382 return(1);
383 }
384
385
386
387
388
389 // This function is destructive to its first parameter
390 //
391 // GPGGA,UTC-Time,lat,N/S,long,E/W,GPS-Quality,nsat,HDOP,MSL-Meters,M,Geoidal-Meters,M,DGPS-Data-Age(seconds),DGPS-Ref-Station-ID[*CHK]
392 // GPGGA,hhmmss[.sss],ddmm.mm[mm],{N|S},dddmm.mm[mm],{E|W},{0-8},dd,[d]d.d,[-dddd]d.d,M,[-ddd]d.d,M,[dddd.d],[dddd][*CHK]
393 //
394 // GPS-Quality:
395 // 0: Invalid Fix
396 // 1: GPS Fix
397 // 2: DGPS Fix
398 // 3: PPS Fix
399 // 4: RTK Fix
400 // 5: Float RTK Fix
401 // 6: Estimated (dead-reckoning) Fix
402 // 7: Manual Input Mode
403 // 8: Simulation Mode
404 //
405 // $GPGGA,170834,4124.8963,N,08151.6838,W,1,05,1.5,280.2,M,-34.0,M,,,*75
406 // $GPGGA,104438.833,4301.1439,N,08803.0338,W,1,05,1.8,185.8,M,-34.2,M,0.0,0000*40
407
408 // NOTE: while the above specifically refers to $GP strings for the GPS
409 // receivers, there are others such as GNSS, GLONASS, Beidou, and Gallileo
410 // that differ only in the third character. We support those, too.
411 //
decode_gps_gga(char * data,char * long_pos,int long_pos_length,char * lat_pos,int lat_pos_length,char * sats,char * alt,char * aunit,int * status)412 int decode_gps_gga( char *data,
413 char *long_pos,
414 int long_pos_length,
415 char *lat_pos,
416 int lat_pos_length,
417 char *sats,
418 char *alt,
419 char *aunit,
420 int *status )
421 {
422
423 char *temp_ptr;
424 char *temp_ptr2;
425 char temp_data[MAX_LINE_SIZE+1]; // Big in case we get concatenated packets (it happens!)
426 char long_pos_x[11];
427 char long_ew;
428 char lat_pos_y[10];
429 char lat_ns;
430 char sats_visible[4];
431 char altitude[8];
432 char alt_unit;
433
434
435 // We should check for a minimum line length before parsing,
436 // and check for end of input while tokenizing.
437
438
439 if ( (data == NULL) || (strlen(data) < 35) ) // Not enough data to parse position from.
440 {
441 return(0);
442 }
443
444 if (!(isGGA(data)))
445 {
446 return(0);
447 }
448
449 if (strchr(data,',') == NULL)
450 {
451 return(0);
452 }
453
454 if (strtok(data,",") == NULL) // get GPGGA and skip it
455 {
456 return(0);
457 }
458
459 if(strtok(NULL,",") == NULL) // get time and skip it
460 {
461 return(0);
462 }
463
464 temp_ptr = strtok(NULL,","); // get latitude
465
466 if (temp_ptr == NULL)
467 {
468 return(0);
469 }
470
471 xastir_snprintf(lat_pos_y,
472 sizeof(lat_pos_y),
473 "%s",
474 temp_ptr);
475 lat_pos_y[9] = '\0';
476
477 // Note that some GPS's put out latitude with extra precision, such as 4801.1234
478
479 // Check for comma char, replace with '\0'
480 temp_ptr2 = strstr(lat_pos_y, ",");
481 if (temp_ptr2)
482 {
483 temp_ptr2[0] = '\0';
484 }
485
486 temp_ptr = strtok(NULL,","); // get N-S
487
488 if (temp_ptr == NULL)
489 {
490 return(0);
491 }
492
493 xastir_snprintf(temp_data,
494 sizeof(temp_data),
495 "%s",
496 temp_ptr);
497 temp_data[1] = '\0';
498
499 lat_ns=toupper((int)temp_data[0]);
500
501 if(lat_ns != 'N' && lat_ns != 'S')
502 {
503 return(0);
504 }
505
506 temp_ptr = strtok(NULL,","); // get long
507
508 if(temp_ptr == NULL)
509 {
510 return(0);
511 }
512
513 xastir_snprintf(long_pos_x,
514 sizeof(long_pos_x),
515 "%s",
516 temp_ptr);
517 long_pos_x[10] = '\0';
518
519 // Note that some GPS's put out longitude with extra precision, such as 12201.1234
520
521 // Check for comma char, replace with '\0'
522 temp_ptr2 = strstr(long_pos_x, ",");
523 if (temp_ptr2)
524 {
525 temp_ptr2[0] = '\0';
526 }
527
528 temp_ptr = strtok(NULL,","); // get E-W
529
530 if (temp_ptr == NULL)
531 {
532 return(0);
533 }
534
535 xastir_snprintf(temp_data,
536 sizeof(temp_data),
537 "%s",
538 temp_ptr);
539 temp_data[1] = '\0';
540
541 long_ew=toupper((int)temp_data[0]);
542
543 if (long_ew != 'E' && long_ew != 'W')
544 {
545 return(0);
546 }
547
548 temp_ptr = strtok(NULL,","); // get FIX Quality
549
550 if (temp_ptr == NULL)
551 {
552 return(0);
553 }
554
555 xastir_snprintf(temp_data,
556 sizeof(temp_data),
557 "%s",
558 temp_ptr);
559 temp_data[1] = '\0';
560
561 // '0' = bad fix, positive numbers = ok
562 if(temp_data[0] == '0')
563 {
564 return(0);
565 }
566
567 // Save the fix quality in "status"
568 *status = atoi(temp_data);
569
570 temp_ptr=strtok(NULL,","); // Get sats vis
571
572 if (temp_ptr == NULL)
573 {
574 return(0);
575 }
576
577 xastir_snprintf(sats_visible,
578 sizeof(sats_visible),
579 "%s",
580 temp_ptr);
581 sats_visible[2] = '\0';
582
583 temp_ptr=strtok(NULL,","); // get hoz dil
584
585 if (temp_ptr == NULL)
586 {
587 return(0);
588 }
589
590 temp_ptr=strtok(NULL,","); // Get altitude
591
592 if (temp_ptr == NULL)
593 {
594 return(0);
595 }
596
597 // Get altitude
598 xastir_snprintf(altitude,
599 sizeof(altitude),
600 "%s",
601 temp_ptr);
602 altitude[7] = '\0';
603
604 temp_ptr=strtok(NULL,","); // get UNIT
605
606 if (temp_ptr == NULL)
607 {
608 return(0);
609 }
610
611 // get UNIT
612 xastir_snprintf(temp_data,
613 sizeof(temp_data),
614 "%s",
615 temp_ptr);
616 temp_data[1] = '\0';
617
618 alt_unit=temp_data[0];
619
620 // Data is good
621 xastir_snprintf(long_pos, long_pos_length, "%s%c", long_pos_x, long_ew);
622 xastir_snprintf(lat_pos, lat_pos_length, "%s%c", lat_pos_y, lat_ns);
623 xastir_snprintf(sats, 4, "%s", sats_visible);
624 xastir_snprintf(alt, 8, "%s", altitude);
625 xastir_snprintf(aunit, 2, "%c", alt_unit);
626
627 return(1);
628 }
629
630
631
632
633
634 //
635 // Note that the length of "gps_line_data" can be up to
636 // MAX_DEVICE_BUFFER, which is currently set to 4096.
637 //
gps_data_find(char * gps_line_data,int port)638 int gps_data_find(char *gps_line_data, int port)
639 {
640
641 char long_pos[20],lat_pos[20],aunit[2];
642 time_t t;
643 char temp_str[MAX_GPS_STRING+1];
644 int have_valid_string = 0;
645 #ifndef __CYGWIN__
646 struct timeval tv;
647 struct timezone tz;
648 #endif // __CYGWIN__
649
650
651
652 if (isRMC(gps_line_data))
653 {
654
655 if (debug_level & 128)
656 {
657 char filtered_data[MAX_LINE_SIZE+1];
658
659 xastir_snprintf(filtered_data,
660 sizeof(filtered_data),
661 "%s",
662 gps_line_data);
663
664 makePrintable(filtered_data);
665 fprintf(stderr,"Got RMC %s\n", filtered_data);
666 }
667
668 if (debug_level & 128)
669 {
670 // Got GPS RMC String
671 statusline(langcode("BBARSTA015"),0);
672 }
673
674 xastir_snprintf(gps_gprmc,
675 sizeof(gps_gprmc),
676 "%s",
677 gps_line_data);
678
679 xastir_snprintf(temp_str, sizeof(temp_str), "%s", gps_gprmc);
680 // decode_gps_rmc is destructive to its first parameter
681 if (decode_gps_rmc( temp_str,
682 long_pos,
683 sizeof(long_pos),
684 lat_pos,
685 sizeof(lat_pos),
686 gps_spd,
687 gps_sunit,
688 sizeof(gps_sunit),
689 gps_cse,
690 &t,
691 &gps_valid ) == 1) // mod station data
692 {
693 // got GPS data
694 have_valid_string++;
695 if (debug_level & 128)
696 fprintf(stderr,"RMC <%s> <%s><%s> %c <%s>\n",
697 long_pos,lat_pos,gps_spd,gps_sunit[0],gps_cse);
698
699 if (debug_level & 128)
700 {
701 fprintf(stderr,"Checking for Time Set on %d (%d)\n",
702 port, devices[port].set_time);
703 }
704
705 // Don't set the time if it's a Cygwin system. Causes problems with
706 // date, plus time can be an hour off if daylight savings time is
707 // enabled on Windows.
708 //
709 #ifndef __CYGWIN__
710 if (devices[port].set_time)
711 {
712 tv.tv_sec=t;
713 tv.tv_usec=0;
714 tz.tz_minuteswest=0;
715 tz.tz_dsttime=0;
716
717 if (debug_level & 128)
718 {
719 fprintf(stderr,"Setting Time %ld EUID: %d, RUID: %d\n",
720 (long)t, (int)getuid(), (int)getuid());
721 }
722
723 #ifdef HAVE_SETTIMEOFDAY
724
725 ENABLE_SETUID_PRIVILEGE;
726 settimeofday(&tv, &tz);
727 DISABLE_SETUID_PRIVILEGE;
728
729 #endif // HAVE_SETTIMEOFDAY
730
731 }
732
733 #endif // __CYGWIN__
734
735 }
736 }
737 else
738 {
739 if (debug_level & 128)
740 {
741 int i;
742 fprintf(stderr,"Not $GxRMC: ");
743 for (i = 0; i<7; i++)
744 {
745 fprintf(stderr,"%c", gps_line_data[i]);
746 }
747 fprintf(stderr,"\n");
748 }
749 }
750
751 if (isGGA(gps_line_data))
752 {
753
754 if (debug_level & 128)
755 {
756 char filtered_data[MAX_LINE_SIZE+1];
757
758 xastir_snprintf(filtered_data,
759 sizeof(filtered_data),
760 "%s",
761 gps_line_data);
762
763 makePrintable(filtered_data);
764 fprintf(stderr,"Got GGA %s\n", filtered_data);
765 }
766
767 if (debug_level & 128)
768 {
769 // Got GPS GGA String
770 statusline(langcode("BBARSTA016"),0);
771 }
772
773 xastir_snprintf(gps_gpgga,
774 sizeof(gps_gpgga),
775 "%s",
776 gps_line_data);
777
778 xastir_snprintf(temp_str, sizeof(temp_str), "%s", gps_gpgga);
779
780 // decode_gps_gga is destructive to its first parameter
781 if ( decode_gps_gga( temp_str,
782 long_pos,
783 sizeof(long_pos),
784 lat_pos,
785 sizeof(lat_pos),
786 gps_sats,
787 gps_alt,
788 aunit,
789 &gps_valid ) == 1) // mod station data
790 {
791 // got GPS data
792 have_valid_string++;
793 if (debug_level & 128)
794 fprintf(stderr,"GGA <%s> <%s> <%s> <%s> %c\n",
795 long_pos,lat_pos,gps_sats,gps_alt,aunit[0]);
796 }
797 }
798 else
799 {
800 if (debug_level & 128)
801 {
802 int i;
803 fprintf(stderr,"Not $GPGGA: ");
804 for (i = 0; i<7; i++)
805 {
806 fprintf(stderr,"%c",gps_line_data[i]);
807 }
808 fprintf(stderr,"\n");
809 }
810 }
811
812
813 if (have_valid_string)
814 {
815
816 if (debug_level & 128)
817 {
818 statusline(langcode("BBARSTA037"),0);
819 }
820
821 // Go update my screen position
822 my_station_gps_change(long_pos,lat_pos,gps_cse,gps_spd,
823 gps_sunit[0],gps_alt,gps_sats);
824
825 // gps_stop_now is how we tell main.c that we've got a valid GPS string.
826 // Only useful for HSP mode?
827 if (!gps_stop_now)
828 {
829 gps_stop_now=1;
830 }
831
832 // If HSP port, shutdown gps for timed interval
833 if (port_data[port].device_type == DEVICE_SERIAL_TNC_HSP_GPS)
834 {
835 // return dtr to normal
836 port_dtr(port,0);
837 }
838 }
839 return(have_valid_string);
840 }
841
842
843
844
845
846 static char checksum[3];
847
848
849
850
851
852 // Function to compute checksums for NMEA sentences
853 //
854 // Input: "$............*"
855 // Output: Two character string containing the checksum
856 //
857 // Checksum is computed from the '$' to the '*', but not including
858 // these two characters. It is an 8-bit Xor of the characters
859 // specified, encoded in hexadecimal format.
860 //
nmea_checksum(char * nmea_sentence)861 char *nmea_checksum(char *nmea_sentence)
862 {
863 int i;
864 int sum = 0;
865 int right;
866 int left;
867 char convert[17] = "0123456789ABCDEF";
868
869
870 for (i = 1; i < ((int)strlen(nmea_sentence) - 1); i++)
871 {
872 sum = sum ^ nmea_sentence[i];
873 }
874
875 right = sum % 16;
876 left = (sum / 16) % 16;
877
878 xastir_snprintf(checksum, sizeof(checksum), "%c%c",
879 convert[left],
880 convert[right]);
881
882 return(checksum);
883 }
884
885
886
887
888
889
890 // Function which will send an NMEA sentence to a Garmin GPS which
891 // will create a waypoint if the Garmin is set to NMEA-in/NMEA-out
892 // mode. The sentence looks like this:
893 //
894 // $GPWPL,4807.038,N,01131.000,E,WPTNME*31
895 //
896 // $GPWPL,4849.65,N,06428.53,W,0001*54
897 // $GPWPL,4849.70,N,06428.50,W,0002*50
898 //
899 // 4807.038,N Latitude
900 // 01131.000,E Longitude
901 // WPTNME Waypoint Name (stick to 6 chars for compatibility?)
902 // *31 Checksum, always begins with '*'
903 //
904 //
905 // Future implementation ideas:
906 //
907 // Create linked list of waypoints/location.
908 // Use the list to prevent multiple writes of the same waypoint if
909 // nothing has changed.
910 //
911 // Use the list to check distance of previously-written waypoints.
912 // If we're out of range, delete the waypoint and remove it from the
913 // list.
914 //
915 // Perhaps write the list to disk also. As we shut down, delete the
916 // waypoints (self-cleaning). As we come up, load them in again?
917 // We could also just continue cleaning out waypoints that are
918 // out-of-range since the last time we ran the program. That's
919 // probably a better solution.
920 //
create_garmin_waypoint(long latitude,long longitude,char * call_sign)921 void create_garmin_waypoint(long latitude,long longitude,char *call_sign)
922 {
923 char short_callsign[10];
924 char lat_string[15];
925 char long_string[15];
926 char lat_char;
927 char long_char;
928 int i,j,len;
929 char out_string[80];
930 char out_string2[80];
931
932
933 convert_lat_l2s(latitude,
934 lat_string,
935 sizeof(lat_string),
936 CONVERT_HP_NOSP);
937 lat_char = lat_string[strlen(lat_string) - 1];
938 lat_string[strlen(lat_string) - 1] = '\0';
939
940 convert_lon_l2s(longitude,
941 long_string,
942 sizeof(long_string),
943 CONVERT_HP_NOSP);
944 long_char = long_string[strlen(long_string) - 1];
945 long_string[strlen(long_string) - 1] = '\0';
946
947 len = strlen(call_sign);
948 if (len > 9)
949 {
950 len = 9;
951 }
952
953 j = 0;
954 for (i = 0; i <= len; i++) // Copy the '\0' as well
955 {
956 if (call_sign[i] != '-') // We don't want the dash
957 {
958 short_callsign[j++] = call_sign[i];
959 }
960 }
961 short_callsign[6] = '\0'; // Truncate at 6 chars
962
963 // Convert to upper case. Garmin's don't seem to like lower
964 // case waypoint names
965 to_upper(short_callsign);
966
967 //fprintf(stderr,"Creating waypoint for %s:%s\n",call_sign,short_callsign);
968
969 xastir_snprintf(out_string, sizeof(out_string),
970 "$GPWPL,%s,%c,%s,%c,%s*",
971 lat_string,
972 lat_char,
973 long_string,
974 long_char,
975 short_callsign);
976
977 nmea_checksum(out_string);
978
979 strncpy(out_string2, out_string, sizeof(out_string2));
980 out_string2[sizeof(out_string2)-1] = '\0'; // Terminate string
981
982 strcat(out_string2, checksum);
983
984 output_waypoint_data(out_string2);
985
986 //fprintf(stderr,"%s\n",out_string2);
987 }
988
989 // Test if this is a GGA string, irrespective of what constellation it
990 // might be for. This should allow us to support GPS, GLONASS, Gallileo,
991 // Beidou, or GNSS receivers, and multi-constellation receivers.
isGGA(char * gps_line_data)992 int isGGA(char *gps_line_data)
993 {
994 int retval;
995 retval = (strncmp(gps_line_data,"$",1)==0);
996 retval = retval && ((strlen(gps_line_data)>7)
997 && strncmp(&(gps_line_data[3]),"GGA,",4)==0);
998 retval = retval && (strchr("PNALB",gps_line_data[2])!=0);
999 return (retval);
1000 }
1001
1002 // Test if this is an RMC string. See comments for isGGA.
isRMC(char * gps_line_data)1003 int isRMC(char *gps_line_data)
1004 {
1005 int retval;
1006 retval = (strncmp(gps_line_data,"$",1)==0);
1007 retval = retval && ((strlen(gps_line_data)>7)
1008 && strncmp(&(gps_line_data[3]),"RMC,",4)==0);
1009 retval = retval && (strchr("PNALB",gps_line_data[2])!=0);
1010 return (retval);
1011 }
1012