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 
25 #ifdef HAVE_CONFIG_H
26   #include "config.h"
27 #endif  // HAVE_CONFIG_H
28 
29 #include "snprintf.h"
30 
31 #if TIME_WITH_SYS_TIME
32   #include <sys/time.h>
33   #include <time.h>
34 #else   // TIME_WITH_SYS_TIME
35   #if HAVE_SYS_TIME_H
36     #include <sys/time.h>
37   #else  // HAVE_SYS_TIME_H
38     #include <time.h>
39   #endif // HAVE_SYS_TIME_H
40 #endif  // TIME_WITH_SYS_TIME
41 
42 #include <unistd.h>
43 #include <signal.h>
44 #include <string.h>
45 
46 // Needed for Solaris
47 #ifdef HAVE_STRINGS_H
48   #include <strings.h>
49 #endif  // HAVE_STRINGS_H
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <sys/ioctl.h>
54 #include <sys/stat.h>
55 #include <ctype.h>
56 #include <math.h>
57 #include <errno.h>
58 
59 #include "xastir.h"
60 #include "util.h"
61 #include "main.h"
62 #include "xa_config.h"
63 #include "datum.h"
64 #include "hashtable.h"
65 #include "hashtable_itr.h"
66 #include "maps.h"
67 
68 
69 #define CHECKMALLOC(m)  if (!m) { fprintf(stderr, "***** Malloc Failed *****\n"); exit(0); }
70 
71 // For mutex debugging with Linux threads only
72 #ifdef MUTEX_DEBUG
73   #include <asm/errno.h>
74   //
75   // Newer pthread function
76   #ifdef HAVE_PTHREAD_MUTEXATTR_SETTYPE
77     extern int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int kind);
78   #endif  // HAVE_PTHREAD_MUTEXATTR_SETTYPE
79   //
80   // Older, deprecated pthread function
81   #ifdef HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
82     extern int pthread_mutexattr_setkind_np(pthread_mutexattr_t *attr, int kind);
83   #endif  // HAVE_PTHREAD_MUTEXATTR_SETKIND_NP
84   //
85 #endif  // MUTEX_DEBUG
86 
87 
88 #ifdef HAVE_LIBCURL
89   #include <curl/curl.h>
90 #endif  // HAVE_LIBCURL
91 
92 // Needed for size_t
93 #include <sys/types.h>
94 
95 
96 // Must be last include file
97 #include "leak_detection.h"
98 
99 
100 
101 int position_amb_chars;
102 char echo_digis[6][MAX_CALLSIGN+1];
103 
104 #define ACCEPT_0N_0E    /* set this to see stations at 0N/0E on the map */
105 
106 struct timeval timer_start;
107 struct timeval timer_stop;
108 struct timezone tz;
109 
110 
111 static struct hashtable *tactical_hash = NULL;
112 #define TACTICAL_HASH_SIZE 1024
113 
114 #define MAX_LOGFILE_SIZE 2048000
115 
116 
117 /////////////////////////////////////////////////////////////////////
118 // convert_from_xastir_coordinates()
119 //
120 // Converts from Xastir coordinate system to lat/lon.  First two
121 // parameters are the output floating point lat/lon values.  2nd two
122 // are the input Xastir X/Y values.
123 //
124 //              0 (90 deg. or 90N)
125 //
126 // 0 (-180 deg. or 180W)      129,600,000 (180 deg. or 180E)
127 //
128 //          64,800,000 (-90 deg. or 90S)
129 //
130 // Returns 0 if error, 1 if good values were converted.
131 //         Errors are due to the x and/or y values exceeding the above
132 //         limits. In such cases the float values are set to appropriate
133 //         minimum or maximum values.
134 /////////////////////////////////////////////////////////////////////
convert_from_xastir_coordinates(float * f_longitude,float * f_latitude,long x,long y)135 int convert_from_xastir_coordinates ( float *f_longitude,
136                                       float *f_latitude,
137                                       long x,
138                                       long y )
139 {
140 
141 //fprintf(stderr,"convert_from_xastir_coordinates\n");
142   int result = 1;  // assume the input values are in range
143 
144   if (x < 0l )
145   {
146     fprintf(stderr,
147             "convert_from_xastir_coordinates:X out-of-range (too low):%lu\n",
148             x);
149     x = 0;
150     result = 0;
151   }
152 
153   if (x > 129600000l)
154   {
155     fprintf(stderr,
156             "convert_from_xastir_coordinates:X out-of-range (too high):%lu\n",
157             x);
158     x = 129600000l;
159     result = 0;
160   }
161 
162   if (y < 0l)
163   {
164     fprintf(stderr,
165             "convert_from_xastir_coordinates:Y out-of-range (too low):%lu\n",
166             y);
167     y = 0;
168     result = 0;
169   }
170 
171   if (y > 64800000l)
172   {
173     fprintf(stderr,
174             "convert_from_xastir_coordinates:Y out-of-range (too high):%lu\n",
175             y);
176     y = 64800000l;
177     result = 0;
178   }
179 
180   *f_latitude  = (float)( -((y - 32400000l) / 360000.0) );
181   *f_longitude = (float)(   (x - 64800000l) / 360000.0  );
182 
183 //fprintf(stderr,"input x: %lu\tinput y: %lu\n",
184 //    x,
185 //    y);
186 //fprintf(stderr,"latitude: %f\tlongitude: %f\n",
187 //    *f_latitude,
188 //    *f_longitude);
189 
190   return(result);
191 }
192 
193 
194 
195 
196 
197 /////////////////////////////////////////////////////////////////////
198 // convert_to_xastir_coordinates()
199 //
200 // Converts from lat/lon to Xastir coordinate system.
201 // First two parameters are the output Xastir X/Y values,
202 // 2nd two are the input floating point lat/lon values.
203 //
204 //              0 (90 deg. or 90N)
205 //
206 // 0 (-180 deg. or 180W)      129,600,000 (180 deg. or 180E)
207 //
208 //          64,800,000 (-90 deg. or 90S)
209 //
210 // Returns 0 if error, 1 if good values were converted.
211 /////////////////////////////////////////////////////////////////////
convert_to_xastir_coordinates(unsigned long * x,unsigned long * y,float f_longitude,float f_latitude)212 int convert_to_xastir_coordinates ( unsigned long* x,
213                                     unsigned long* y,
214                                     float f_longitude,
215                                     float f_latitude )
216 {
217 
218   int ok = 1;
219 
220   *y = (unsigned long)(32400000l + (360000.0 * (-f_latitude)));
221   *x = (unsigned long)(64800000l + (360000.0 * f_longitude));
222 
223   if (f_longitude < -180.0)
224   {
225     fprintf(stderr,
226             "convert_to_xastir_coordinates:Longitude out-of-range (too low):%f\n",
227             f_longitude);
228     *x = 0;
229     ok = 0;
230   }
231 
232   if (f_longitude >  180.0)
233   {
234     fprintf(stderr,
235             "convert_to_xastir_coordinates:Longitude out-of-range (too high):%f\n",
236             f_longitude);
237     *x = 129600000l;
238     ok = 0;
239   }
240 
241   if (f_latitude <  -90.0)
242   {
243     fprintf(stderr,
244             "convert_to_xastir_coordinates:Latitude out-of-range (too low):%f\n",
245             f_latitude);
246     *y = 0;
247     ok = 0;
248   }
249 
250   if (f_latitude >   90.0)
251   {
252     fprintf(stderr,
253             "convert_to_xastir_coordinates:Latitude out-of-range (too high):%f\n",
254             f_latitude);
255     *y = 64800000l;
256     ok =0;
257   }
258 
259   return(ok);
260 }
261 
262 
263 
264 
265 
266 // Multiply all the characters in the callsign, truncated to
267 // TACTICAL_HASH_SIZE
268 //
tactical_hash_from_key(void * key)269 unsigned int tactical_hash_from_key(void *key)
270 {
271   unsigned char *jj = key;
272   unsigned int tac_hash = 1;
273 
274   while (*jj != '\0')
275   {
276     tac_hash = tac_hash * (unsigned int)*jj++;
277   }
278 
279   tac_hash = tac_hash % TACTICAL_HASH_SIZE;
280 
281 //    fprintf(stderr,"hash = %d\n", tac_hash);
282   return (tac_hash);
283 }
284 
285 
286 
287 
288 
tactical_keys_equal(void * key1,void * key2)289 int tactical_keys_equal(void *key1, void *key2)
290 {
291 
292 //fprintf(stderr,"Comparing %s to %s\n",(char *)key1,(char *)key2);
293   if (strlen((char *)key1) == strlen((char *)key2)
294       && strncmp((char *)key1,(char *)key2,strlen((char *)key1))==0)
295   {
296 //fprintf(stderr,"    match\n");
297     return(1);
298   }
299   else
300   {
301 //fprintf(stderr,"  no match\n");
302     return(0);
303   }
304 }
305 
306 
307 
308 
309 
init_tactical_hash(int clobber)310 void init_tactical_hash(int clobber)
311 {
312 //    fprintf(stderr," Initializing tactical hash \n");
313   // make sure we don't leak
314 //fprintf(stderr,"init_tactical_hash\n");
315   if (tactical_hash)
316   {
317 //fprintf(stderr,"Already have one!\n");
318     if (clobber)
319     {
320 //fprintf(stderr,"Clobbering hash table\n");
321       hashtable_destroy(tactical_hash, 1);
322       tactical_hash=create_hashtable(TACTICAL_HASH_SIZE,
323                                      tactical_hash_from_key,
324                                      tactical_keys_equal);
325     }
326   }
327   else
328   {
329 //fprintf(stderr,"Creating hash table from scratch\n");
330     tactical_hash=create_hashtable(TACTICAL_HASH_SIZE,
331                                    tactical_hash_from_key,
332                                    tactical_keys_equal);
333   }
334 }
335 
336 
337 
338 
339 
get_tactical_from_hash(char * callsign)340 char *get_tactical_from_hash(char *callsign)
341 {
342   char *result;
343 
344   if (callsign == NULL || *callsign == '\0')
345   {
346     fprintf(stderr,"Empty callsign passed to get_tactical_from_hash()\n");
347     return(NULL);
348   }
349 
350   if (!tactical_hash)    // no table to search
351   {
352 //fprintf(stderr,"Creating hash table\n");
353     init_tactical_hash(1); // so create one
354     return NULL;
355   }
356 
357 //    fprintf(stderr,"   searching for %s...",callsign);
358 
359   result=hashtable_search(tactical_hash,callsign);
360 
361   if (result)
362   {
363 //            fprintf(stderr,"\t\tFound it, %s, len=%d, %s\n",
364 //                callsign,
365 //                strlen(callsign),
366 //                result);
367   }
368   else
369   {
370 //            fprintf(stderr,"\t\tNot found, %s, len=%d\n",
371 //                callsign,
372 //                strlen(callsign));
373   }
374 
375   return (result);
376 }
377 
378 
379 
380 
381 
382 // This function checks whether there already is something in the
383 // hashtable that matches.  If a match found, it overwrites the
384 // tactical call for that entry, else it inserts a new record.
385 //
add_tactical_to_hash(char * callsign,char * tactical_call)386 void add_tactical_to_hash(char *callsign, char *tactical_call)
387 {
388   char *temp1;    // tac-call
389   char *temp2;    // callsign
390   char *ptr;
391 
392 
393   // Note that tactical_call can be '\0', which means we're
394   // getting rid of a previous tactical call.
395   //
396   if (callsign == NULL || *callsign == '\0'
397       || tactical_call == NULL)
398   {
399     return;
400   }
401 
402   if (!tactical_hash)    // no table to add to
403   {
404 //fprintf(stderr,"init_tactical_hash\n");
405     init_tactical_hash(1); // so create one
406   }
407 
408   // Remove any matching entry to avoid duplicates
409   ptr = hashtable_remove(tactical_hash, callsign);
410   if (ptr)    // If value found, free the storage space for it as
411   {
412     // the hashtable_remove function doesn't.  It does
413     // however remove the key (callsign) ok.
414     free(ptr);
415   }
416 
417   temp1 = (char *)malloc(MAX_TACTICAL_CALL+1);
418   CHECKMALLOC(temp1);
419 
420   temp2 = (char *)malloc(MAX_CALLSIGN+1);
421   CHECKMALLOC(temp2);
422 
423 //fprintf(stderr, "\t\t\tAdding %s = %s...\n", callsign, tactical_call);
424 
425   xastir_snprintf(temp2,
426                   MAX_CALLSIGN+1,
427                   "%s",
428                   callsign);
429 
430   xastir_snprintf(temp1,
431                   MAX_TACTICAL_CALL+1,
432                   "%s",
433                   tactical_call);
434 
435   //                                   (key)  (value)
436   //                         hash      call   tac-call
437   if (!hashtable_insert(tactical_hash, temp2, temp1))
438   {
439     fprintf(stderr,"Insert failed on tactical hash --- fatal\n");
440     free(temp1);
441     free(temp2);
442     exit(1);
443   }
444 
445   // A check to see whether hash insert/update worked properly
446   ptr = get_tactical_from_hash(callsign);
447   if (!ptr)
448   {
449     fprintf(stderr,"***Failed hash insert/update***\n");
450   }
451   else
452   {
453 //fprintf(stderr,"Current: %s -> %s\n",
454 //    callsign,
455 //    ptr);
456   }
457 }
458 
459 
460 
461 
462 
destroy_tactical_hash(void)463 void destroy_tactical_hash(void)
464 {
465   struct hashtable_itr *iterator = NULL;
466   char *value;
467 
468   if (tactical_hash && hashtable_count(tactical_hash) > 0)
469   {
470 
471     iterator = hashtable_iterator(tactical_hash);
472 
473     do
474     {
475       if (iterator)
476       {
477         value = hashtable_iterator_value(iterator);
478         if (value)
479         {
480           free(value);
481         }
482       }
483     }
484     while (hashtable_iterator_remove(iterator));
485 
486     // Destroy the hashtable, freeing what's left of the
487     // entries.
488     hashtable_destroy(tactical_hash, 1);
489 
490     tactical_hash = NULL;
491 
492     if (iterator)
493     {
494       free(iterator);
495     }
496   }
497 }
498 
499 
500 
501 
502 
503 // Prints string to STDERR only if "my_debug_level" bits are set in
504 // the global "debug_level" variable.  Used for getting extra debug
505 // messages during various stages of debugging.
506 //
xastir_debug(int my_debug_level,char * debug_string)507 void xastir_debug(int my_debug_level, char *debug_string)
508 {
509 
510   if (debug_level & my_debug_level)
511   {
512     fprintf(stderr, "%s", debug_string);
513   }
514 }
515 
516 
517 
518 
519 
remove_all_spaces(char * data)520 char *remove_all_spaces(char *data)
521 {
522   char *ptr;
523   int length = strlen(data);
524 
525   ptr = data;
526   while ( (ptr = strpbrk(data, " ")) )
527   {
528     memmove(ptr, ptr+1, strlen(ptr)+1);
529     length--;
530   }
531 
532   // Terminate at the new string length
533   data[length] = '\0';
534 
535   return(data);
536 }
537 
538 
539 
540 
541 
remove_leading_spaces(char * data)542 char *remove_leading_spaces(char *data)
543 {
544   int i,j;
545   int count;
546 
547   if (data == NULL)
548   {
549     return NULL;
550   }
551 
552   if (strlen(data) == 0)
553   {
554     return NULL;
555   }
556 
557   count = 0;
558   // Count the leading space characters
559   for (i = 0; i < (int)strlen(data); i++)
560   {
561     if (data[i] == ' ')
562     {
563       count++;
564     }
565     else    // Found a non-space
566     {
567       break;
568     }
569   }
570 
571   // Check whether entire string was spaces
572   if (count == (int)strlen(data))
573   {
574     // Empty the string
575     data[0] = '\0';
576   }
577   else if (count > 0)    // Found some spaces
578   {
579     i = 0;
580     for( j = count; j < (int)strlen(data); j++ )
581     {
582       data[i++] = data[j];    // Move string left
583     }
584     data[i] = '\0'; // Terminate the new string
585   }
586 
587   return(data);
588 }
589 
590 
591 
592 
593 
remove_trailing_spaces(char * data)594 char *remove_trailing_spaces(char *data)
595 {
596   int datalen;
597 
598   if (data != NULL)
599   {
600 
601     datalen=strlen(data);
602     for(datalen--; datalen>=0; datalen--)
603       if(data[datalen] == ' ')
604       {
605         data[datalen] = '\0';
606       }
607       else
608       {
609         break;
610       }
611   }
612 
613   // May end up with nothing left.
614   return(data);
615 }
616 
617 
618 
619 
620 
remove_trailing_asterisk(char * data)621 char *remove_trailing_asterisk(char *data)
622 {
623   int datalen;
624 
625   if (data != NULL)
626   {
627 
628     datalen=strlen(data);
629     for(datalen--; datalen>0; datalen--)
630     {
631       if(data[datalen] == '*')
632       {
633         data[datalen] = '\0';
634       }
635       else
636       {
637         break;
638       }
639     }
640   }
641   // May end up with nothing left.
642   return(data);
643 }
644 
645 
646 
647 
648 
649 // Removes trailing "-0" from string.
650 //
651 // Modifies "data" variable.
652 //
remove_trailing_dash_zero(char * data)653 char *remove_trailing_dash_zero(char *data)
654 {
655   char *ptr;
656   char *ptr2;
657   int len = strlen(data);
658 
659 
660   // String too short?
661   if (len < 2)
662   {
663     return(data);
664   }
665 
666   ptr2 = data + len - 1; // Point to last char
667   ptr = ptr2 - 1; // Point to next-to-last char
668 
669   // Check for "-0" at end.  Remove if found.
670   if (*ptr == '-' && *ptr2 == '0')
671   {
672     *ptr = '\0';    // Terminate
673   }
674 
675   return(data);
676 }
677 
678 
679 
680 
681 
682 // Save the current time, used for timing code sections.
start_timer(void)683 void start_timer(void)
684 {
685   gettimeofday(&timer_start,&tz);
686 }
687 
688 
689 
690 
691 
692 // Save the current time, used for timing code sections.
stop_timer(void)693 void stop_timer(void)
694 {
695   gettimeofday(&timer_stop,&tz);
696 }
697 
698 
699 
700 
701 
702 // Print the difference in the two times saved above.
print_timer_results(void)703 void print_timer_results(void)
704 {
705   fprintf(stderr,"Total: %f sec\n",
706           (float)(timer_stop.tv_sec - timer_start.tv_sec +
707                   ((timer_stop.tv_usec - timer_start.tv_usec) / 1000000.0) ));
708 }
709 
710 
711 
712 
713 
714 //
715 // Inserts localtime date/time in "timestring".  Timestring
716 // Should be at least 101 characters long.
717 //
get_timestamp(char * timestring)718 void get_timestamp(char *timestring)
719 {
720   struct tm *time_now;
721   time_t secs_now;
722 
723 
724   secs_now=sec_now();
725   time_now = localtime(&secs_now);
726 
727 // %e is not implemented on all systems, but %d should be
728 //    (void)strftime(timestring,100,"%a %b %e %H:%M:%S %Z %Y",time_now);
729   (void)strftime(timestring,100,"%a %b %d %H:%M:%S %Z %Y",time_now);
730 }
731 
732 
733 
734 
735 
736 /* function get_iso_datetime converts time in seconds to an ISO date
737  * time in the form yyyy-mm-dd hh:mm:ss utc_offset
738  * @param aTime time in seconds since the begining of the unix epoch
739  * @param timestring pointer to a char[101] into which the timestamp
740  * is written in the format yyyy-mm-dd hh:mm:ss followed by a utc
741  * offset for the timezone.
742  * @param nowIfNotSet when true, if aTime is not set (int)aTime==0,
743  * then return the current time rather than formatting aTime, when
744  * false, returns formatted aTime even if it was zero.
745  * @param nowIfInvalid when true, if aTime is invalid (int)aTime=-1,
746  * then returns the formatted current time rather than formatting
747  * aTime, otherwise returns formatted invalid time.
748  * @returns 0 on when (int)aTime==-1 where time provided invalid
749  * returns 1 otherwise. */
get_iso_datetime(time_t aTime,char * timestring,int nowIfNotSet,int nowIfInvalid)750 int get_iso_datetime(time_t aTime, char *timestring,int nowIfNotSet, int nowIfInvalid)
751 {
752   struct tm *time_now;
753   time_t secs_now;
754   int returnvalue = 1;
755   if (((int)aTime==0 && nowIfNotSet) || ((int)aTime==-1 && nowIfInvalid))
756   {
757     secs_now=sec_now();
758     time_now = localtime(&secs_now);
759     (void)strftime(timestring,100,"%F %H:%M:%S %z",time_now);
760   }
761   else
762   {
763     // will also end up here if time_t is -1
764     (void)strftime(timestring,100,"%F %H:%M:%S %z",localtime(&aTime));
765   }
766   if ((int)aTime==-1)
767   {
768     returnvalue = 0;
769   }
770   return returnvalue;
771 }
772 
773 
774 
775 
776 
777 
778 /* function get_W3CDTF_datetime converts time in seconds to a W3CDTF date
779  * time in the form yyyy-mm-ddThh:mm:ss-utc_offsethh:mm
780  * See: http://www.w3.org/TR/NOTE-datetime
781  * This is effectively an ISO date, with a T between the date and the
782  * time, no space between the time and the utc offset, and a colon
783  * separating utc offset hours from utc offset seconds.
784  * Example:  2008-01-25T08:35:20-05:00
785  * @param aTime time in seconds since the begining of the unix epoch
786  * @param timestring pointer to a char[101] into which the timestamp
787  * is written in the format yyyy-mm-ddThh:mm:ss followed by a utc
788  * offset for the timezone, (e.g. -05:00).
789  * @param nowIfNotSet when true, if aTime is not set (int)aTime==0,
790  * then return the current time rather than formatting aTime, when
791  * false, returns formatted aTime even if it was zero.
792  * @param nowIfInvalid when true, if aTime is invalid (int)aTime=-1,
793  * then returns the formatted current time rather than formatting
794  * aTime, otherwise returns formatted invalid time.
795  * @returns 0 on when (int)aTime==-1 where time provided invalid
796  * returns 1 otherwise. */
get_w3cdtf_datetime(time_t aTime,char * timestring,int nowIfNotSet,int nowIfInvalid)797 int get_w3cdtf_datetime(time_t aTime, char *timestring,int nowIfNotSet, int nowIfInvalid)
798 {
799   struct tm *time_now;
800   time_t secs_now;
801   int returnvalue = 1;
802   if (((int)aTime==0 && nowIfNotSet) || ((int)aTime==-1 && nowIfInvalid))
803   {
804     secs_now=sec_now();
805     time_now = localtime(&secs_now);
806     (void)strftime(timestring,100,"%FT%H:%M:%S %z",time_now);
807     timestring[19] = timestring[20];
808     timestring[20] = timestring[21];
809     timestring[21] = timestring[22];
810     timestring[22] = ':';
811   }
812   else
813   {
814     // will also end up here if time_t is -1
815     (void)strftime(timestring,100,"%FT%H:%M:%S %z",localtime(&aTime));
816     timestring[19] = timestring[20];
817     timestring[20] = timestring[21];
818     timestring[21] = timestring[22];
819     timestring[22] = ':';
820   }
821   if ((int)aTime==-1)
822   {
823     returnvalue = 0;
824   }
825   return returnvalue;
826 }
827 
828 
829 
830 
831 /***********************************************************/
832 /* returns the hour (00..23), localtime                    */
833 /***********************************************************/
get_hours(void)834 int get_hours(void)
835 {
836   struct tm *time_now;
837   time_t secs_now;
838   char shour[5];
839 
840   secs_now=sec_now();
841   time_now = localtime(&secs_now);
842   (void)strftime(shour,4,"%H",time_now);
843   return(atoi(shour));
844 }
845 
846 
847 
848 
849 
850 /***********************************************************/
851 /* returns the minute (00..59), localtime                  */
852 /***********************************************************/
get_minutes(void)853 int get_minutes(void)
854 {
855   struct tm *time_now;
856   time_t secs_now;
857   char sminute[5];
858 
859   secs_now=sec_now();
860   time_now = localtime(&secs_now);
861   (void)strftime(sminute,4,"%M",time_now);
862   return(atoi(sminute));
863 }
864 
865 
866 
867 
868 
869 /***********************************************************/
870 /* returns the second (00..61), localtime                  */
871 /***********************************************************/
get_seconds(void)872 int get_seconds(void)
873 {
874   struct tm *time_now;
875   time_t secs_now;
876   char sminute[5];
877 
878   secs_now=sec_now();
879   time_now = localtime(&secs_now);
880   (void)strftime(sminute,4,"%S",time_now);
881   return(atoi(sminute));
882 }
883 
884 
885 
886 
887 
888 
889 /*************************************************************************/
890 /* output_lat - format position with position_amb_chars for transmission */
891 /*************************************************************************/
output_lat(char * in_lat,int comp_pos)892 char *output_lat(char *in_lat, int comp_pos)
893 {
894   int i,j;
895 
896 //fprintf(stderr,"in_lat:%s\n", in_lat);
897 
898   if (!comp_pos)
899   {
900     // Don't do this as it results in truncation!
901     //in_lat[7]=in_lat[8]; // Shift N/S down for transmission
902   }
903   else if (position_amb_chars>0)
904   {
905     in_lat[7]='0';
906   }
907 
908   j=0;
909   if (position_amb_chars>0 && position_amb_chars<5)
910   {
911     for (i=6; i>(6-position_amb_chars-j); i--)
912     {
913       if (i==4)
914       {
915         i--;
916         j=1;
917       }
918       if (!comp_pos)
919       {
920         in_lat[i]=' ';
921       }
922       else
923       {
924         in_lat[i]='0';
925       }
926     }
927   }
928 
929   if (!comp_pos)
930   {
931     in_lat[8] = '\0';
932   }
933 
934   return(in_lat);
935 }
936 
937 
938 
939 
940 
941 /**************************************************************************/
942 /* output_long - format position with position_amb_chars for transmission */
943 /**************************************************************************/
output_long(char * in_long,int comp_pos)944 char *output_long(char *in_long, int comp_pos)
945 {
946   int i,j;
947 
948 //fprintf(stderr,"in_long:%s\n", in_long);
949 
950   if (!comp_pos)
951   {
952     // Don't do this as it results in truncation!
953     //in_long[8]=in_long[9]; // Shift e/w down for transmission
954   }
955   else if (position_amb_chars>0)
956   {
957     in_long[8]='0';
958   }
959 
960   j=0;
961   if (position_amb_chars>0 && position_amb_chars<5)
962   {
963     for (i=7; i>(7-position_amb_chars-j); i--)
964     {
965       if (i==5)
966       {
967         i--;
968         j=1;
969       }
970       if (!comp_pos)
971       {
972         in_long[i]=' ';
973       }
974       else
975       {
976         in_long[i]='0';
977       }
978     }
979   }
980 
981   if (!comp_pos)
982   {
983     in_long[9] = '\0';
984   }
985 
986   return(in_long);
987 }
988 
989 
990 
991 
992 
993 /*********************************************************************/
994 /* PHG range calculation                                             */
995 /* NOTE: Keep these calculations consistent with phg_decode!         */
996 /*       Yes, there is a reason why they both exist.                 */
997 /*********************************************************************/
phg_range(char p,char h,char g)998 double phg_range(char p, char h, char g)
999 {
1000   double power, height, gain, range;
1001 
1002   if ( (p < '0') || (p > '9') )   // Power is outside limits
1003   {
1004     power = 0.0;
1005   }
1006   else
1007   {
1008     power = (double)( (p-'0')*(p-'0') );  // lclint said: "Assignment of char to double" here
1009   }
1010 
1011   if (h < '0')   // Height is outside limits (there is no upper limit according to the spec)
1012   {
1013     height = 10.0;
1014   }
1015   else
1016   {
1017     height = 10.0 * pow(2.0, (double)(h-'0'));
1018   }
1019 
1020   if ( (g < '0') || (g > '9') )   // Gain is outside limits
1021   {
1022     gain = 1.0;
1023   }
1024   else
1025   {
1026     gain = pow(10.0, (double)(g-'0') / 10.0);
1027   }
1028 
1029   range = sqrt(2.0 * height * sqrt((power / 10.0) * (gain / 2.0)));
1030 
1031 //    if (range > 70.0)
1032 //        fprintf(stderr,"PHG%c%c%c results in range of %f\n", p, h, g, range);
1033 
1034   // Note:  Bob Bruninga, WB4APR, decided to cut PHG circles by
1035   // 1/2 in order to show more realistic mobile ranges.
1036   range = range / 2.0;
1037 
1038   return(range);
1039 }
1040 
1041 
1042 
1043 
1044 
1045 /*********************************************************************/
1046 /* shg range calculation (for DF'ing)                                */
1047 /*                                                                   */
1048 /*********************************************************************/
shg_range(char s,char h,char g)1049 double shg_range(char s, char h, char g)
1050 {
1051   double power, height, gain, range;
1052 
1053   if ( (s < '0') || (s > '9') )   // Power is outside limits
1054   {
1055     s = '0';
1056   }
1057 
1058   if (s == '0')               // No signal strength
1059   {
1060     power = 10.0 / 0.8;  // Preventing divide by zero (same as DOSaprs does it)
1061   }
1062   else
1063   {
1064     power = 10 / (s - '0');  // Makes circle smaller with higher signal strengths
1065   }
1066 
1067   if (h < '0')   // Height is outside limits (there is no upper limit according to the spec)
1068   {
1069     height = 10.0;
1070   }
1071   else
1072   {
1073     height = 10.0 * pow(2.0, (double)(h-'0'));
1074   }
1075 
1076   if ( (g < '0') || (g > '9') )   // Gain is outside limits
1077   {
1078     gain = 1.0;
1079   }
1080   else
1081   {
1082     gain = pow(10.0, (double)(g-'0') / 10.0);
1083   }
1084 
1085   range = sqrt(2.0 * height * sqrt((power / 10.0) * (gain / 2.0)));
1086 
1087   range = (range * 40) / 51;   // Present fudge factors used in DOSaprs
1088 
1089   //fprintf(stderr,"SHG%c%c%c results in range of %f\n", s, h, g, range);
1090 
1091   return(range);
1092 }
1093 
1094 
1095 
1096 
1097 
1098 /*********************************************************************/
1099 /* PHG decode                                                        */
1100 /* NOTE: Keep these calculations consistent with phg_range!          */
1101 /*       Yes, there is a reason why they both exist.                 */
1102 /*********************************************************************/
phg_decode(const char * langstr,const char * phg,char * phg_decoded,int phg_decoded_length)1103 void phg_decode(const char *langstr, const char *phg, char *phg_decoded, int phg_decoded_length)
1104 {
1105   double power, height, gain, range;
1106   char directivity[6], temp[64];
1107   int  gain_db;
1108 
1109 
1110   if (strlen(phg) != 7)
1111   {
1112     xastir_snprintf(phg_decoded,
1113                     phg_decoded_length,
1114                     "%s %s",
1115                     langstr,
1116                     langcode("WPUPSTI073") ); // "BAD PHG"
1117     return;
1118   }
1119 
1120   if ( (phg[3] < '0') || (phg[3] > '9') )   // Power is outside limits
1121   {
1122     power = 0.0;
1123   }
1124   else
1125   {
1126     power = (double)( (phg[3]-'0')*(phg[3]-'0') );
1127   }
1128 
1129   if (phg[4] < '0')   // Height is outside limits (there is no upper limit according to the spec)
1130   {
1131     height = 10.0;
1132   }
1133   else
1134   {
1135     height = 10.0 * pow(2.0, (double)(phg[4]-'0'));
1136   }
1137 
1138   if ( (phg[5] < '0') || (phg[5] > '9') )   // Gain is outside limits
1139   {
1140     gain = 1.0;
1141     gain_db = 0;
1142   }
1143   else
1144   {
1145     gain = pow(10.0, (double)(phg[5]-'0') / 10.0);
1146     gain_db = phg[5]-'0';
1147   }
1148 
1149   range = sqrt(2.0 * height * sqrt((power / 10.0) * (gain / 2.0)));
1150   // Note:  Bob Bruninga, WB4APR, decided to cut PHG circles by
1151   // 1/2 in order to show more realistic mobile ranges.
1152   range = range / 2.0;
1153 
1154   switch (phg[6])
1155   {
1156     case '0':
1157       xastir_snprintf(directivity,
1158                       sizeof(directivity),
1159                       "%s",
1160                       langcode("WPUPSTI071") );   // "omni"
1161       break;
1162     case '1':
1163       xastir_snprintf(directivity,
1164                       sizeof(directivity),
1165                       "%s",
1166                       " NE");
1167       break;
1168     case '2':
1169       xastir_snprintf(directivity,
1170                       sizeof(directivity),
1171                       "%s",
1172                       " E");
1173       break;
1174     case '3':
1175       xastir_snprintf(directivity,
1176                       sizeof(directivity),
1177                       "%s",
1178                       " SE");
1179       break;
1180     case '4':
1181       xastir_snprintf(directivity,
1182                       sizeof(directivity),
1183                       "%s",
1184                       " S");
1185       break;
1186     case '5':
1187       xastir_snprintf(directivity,
1188                       sizeof(directivity),
1189                       "%s",
1190                       " SW");
1191       break;
1192     case '6':
1193       xastir_snprintf(directivity,
1194                       sizeof(directivity),
1195                       "%s",
1196                       " W");
1197       break;
1198     case '7':
1199       xastir_snprintf(directivity,
1200                       sizeof(directivity),
1201                       "%s",
1202                       " NW");
1203       break;
1204     case '8':
1205       xastir_snprintf(directivity,
1206                       sizeof(directivity),
1207                       "%s",
1208                       " N");
1209       break;
1210     default:
1211       directivity[0] = '\0';
1212       break;
1213   }
1214 
1215   if (english_units)
1216     xastir_snprintf(temp,
1217                     sizeof(temp),
1218                     "%.0fW @ %.0fft %s, %ddB%s, %s %.1fmi",
1219                     power,
1220                     height,
1221                     langcode("WPUPSTI070"), // HAAT
1222                     gain_db,
1223                     directivity,
1224                     langcode("WPUPSTI072"), // range
1225                     range);
1226   else
1227     xastir_snprintf(temp,
1228                     sizeof(temp),
1229                     "%.0fW @ %.1fm %s, %ddB%s, %s %.1fkm",
1230                     power,
1231                     height*0.3048,
1232                     langcode("WPUPSTI070"), // HAAT
1233                     gain_db,
1234                     directivity,
1235                     langcode("WPUPSTI072"), // range
1236                     range*1.609344);
1237 
1238   xastir_snprintf(phg_decoded,
1239                   phg_decoded_length,
1240                   "%s %s",
1241                   langstr,
1242                   temp);
1243 }
1244 
1245 
1246 
1247 
1248 
1249 /*********************************************************************/
1250 /* SHG decode                                                        */
1251 /*                                                                   */
1252 /*********************************************************************/
shg_decode(const char * langstr,const char * shg,char * shg_decoded,int shg_decoded_length)1253 void shg_decode(const char *langstr, const char *shg, char *shg_decoded, int shg_decoded_length)
1254 {
1255   double power, height, gain, range;
1256   char directivity[6], temp[100], signal[64];
1257   int  gain_db;
1258   char s;
1259 
1260   if (strlen(shg) != 7)
1261   {
1262     xastir_snprintf(shg_decoded,
1263                     shg_decoded_length,
1264                     langstr,
1265                     langcode("WPUPSTI074") );   // "BAD SHG"
1266     return;
1267   }
1268 
1269   s = shg[3];
1270 
1271   if ( (s < '0') || (s > '9') )   // Signal is outside limits
1272   {
1273     s = '0';  // force to lowest legal value
1274   }
1275 
1276   switch (s)
1277   {
1278     case '0':
1279       xastir_snprintf(signal,
1280                       sizeof(signal),
1281                       "%s",
1282                       langcode("WPUPSTI076") );
1283       // "No signal detected"
1284       break;
1285     case '1':
1286       xastir_snprintf(signal,
1287                       sizeof(signal),
1288                       "%s",
1289                       langcode("WPUPSTI077") );
1290       // "Detectible signal (Maybe)"
1291       break;
1292     case '2':
1293       xastir_snprintf(signal,
1294                       sizeof(signal),
1295                       "%s",
1296                       langcode("WPUPSTI078") );
1297       // "Detectible signal but not copyable)"
1298       break;
1299     case '3':
1300       xastir_snprintf(signal,
1301                       sizeof(signal),
1302                       "%s",
1303                       langcode("WPUPSTI079") );
1304       // "Weak signal, marginally readable"
1305       break;
1306     case '4':
1307       xastir_snprintf(signal,
1308                       sizeof(signal),
1309                       "%s",
1310                       langcode("WPUPSTI080") );
1311       // "Noisy but copyable signal"
1312       break;
1313     case '5':
1314       xastir_snprintf(signal,
1315                       sizeof(signal),
1316                       "%s",
1317                       langcode("WPUPSTI081") );
1318       // "Some noise, easy to copy signal"
1319       break;
1320     case '6':
1321       xastir_snprintf(signal,
1322                       sizeof(signal),
1323                       "%s",
1324                       langcode("WPUPSTI082") );
1325       // "Good signal w/detectible noise"
1326       break;
1327     case '7':
1328       xastir_snprintf(signal,
1329                       sizeof(signal),
1330                       "%s",
1331                       langcode("WPUPSTI083") );
1332       // "Near full-quieting signal"
1333       break;
1334     case '8':
1335       xastir_snprintf(signal,
1336                       sizeof(signal),
1337                       "%s",
1338                       langcode("WPUPSTI084") );
1339       // "Full-quieting signal"
1340       break;
1341     case '9':
1342       xastir_snprintf(signal,
1343                       sizeof(signal),
1344                       "%s",
1345                       langcode("WPUPSTI085") );
1346       // "Extremely strong & full-quieting signal"
1347       break;
1348     default:
1349       signal[0] = '\0';
1350       break;
1351   }
1352 
1353   if (s == '0')
1354   {
1355     power = (double)( 10 / 0.8 );  // Preventing divide by zero (same as DOSaprs does it)
1356   }
1357   else
1358   {
1359     power = (double)( 10 / (s - '0') );
1360   }
1361 
1362   if (shg[4] < '0')   // Height is outside limits (there is no upper limit according to the spec)
1363   {
1364     height = 10.0;
1365   }
1366   else
1367   {
1368     height = 10.0 * pow(2.0, (double)(shg[4]-'0'));
1369   }
1370 
1371   if ( (shg[5] < '0') || (shg[5] > '9') )   // Gain is outside limits
1372   {
1373     gain = 1.0;
1374     gain_db = 0;
1375   }
1376   else
1377   {
1378     gain = pow(10.0, (double)(shg[5]-'0') / 10.0);
1379     gain_db = shg[5]-'0';
1380   }
1381 
1382   range = sqrt(2.0 * height * sqrt((power / 10.0) * (gain / 2.0)));
1383 
1384   range = range * 0.85;   // Present fudge factor (used by DOSaprs)
1385 
1386   switch (shg[6])
1387   {
1388     case '0':
1389       xastir_snprintf(directivity,
1390                       sizeof(directivity),
1391                       "%s ",
1392                       langcode("WPUPSTI071") );   // "omni"
1393       break;
1394     case '1':
1395       xastir_snprintf(directivity,
1396                       sizeof(directivity),
1397                       "%s",
1398                       " NE");
1399       break;
1400     case '2':
1401       xastir_snprintf(directivity,
1402                       sizeof(directivity),
1403                       "%s",
1404                       " E");
1405       break;
1406     case '3':
1407       xastir_snprintf(directivity,
1408                       sizeof(directivity),
1409                       "%s",
1410                       " SE");
1411       break;
1412     case '4':
1413       xastir_snprintf(directivity,
1414                       sizeof(directivity),
1415                       "%s",
1416                       " S");
1417       break;
1418     case '5':
1419       xastir_snprintf(directivity,
1420                       sizeof(directivity),
1421                       "%s",
1422                       " SW");
1423       break;
1424     case '6':
1425       xastir_snprintf(directivity,
1426                       sizeof(directivity),
1427                       "%s",
1428                       " W");
1429       break;
1430     case '7':
1431       xastir_snprintf(directivity,
1432                       sizeof(directivity),
1433                       "%s",
1434                       " NW");
1435       break;
1436     case '8':
1437       xastir_snprintf(directivity,
1438                       sizeof(directivity),
1439                       "%s",
1440                       " N");
1441       break;
1442     default:
1443       directivity[0] = '\0';
1444       break;
1445   }
1446 
1447   if (english_units)
1448   {
1449     xastir_snprintf(temp,
1450                     sizeof(temp),
1451                     "%.0fft %s, %ddB%s, %s: %.1fmi, %s",
1452                     height,
1453                     langcode("WPUPSTI070"), // "HAAT"
1454                     gain_db,
1455                     directivity,
1456                     langcode("WPUPSTI075"), // "DF Range"
1457                     range,
1458                     signal);
1459   }
1460   else
1461   {
1462     xastir_snprintf(temp,
1463                     sizeof(temp),
1464                     "%.1fm %s, %ddB%s, %s: %.1fkm, %s",
1465                     height*0.3048,
1466                     langcode("WPUPSTI070"), // "HAAT"
1467                     gain_db,
1468                     directivity,
1469                     langcode("WPUPSTI075"), // "DF Range"
1470                     range*1.609344,
1471                     signal);
1472   }
1473 
1474   xastir_snprintf(shg_decoded,
1475                   shg_decoded_length,
1476                   langstr,
1477                   temp);
1478 }
1479 
1480 
1481 
1482 
1483 
1484 /*********************************************************************/
1485 /* Bearing decode                                                    */
1486 /*                                                                   */
1487 /*********************************************************************/
bearing_decode(const char * langstr,const char * bearing_str,const char * NRQ,char * bearing_decoded,int bearing_decoded_length)1488 void bearing_decode(const char *langstr, const char *bearing_str,
1489                     const char *NRQ, char *bearing_decoded, int bearing_decoded_length)
1490 {
1491   int bearing, range, width;
1492   char N,R,Q;
1493   char temp[64];
1494 
1495 
1496 //fprintf(stderr,"bearing_decode incoming: bearing is %s, NRQ is %s\n", bearing_str, NRQ);
1497 
1498   if (strlen(bearing_str) != 3)
1499   {
1500     xastir_snprintf(bearing_decoded,
1501                     bearing_decoded_length,
1502                     langstr,
1503                     langcode("WPUPSTI086") );   // "BAD BEARING"
1504     return;
1505   }
1506 
1507   if (strlen(NRQ) != 3)
1508   {
1509     xastir_snprintf(bearing_decoded,
1510                     bearing_decoded_length,
1511                     langstr,
1512                     langcode("WPUPSTI087") );   // "BAD NRQ"
1513     return;
1514   }
1515 
1516   bearing = atoi(bearing_str);
1517   N = NRQ[0];
1518   R = NRQ[1];
1519   Q = NRQ[2];
1520 
1521   range = 0;
1522   width = 0;
1523 
1524   if (N != 0)
1525   {
1526 
1527 //fprintf(stderr,"N != 0\n");
1528 
1529     range = pow(2.0,R - '0');
1530 
1531     switch (Q)
1532     {
1533       case('1'):
1534         width = 240;    // Degrees of beam width.  What's the point?
1535         break;
1536       case('2'):
1537         width = 120;    // Degrees of beam width.  What's the point?
1538         break;
1539       case('3'):
1540         width = 64; // Degrees of beam width.  What's the point?
1541         break;
1542       case('4'):
1543         width = 32; // Degrees of beam width.  Starting to be usable.
1544         break;
1545       case('5'):
1546         width = 16; // Degrees of beam width.  Usable.
1547         break;
1548       case('6'):
1549         width = 8;  // Degrees of beam width.  Usable.
1550         break;
1551       case('7'):
1552         width = 4;  // Degrees of beam width.  Nice!
1553         break;
1554       case('8'):
1555         width = 2;  // Degrees of beam width.  Nice!
1556         break;
1557       case('9'):
1558         width = 1;  // Degrees of beam width.  Very Nice!
1559         break;
1560       default:
1561         width = 8;  // Degrees of beam width
1562         break;
1563     }
1564 
1565 //fprintf(stderr,"Width = %d\n",width);
1566 
1567     if (english_units)
1568     {
1569       xastir_snprintf(temp,
1570                       sizeof(temp),
1571                       "%i%c, %s: %i%c, %s: %i mi",
1572                       bearing,
1573                       0xb0,                   // Degree symbol
1574                       langcode("WPUPSTI088"), // DF Beamwidth
1575                       width,
1576                       0xb0,                   // Degree symbol
1577                       langcode("WPUPSTI089"), // DF Length
1578                       range);
1579     }
1580     else
1581     {
1582       xastir_snprintf(temp,
1583                       sizeof(temp),
1584                       "%i%c, %s: %i%c, %s: %0.2f km",
1585                       bearing,
1586                       0xb0,                   // Degree symbol
1587                       langcode("WPUPSTI088"), // DF Beamwidth
1588                       width,
1589                       0xb0,                   // Degree symbol
1590                       langcode("WPUPSTI089"), // DF Length
1591                       range*1.609344);
1592     }
1593   }
1594   else
1595   {
1596     xastir_snprintf(temp,
1597                     sizeof(temp),
1598                     "%s",
1599                     langcode("WPUPSTI090") );   // "Not Valid"
1600 
1601 //fprintf(stderr,"N was 0\n");
1602   }
1603   xastir_snprintf(bearing_decoded,
1604                   bearing_decoded_length,
1605                   langstr,
1606                   temp);
1607 
1608 //fprintf(stderr,"bearing_decoded:%s\n", bearing_decoded);
1609 //fprintf(stderr,"temp:%s\n", temp);
1610 }
1611 
1612 
1613 
1614 
1615 
1616 //********************************************************************/
1617 // get_line - read a line from a file                                */
1618 //
1619 // NOTE: This routine will not work for binary files.  It works only
1620 // for ASCII-format files, and terminates each line at the first
1621 // control-character found (unless it's a tab).  Converts tab
1622 // character to 1 space character.
1623 //
1624 //********************************************************************/
1625 /*
1626 char *get_line(FILE *f, char *linedata, int maxline) {
1627     char temp_line[32768];
1628     int i;
1629 
1630     // Snag one string from the file.  We'll end up with a
1631     // terminating zero at temp_line[32767] in any case, because the
1632     // max quantity we'll get here will be 32767 with a terminating
1633     // zero added after whatever quantity is read.
1634     (void)fgets(temp_line, 32768, f);
1635 
1636     // A newline may have been added by the above fgets call.
1637     // Change any newlines or other control characters to line-end
1638     // characters.
1639     for (i = 0; i < strlen(temp_line); i++) {
1640         // Change any control characters to '\0';
1641         if (temp_line[i] < 0x20) {
1642             if (temp_line[i] == '\t') { // Found a tab char
1643                 temp_line[i] = ' '; // Convert to a space char
1644             }
1645             else {  // Not a tab
1646                 if (temp_line[i] != '\n') {                         // LF
1647                     if ( (i != (strlen(temp_line) - 2) )            // CRLF
1648                             && (i != (strlen(temp_line) - 1) ) ) {  // CR
1649                         fprintf(stderr,"get_line: found control chars in: %s\n",
1650                             temp_line);
1651                     }
1652                 }
1653                 temp_line[i] = '\0';    // Terminate the string
1654             }
1655         }
1656     }
1657 
1658     xastir_snprintf(linedata,
1659         maxline,
1660         "%s",
1661         temp_line);
1662 
1663     return(linedata);
1664 }
1665 */
get_line(FILE * f,char * linedata,int maxline)1666 char *get_line(FILE *f, char *linedata, int maxline)
1667 {
1668   int length;
1669 
1670   // Write terminating chars throughout variable
1671   memset(linedata,0,maxline);
1672 
1673   // Get the data
1674   if (fgets(linedata, maxline, f) == 0)
1675   {
1676     return "\0";  // Couldn't read from file: Return empty string
1677   }
1678 
1679   // Change CR/LF to '\0'
1680   length = strlen(linedata);
1681 
1682   // Check the last two characters
1683   if (length > 0)
1684   {
1685     if ( (linedata[length - 1] == '\n')
1686          || (linedata[length - 1] == '\r') )
1687     {
1688       linedata[length - 1] = '\0';
1689     }
1690   }
1691 
1692   if (length > 1)
1693   {
1694     if ( (linedata[length - 2] == '\n')
1695          || (linedata[length - 2] == '\r') )
1696     {
1697       linedata[length - 2] = '\0';
1698     }
1699   }
1700 
1701   return(linedata);
1702 }
1703 
1704 
1705 
1706 
1707 
1708 // time_from_aprsstring()
1709 //
1710 // Called from alert.c:alert_build_list() only.  Converts to
1711 // localtime if it's a zulu time string we're parsing.
1712 //
time_from_aprsstring(char * aprs_time)1713 time_t time_from_aprsstring(char *aprs_time)
1714 {
1715   int day, hour, minute;
1716   char tz[20];
1717   struct tm *time_now, alert_time;
1718   time_t timenw;
1719   long zone;
1720 
1721 #ifndef HAVE_TM_GMTOFF
1722 #ifdef __CYGWIN__
1723   // Use "_timezone" instead of timezone in Cygwin
1724 #define timezone _timezone
1725 #else // __CYGWIN__
1726   extern time_t timezone;
1727 #endif  // __CYGWIN__
1728 #endif  // HAVE_TM_GMTOFF
1729 
1730 
1731 #ifdef __CYGWIN__
1732   // Must call tzset before using the _timezone variable in
1733   // Cygwin, else the timezone won't have been initialized.
1734   tzset();
1735 #endif  // __CYGWIN__
1736 
1737   // Compute our current time and the offset from GMT.  If
1738   // daylight savings time is in effect, factor that in as well.
1739   (void)time(&timenw);
1740   time_now = localtime(&timenw);
1741 #ifdef HAVE_TM_GMTOFF
1742   // tm_gmtoff is the GMT offset in seconds.  Some Unix systems
1743   // have this extra field in the tm struct, some don't.
1744   // tm_gmtoff is seconds EAST of UTC.
1745   zone = time_now->tm_gmtoff;
1746   //fprintf(stderr,"gmtoff: %ld, tm_isdst: %d\n",
1747   //time_now->tm_gmtoff,
1748   //time_now->tm_isdst);
1749 #else   // HAVE_TM_GMTOFF
1750   // Note:  timezone is seconds WEST of UTC.  Need to negate
1751   // timezone to have the offset occur in the correct direction.
1752   zone = -((int)timezone - 3600 * (int)(time_now->tm_isdst > 0));
1753   //fprintf(stderr,"timezone: %d, tm_isdst: %d\n",
1754   //timezone,
1755   //time_now->tm_isdst);
1756 #endif  // HAVE_TM_GMTOFF
1757   // zone should now be the number to subtract in order to get
1758   // localtime, in seconds.  For PST, I get -28800 which equals -8
1759   // hours.  Summertime I should get -25200, or -7 hours.
1760   //fprintf(stderr,"Zone: %ld\n",zone);
1761 
1762 
1763   // Split the input time string into its component parts.
1764   tz[0] = tz[1] = '\0';
1765   switch (sscanf(aprs_time, "%2d%2d%2d%19s", &day, &hour, &minute, tz))
1766   {
1767     case 0:
1768       day = 0;
1769     /* Falls through. */
1770 
1771     case 1:
1772       hour = 0;
1773     /* Falls through. */
1774 
1775     case 2:
1776       minute = 0;
1777     /* Falls through. */
1778 
1779     default:
1780       break;
1781   }
1782   if (day > 31)
1783   {
1784     day = 31; // Wierd, can't have too many days in the month!
1785     hour = 23;
1786     minute = 59;
1787   }
1788 
1789   // We set up our alert_time so that it points into the same
1790   // struct as time_now.  We do this both so that we can get
1791   // automatically filled in pieces of the struct (year, etc), and
1792   // so that we have a more global struct to return the time in.
1793   // We'll have to adjust a few things like month/year if the time
1794   // is too far distant from our current time and crosses some
1795   // boundary.
1796   alert_time = *time_now;
1797   alert_time.tm_sec = 0;
1798 
1799   //fprintf(stderr,"alert_time: %d %d %d\n",
1800   //    alert_time.tm_mday,
1801   //    alert_time.tm_hour,
1802   //    alert_time.tm_min);
1803 
1804   // Check to see how many parameters we parsed, and do our
1805   // computations accordingly.
1806   if (day)    // If we parsed out the day
1807   {
1808     // Check whether our new day is more than ten days +/- from
1809     // the current day of the month.  If so, assume it was from
1810     // the month previous or after.
1811     if (day < (time_now->tm_mday - 10))
1812     {
1813       // Day of month went down drastically.  Must be a date
1814       // in the next month.  Bump up by one month and check
1815       // whether we overflowed into the next year.  Note that
1816       // month ranges from 0 to 11.
1817       if (++alert_time.tm_mon > 11)
1818       {
1819         alert_time.tm_mon = 0;
1820         alert_time.tm_year++;
1821       }
1822     }
1823     else if (day > (time_now->tm_mday + 10))
1824     {
1825       // Day of month went up drastically.  Must be a date
1826       // during last month.  Decrement by one month and check
1827       // whether we need to also decrement the year.
1828       if (--alert_time.tm_mon < 0)
1829       {
1830         alert_time.tm_mon = 11;
1831         alert_time.tm_year--;
1832       }
1833     }
1834 
1835     // Fill in the struct with our new values that we parsed.
1836     alert_time.tm_mday = day;
1837     alert_time.tm_min = minute;
1838     alert_time.tm_hour = hour;
1839 
1840     // Need to do conversions from zulu time?
1841     if ((char)tolower((int)tz[0]) == 'z')
1842     {
1843       // Yep, do the conversions.  Note that the zone variable
1844       // already has the sign set correctly to get the correct
1845       // time by using addition (PDT zone = -28800).
1846 
1847       // Initialize daylight savings time to 0 in this
1848       // instance, 'cuz we're starting with Zulu time and we
1849       // want the localtime conversion to change it correctly.
1850       // Zulu time has no daylight savings time offset.
1851       //
1852 // WE7U:
1853 // No, it gave us an offset of 6 hours from UTC when we set this to
1854 // zero, 7 hours (correct) when we set it to one, during the summer.
1855 // Hopefully it will give us an 8-hour offset during the wintertime
1856 // but that remains to be seen...
1857 //
1858 // FYI:  We're in daylight savings time during the summer, when
1859 // we're at a 7-hour offset.  Winter is actual time and a -8 hour
1860 // offset.
1861 //
1862 // One on-line resource suggested setting it to -1 for automatic
1863 // determination of DST.  This works too, during the summer.  Again,
1864 // check during the wintertime too when we're at normal time.
1865       //
1866       alert_time.tm_isdst = -1;
1867 
1868 
1869       // Do the hour offset
1870       alert_time.tm_hour += zone/3600;
1871 
1872       // Now check whether we have any offsets left to do.
1873       zone %= 3600;
1874       if (zone)
1875       {
1876         alert_time.tm_min += zone/60;
1877       }
1878 
1879       // Now check whether we have any overflows.  According
1880       // to the "mktime()" man page, we probably don't need to
1881       // do this for overflow (It normalizes the time itself),
1882       // but I think we still need to for underflow.
1883 //WE7U: Check this stuff carefully!
1884       if (alert_time.tm_min > 59)
1885       {
1886         alert_time.tm_hour++;
1887         alert_time.tm_min -= 60;
1888       }
1889       if (alert_time.tm_hour > 23)
1890       {
1891         alert_time.tm_mday++;
1892         alert_time.tm_hour -= 24;
1893         if (mktime(&alert_time) == -1)
1894         {
1895           alert_time.tm_mday = 1;
1896           alert_time.tm_mon++;
1897           if (mktime(&alert_time) == -1)
1898           {
1899             alert_time.tm_mon = 0;
1900             alert_time.tm_year++;
1901           }
1902         }
1903       }
1904       else if (alert_time.tm_hour < 0)
1905       {
1906         alert_time.tm_hour += 24;
1907         if (--alert_time.tm_mday <= 0)
1908         {
1909           if (--alert_time.tm_mon < 0)
1910           {
1911             alert_time.tm_year--;
1912             alert_time.tm_mon = 11;
1913             alert_time.tm_mday = 31;
1914           }
1915           else if (alert_time.tm_mon == 3 || alert_time.tm_mon == 5 ||
1916                    alert_time.tm_mon == 8 || alert_time.tm_mon == 10)
1917           {
1918             alert_time.tm_mday = 30;
1919           }
1920           else if (alert_time.tm_mon == 1)
1921           {
1922             alert_time.tm_mday = (alert_time.tm_year%4 == 0) ? 29: 28;
1923           }
1924           else
1925           {
1926             alert_time.tm_mday = 31;
1927           }
1928         }
1929       }
1930     }
1931   }
1932   else    // We didn't parse out the day from the input string.
1933   {
1934 
1935     // What's this all about???  Different format of APRS
1936     // time/date string?
1937     alert_time.tm_year--;
1938   }
1939 
1940   if ( debug_level & 2 )
1941   {
1942     time_t a_time,now_time,diff;
1943 
1944     fprintf(stderr,"\n Input: %s\n",aprs_time);
1945 
1946     fprintf(stderr,"Output: %02d%02d%02d\n\n",
1947             alert_time.tm_mday,
1948             alert_time.tm_hour,
1949             alert_time.tm_min);
1950 
1951     a_time = mktime(&alert_time);
1952     fprintf(stderr,"Alert: %ld\n", (long)a_time);
1953 
1954     now_time = sec_now();
1955     fprintf(stderr,"  Now: %ld\n", (long)now_time);
1956 
1957     diff = now_time - a_time;
1958 
1959     if (diff >= 0)
1960     {
1961       fprintf(stderr,"Expired by %ld minutes\n", (long)(diff/60) );
1962     }
1963     else
1964     {
1965       fprintf(stderr,"%ld minutes until expiration\n", (long)((-diff)/60) );
1966     }
1967 
1968     if (alert_time.tm_isdst > 0)
1969     {
1970       fprintf(stderr,"Daylight savings time is in effect\n");
1971     }
1972   }
1973 
1974   return(mktime(&alert_time));
1975 }
1976 
1977 
1978 
1979 
1980 
1981 // Note: last_speed should be in knots.
1982 //
1983 // Input format for lat/long is DDMM.MM or DDMM.MMM format, like:
1984 // 4722.93N   12244.17W  or
1985 // 4722.938N  12244.177W
1986 //
compress_posit(const char * input_lat,const char group,const char * input_lon,const char symbol,const unsigned int last_course,const unsigned int last_speed,const char * phg)1987 char *compress_posit(const char *input_lat, const char group, const char *input_lon, const char symbol,
1988                      const unsigned int last_course, const unsigned int last_speed, const char *phg)
1989 {
1990   static char pos[100];
1991   char lat[5], lon[5];
1992   char c, s, t, ext;
1993   int temp, deg;
1994   double minutes;
1995   char temp_str[20];
1996 
1997 
1998 //fprintf(stderr,"lat:%s, long:%s, symbol:%c%c, course:%d, speed:%d, phg:%s\n",
1999 //    input_lat,
2000 //    input_lon,
2001 //    group,
2002 //    symbol,
2003 //    last_course,
2004 //    last_speed,
2005 //    phg);
2006 
2007   // Fetch degrees (first two chars)
2008   temp_str[0] = input_lat[0];
2009   temp_str[1] = input_lat[1];
2010   temp_str[2] = '\0';
2011   deg = atoi(temp_str);
2012 
2013   // Fetch minutes (rest of numbers)
2014   xastir_snprintf(temp_str,
2015                   sizeof(temp_str),
2016                   "%s",
2017                   input_lat);
2018   temp_str[0] = ' ';  // Blank out degrees
2019   temp_str[1] = ' ';  // Blank out degrees
2020   minutes = atof(temp_str);
2021 
2022   // Check for North latitude
2023   if (strstr(input_lat, "N") || strstr(input_lat, "n"))
2024   {
2025     ext = 'N';
2026   }
2027   else
2028   {
2029     ext = 'S';
2030   }
2031 
2032 //fprintf(stderr,"ext:%c\n", ext);
2033 
2034   temp = 380926 * (90 - (deg + minutes / 60.0) * ( ext=='N' ? 1 : -1 ));
2035 
2036 //fprintf(stderr,"temp: %d\t",temp);
2037 
2038   lat[3] = (char)(temp%91 + 33);
2039   temp /= 91;
2040   lat[2] = (char)(temp%91 + 33);
2041   temp /= 91;
2042   lat[1] = (char)(temp%91 + 33);
2043   temp /= 91;
2044   lat[0] = (char)(temp    + 33);
2045   lat[4] = '\0';
2046 
2047 //fprintf(stderr,"%s\n",lat);
2048 
2049   // Fetch degrees (first three chars)
2050   temp_str[0] = input_lon[0];
2051   temp_str[1] = input_lon[1];
2052   temp_str[2] = input_lon[2];
2053   temp_str[3] = '\0';
2054   deg = atoi(temp_str);
2055 
2056   // Fetch minutes (rest of numbers)
2057   xastir_snprintf(temp_str,
2058                   sizeof(temp_str),
2059                   "%s",
2060                   input_lon);
2061   temp_str[0] = ' ';  // Blank out degrees
2062   temp_str[1] = ' ';  // Blank out degrees
2063   temp_str[2] = ' ';  // Blank out degrees
2064   minutes = atof(temp_str);
2065 
2066   // Check for West longitude
2067   if (strstr(input_lon, "W") || strstr(input_lon, "w"))
2068   {
2069     ext = 'W';
2070   }
2071   else
2072   {
2073     ext = 'E';
2074   }
2075 
2076 //fprintf(stderr,"ext:%c\n", ext);
2077 
2078   temp = 190463 * (180 + (deg + minutes / 60.0) * ( ext=='W' ? -1 : 1 ));
2079 
2080 //fprintf(stderr,"temp: %d\t",temp);
2081 
2082   lon[3] = (char)(temp%91 + 33);
2083   temp /= 91;
2084   lon[2] = (char)(temp%91 + 33);
2085   temp /= 91;
2086   lon[1] = (char)(temp%91 + 33);
2087   temp /= 91;
2088   lon[0] = (char)(temp    + 33);
2089   lon[4] = '\0';
2090 
2091 //fprintf(stderr,"%s\n",lon);
2092 
2093   // Set up csT bytes for course/speed if either are non-zero
2094   c = s = t = ' ';
2095   if (last_course > 0 || last_speed > 0)
2096   {
2097 
2098     if (last_course >= 360)
2099     {
2100       c = '!';  // 360 would be past 'z'.  Set it to zero.
2101     }
2102     else
2103     {
2104       c = (char)(last_course/4 + 33);
2105     }
2106 
2107     s = (char)(log(last_speed + 1.0) / log(1.08) + 33.5);  // Poor man's rounding + ASCII
2108     t = 'C';
2109   }
2110   // Else set up csT bytes for PHG if within parameters
2111   else if (strlen(phg) >= 6)
2112   {
2113     double power, height, gain, range, s_temp;
2114 
2115 
2116     c = '{';
2117 
2118     if ( (phg[3] < '0') || (phg[3] > '9') )   // Power is out of limits
2119     {
2120       power = 0.0;
2121     }
2122     else
2123     {
2124       power = (double)((int)(phg[3]-'0'));
2125       power = power * power;  // Lowest possible value is 0.0
2126     }
2127 
2128     if (phg[4] < '0') // Height is out of limits (no upper limit according to the spec)
2129     {
2130       height = 10.0;
2131     }
2132     else
2133     {
2134       height= 10.0 * pow(2.0,(double)phg[4] - (double)'0');  // Lowest possible value is 10.0
2135     }
2136 
2137     if ( (phg[5] < '0') || (phg[5] > '9') ) // Gain is out of limits
2138     {
2139       gain = 1.0;
2140     }
2141     else
2142     {
2143       gain = pow(10.0,((double)(phg[5]-'0') / 10.0));  // Lowest possible value is 1.0
2144     }
2145 
2146     range = sqrt(2.0 * height * sqrt((power / 10.0) * (gain / 2.0)));   // Lowest possible value is 0.0
2147 
2148     // Check for range of 0, and skip log10 if so
2149     if (range != 0.0)
2150     {
2151       s_temp = log10(range/2) / log10(1.08) + 33.0;
2152     }
2153     else
2154     {
2155       s_temp = 0.0 + 33.0;
2156     }
2157 
2158     s = (char)(s_temp + 0.5);    // Cheater's way of rounding, add 0.5 and truncate
2159 
2160     t = 'C';
2161   }
2162   // Note that we can end up with three spaces at the end if no
2163   // course/speed/phg were supplied.  Do not knock this down, as
2164   // the compressed posit has a fixed 13-character length
2165   // according to the spec!
2166   //
2167   xastir_snprintf(pos,
2168                   sizeof(pos),
2169                   "%c%s%s%c%c%c%c",
2170                   group,
2171                   lat,
2172                   lon,
2173                   symbol,
2174                   c,
2175                   s,
2176                   t);
2177 
2178 //fprintf(stderr,"New compressed pos: (%s)\n",pos);
2179   return pos;
2180 }
2181 
2182 
2183 
2184 
2185 
2186 /*
2187  * See if position is defined
2188  * 90�N 180�W (0,0 in internal coordinates) is our undefined position
2189  * 0N/0E is excluded from trails, could be excluded from map (#define ACCEPT_0N_0E)
2190  */
2191 
position_defined(long lat,long lon,int UNUSED (strict))2192 int position_defined(long lat, long lon, int UNUSED(strict))
2193 {
2194 
2195   if (lat == 0l && lon == 0l)
2196   {
2197     return(0);  // undefined location
2198   }
2199 #ifndef ACCEPT_0N_0E
2200   if (strict)
2201 #endif  // ACCEPT_0N_0E
2202     if (lat == 90*60*60*100l && lon == 180*60*60*100l)      // 0N/0E
2203     {
2204       return(0);  // undefined location
2205     }
2206   return(1);
2207 }
2208 
2209 
2210 
2211 
2212 
2213 // Function to convert from screen (pixel) coordinates to the Xastir
2214 // coordinate system.
2215 //
convert_screen_to_xastir_coordinates(int x,int y,long * lat,long * lon)2216 void convert_screen_to_xastir_coordinates(int x,
2217     int y,
2218     long *lat,
2219     long *lon)
2220 {
2221 
2222   *lon = (center_longitude - ((screen_width  * scale_x)/2) + (x * scale_x));
2223   *lat = (center_latitude  - ((screen_height * scale_y)/2) + (y * scale_y));
2224 
2225   if (*lon < 0)
2226   {
2227     *lon = 0l;  // 180�W
2228   }
2229 
2230   if (*lon > 129600000l)
2231   {
2232     *lon = 129600000l;  // 180�E
2233   }
2234 
2235   if (*lat < 0)
2236   {
2237     *lat = 0l;  //  90�N
2238   }
2239 
2240   if (*lat > 64800000l)
2241   {
2242     *lat = 64800000l;  //  90�S
2243   }
2244 }
2245 
2246 
2247 
2248 
2249 
2250 // Convert Xastir lat/lon to UTM printable string
convert_xastir_to_UTM_str(char * str,int str_len,long x,long y)2251 void convert_xastir_to_UTM_str(char *str, int str_len, long x, long y)
2252 {
2253   double utmNorthing;
2254   double utmEasting;
2255   char utmZone[10];
2256 
2257   ll_to_utm_ups(E_WGS_84,
2258                 (double)(-((y - 32400000l )/360000.0)),
2259                 (double)((x - 64800000l )/360000.0),
2260                 &utmNorthing,
2261                 &utmEasting,
2262                 utmZone,
2263                 sizeof(utmZone) );
2264   utmZone[9] = '\0';
2265   //fprintf(stderr,"%s %07.0f %07.0f\n", utmZone, utmEasting,
2266   //utmNorthing );
2267   xastir_snprintf(str,
2268                   str_len,
2269                   "%s %07.0f %07.0f",
2270                   utmZone,
2271                   utmEasting,
2272                   utmNorthing);
2273 }
2274 
2275 
2276 
2277 
2278 // To produce MGRS coordinates, we'll call ll_to_utm_ups()
2279 // [?? Earlier text: "the above function" ??]
2280 // then convert the result to the 2-letter digraph format used
2281 // for MGRS.  The ll_to_utm_ups() function switches to the special
2282 // irregular UTM zones for the areas near Svalbard and SW Norway
2283 // if the "coordinate_system" variable is set to "USE_MGRS",
2284 // so we'll be using the correct zone boundaries for MGRS if that
2285 // variable is set when we make the call.
2286 //
2287 // If "nice_format" == 1, we add leading spaces plus spaces between
2288 // the easting and northing in order to line up more nicely with the
2289 // UTM output format.
2290 //
2291 // convert_xastir_to_MGRS_str is a wrapper around the components
2292 // function below that returns the MGRS coordinate of x and y as a
2293 // single MGRS string.
2294 //
2295 /* convert_xastir_to_MGRS_str_components returns each of the components
2296    of the MGRS string separately.
2297    Example MGRS string: 18T VK 66790 55998
2298    Parameters:
2299    utmZone          Returns the UTM zone: e.g. 18T
2300    utmZone_len      length of the utmZone char[]
2301    EastingL         Returns the first letter of the MGRS digraph: e.g. V
2302    EastingL_len     length of the EastingL char[]
2303    NorthingL        Returns the second letter of the MGRS digraph: e.g. K
2304    NorthingL_len    length of the NorthingL char[]
2305    int_utmEasting   returns the MGRS easting: e.g. 66790
2306    int_utmNorthing  returns the MGRS northing: e.g. 55998
2307    x                xastir x coordinate to obtain MGRS coordinate for.
2308    y                xastir x coordinate to obtain MGRS coordinate for.
2309    nice_format      1 for populate space_string with three spaces
2310                     0 to make space_string and empty string, see above.
2311    space_string     Returned string that can be used to make MGRS strings
2312                     allign more cleanly with UTM strings.
2313    space_string_len length of the space_string char[]
2314 */
convert_xastir_to_MGRS_str_components(char * utmZone,int UNUSED (utmZone_len),char * EastingL,int EastingL_len,char * NorthingL,int NorthingL_len,unsigned int * int_utmEasting,unsigned int * int_utmNorthing,long x,long y,int nice_format,char * space_string,int UNUSED (space_string_len))2315 void convert_xastir_to_MGRS_str_components(char *utmZone, int UNUSED(utmZone_len),
2316     char *EastingL, int EastingL_len,
2317     char *NorthingL, int NorthingL_len,
2318     unsigned int *int_utmEasting, unsigned int *int_utmNorthing,
2319     long x,  long y,
2320     int nice_format, char *space_string, int UNUSED(space_string_len) )
2321 {
2322 
2323   double utmNorthing;
2324   double utmEasting;
2325   //char utmZone[10];
2326   int start;
2327   int my_east, my_north;
2328   //unsigned int int_utmEasting, int_utmNorthing;
2329   int UPS = 0;
2330   int North_UPS = 0;
2331   int coordinate_system_save = coordinate_system;
2332   //char space_string[4] = "   ";    // Three spaces
2333 
2334   // Set for correct zones
2335   coordinate_system = USE_MGRS;
2336 
2337   ll_to_utm_ups(E_WGS_84,
2338                 (double)(-((y - 32400000l )/360000.0)),
2339                 (double)((x - 64800000l )/360000.0),
2340                 &utmNorthing,
2341                 &utmEasting,
2342                 utmZone,
2343                 sizeof(utmZone) );
2344   utmZone[9] = '\0';
2345 
2346   // Restore it
2347   coordinate_system = coordinate_system_save;
2348 
2349 
2350   //fprintf(stderr,"%s %07.0f %07.0f\n", utmZone, utmEasting,
2351   //utmNorthing );
2352   //xastir_snprintf(str, str_len, "%s %07.0f %07.0f",
2353   //    utmZone, utmEasting, utmNorthing );
2354 
2355 
2356   // Convert the northing and easting values to the digraph letter
2357   // format.  Letters 'I' and 'O' are skipped for both eastings
2358   // and northings.  Letters 'W' through 'Z' are skipped for
2359   // northings.
2360   //
2361   // N/S letters alternate in a cycle of two.  Begins at the
2362   // equator with A and F in alternate zones.  Odd-numbered zones
2363   // use A-V, even-numbered zones use F-V, then A-V.
2364   //
2365   // E/W letters alternate in a cycle of three.  Each E/W zone
2366   // uses an 8-letter block.  Starts at A-H, then J-R, then S-Z,
2367   // then repeats every 18 degrees.
2368   //
2369   // N/S letters have a cycle of two, E/W letters have a cycle of
2370   // three.  The same lettering repeats after six zones (2,000,000
2371   // meter intervals).
2372   //
2373   // AA is at equator and 180W.
2374 
2375   // Easting:  Each zone covers 6 degrees.  Zone 1 = A-H, Zone 2 =
2376   // J-R, Zone 3 = S-Z, then repeat.  So, take zone number-1,
2377   // modulus 3, multiple that number by 8.  Modulus 24.  That's
2378   // our starting letter for the zone.  Take the easting number,
2379   // divide by 100,000, , add the starting number, compute modulus
2380   // 24, then use that index into our E_W array.
2381   //
2382   // Northing:  Figure out whether even/odd zone number.  Divide
2383   // by 100,000.  If even, add 5 (starts at 'F' if even).  Compute
2384   // modulus 20, then use that index into our N_S array.
2385 
2386 
2387   *int_utmEasting = (unsigned int)utmEasting;
2388   *int_utmNorthing = (unsigned int)utmNorthing;
2389   *int_utmEasting = *int_utmEasting % 100000;
2390   *int_utmNorthing = *int_utmNorthing % 100000;
2391 
2392 
2393   // Check for South Polar UPS area, set flags if found.
2394   if (   utmZone[0] == 'A'
2395          || utmZone[0] == 'B'
2396          || utmZone[1] == 'A'
2397          || utmZone[1] == 'B'
2398          || utmZone[2] == 'A'
2399          || utmZone[2] == 'B' )
2400   {
2401     // We're in the South Polar UPS area
2402     UPS++;
2403   }
2404   // Check for North Polar UPS area, set flags if found.
2405   else if (   utmZone[0] == 'Y'
2406               || utmZone[0] == 'Z'
2407               || utmZone[1] == 'Y'
2408               || utmZone[1] == 'Z'
2409               || utmZone[2] == 'Y'
2410               || utmZone[2] == 'Z')
2411   {
2412     // We're in the North Polar UPS area
2413     UPS++;
2414     North_UPS++;
2415   }
2416   else
2417   {
2418     // We're in the UTM area.  Set no flags.
2419   }
2420 
2421 
2422   if (UPS)    // Special processing for UPS area (A/B/Y/Z bands)
2423   {
2424 
2425     // Note: Zone number isn't used for UPS, but zone letter is.
2426     if (nice_format)    // Add two leading spaces
2427     {
2428       utmZone[2] = utmZone[0];
2429       utmZone[0] = ' ';
2430       utmZone[1] = ' ';
2431       utmZone[3] = '\0';
2432     }
2433     else
2434     {
2435       space_string[0] = '\0';
2436     }
2437 
2438     if (North_UPS)      // North polar UPS zone
2439     {
2440       char UPS_N_Easting[15]  = "RSTUXYZABCFGHJ";
2441       char UPS_N_Northing[15] = "ABCDEFGHJKLMNP";
2442 
2443       // Calculate the index into the 2-letter digraph arrays.
2444       my_east = (int)(utmEasting / 100000.0);
2445       my_east = my_east - 13;
2446       my_north = (int)(utmNorthing / 100000.0);
2447       my_north = my_north - 13;
2448 
2449       /*xastir_snprintf(str,
2450           str_len,
2451           "%s %c%c %05d %s%05d",
2452           utmZone,
2453           UPS_N_Easting[my_east],
2454           UPS_N_Northing[my_north],
2455           int_utmEasting,
2456           space_string,
2457           int_utmNorthing ); */
2458       xastir_snprintf(EastingL,EastingL_len,
2459                       "%c", UPS_N_Easting[my_east]);
2460       xastir_snprintf(NorthingL,NorthingL_len,
2461                       "%c", UPS_N_Northing[my_north]);
2462     }
2463     else    // South polar UPS zone
2464     {
2465       char UPS_S_Easting[25]  = "JKLPQRSTUXYZABCFGHJKLPQR";
2466       char UPS_S_Northing[25] = "ABCDEFGHJKLMNPQRSTUVWXYZ";
2467 
2468       // Calculate the index into the 2-letter digraph arrays.
2469       my_east = (int)(utmEasting / 100000.0);
2470       my_east = my_east - 8;
2471       my_north = (int)(utmNorthing / 100000.0);
2472       my_north = my_north - 8;
2473 
2474       /* xastir_snprintf(str,
2475           str_len,
2476           "%s %c%c %05d %s%05d",
2477           utmZone,
2478           UPS_S_Easting[my_east],
2479           UPS_S_Northing[my_north],
2480           int_utmEasting,
2481           space_string,
2482           int_utmNorthing ); */
2483       xastir_snprintf(EastingL,EastingL_len,
2484                       "%c", UPS_S_Easting[my_east]);
2485       xastir_snprintf(NorthingL,NorthingL_len,
2486                       "%c", UPS_S_Northing[my_north]);
2487     }
2488   }
2489   else    // UTM Area
2490   {
2491     char E_W[25] = "ABCDEFGHJKLMNPQRSTUVWXYZ";
2492     char N_S[21] = "ABCDEFGHJKLMNPQRSTUV";
2493 
2494 
2495     if (!nice_format)
2496     {
2497       space_string[0] = '\0';
2498     }
2499 
2500 
2501     // Calculate the indexes into the 2-letter digraph arrays.
2502     start = atoi(utmZone);
2503     start--;
2504     start = start % 3;
2505     start = start * 8;
2506     start = start % 24;
2507     // "start" is now an index into the starting letter for the
2508     // zone.
2509 
2510 
2511     my_east = (int)(utmEasting / 100000.0) - 1;
2512     my_east = my_east + start;
2513     my_east = my_east % 24;
2514 //        fprintf(stderr, "Start: %c   East (guess): %c   ",
2515 //            E_W[start],
2516 //            E_W[my_east]);
2517 
2518 
2519     start = atoi(utmZone);
2520     start = start % 2;
2521     if (start)      // Odd-numbered zone
2522     {
2523       start = 0;
2524     }
2525     else    // Even-numbered zone
2526     {
2527       start = 5;
2528     }
2529     my_north = (int)(utmNorthing / 100000.0);
2530     my_north = my_north + start;
2531     my_north = my_north % 20;
2532 //        fprintf(stderr, "Start: %c   North (guess): %c\n",
2533 //            N_S[start],
2534 //            N_S[my_north]);
2535 
2536     /* xastir_snprintf(str,
2537         str_len,
2538         "%s %c%c %05d %s%05d",
2539         utmZone,
2540         E_W[my_east],
2541         N_S[my_north],
2542         int_utmEasting,
2543         space_string,
2544         int_utmNorthing ); */
2545     xastir_snprintf(EastingL, EastingL_len,
2546                     "%c", E_W[my_east]);
2547     xastir_snprintf(NorthingL, NorthingL_len,
2548                     "%c", N_S[my_north]);
2549   }
2550 }
2551 
2552 
2553 
2554 
2555 
2556 /* Wrapper around convert_xastir_to_MGRS_str_components
2557    to return an MGRS coordinate as a single string.
2558    Parameters:
2559    str The character array to be populated with the MGRS string.
2560    str_len Length of str.
2561    x xastir x coordinate.
2562    y xastir y coordinate.
2563    If "nice_format" == 1, we add leading spaces plus spaces between
2564    the easting and northing in order to line up more nicely with the
2565    UTM output format.
2566 */
convert_xastir_to_MGRS_str(char * str,int str_len,long x,long y,int nice_format)2567 void convert_xastir_to_MGRS_str(char *str, int str_len, long x, long y, int nice_format)
2568 {
2569   char space_string[4] = "   ";    // Three spaces
2570   unsigned int intEasting = 0;
2571   unsigned int intNorthing = 0;
2572   char EastingL[3] = "  ";
2573   char NorthingL[3] = "  ";
2574   char utmZone[10];
2575   convert_xastir_to_MGRS_str_components(utmZone, strlen(utmZone),
2576                                         EastingL, sizeof(EastingL),
2577                                         NorthingL, sizeof(NorthingL),
2578                                         &intEasting, &intNorthing,
2579                                         x,  y,
2580                                         nice_format, space_string, strlen(space_string)) ;
2581   xastir_snprintf(str,
2582                   str_len,
2583                   "%s %c%c %05d %s%05d",
2584                   utmZone,
2585                   EastingL[0],
2586                   NorthingL[0],
2587                   intEasting,
2588                   space_string,
2589                   intNorthing );
2590 }
2591 
2592 
2593 
2594 
2595 
2596 // Convert Xastir lat/lon to UTM
convert_xastir_to_UTM(double * easting,double * northing,char * zone,int zone_len,long x,long y)2597 void convert_xastir_to_UTM(double *easting, double *northing, char *zone, int zone_len, long x, long y)
2598 {
2599 
2600   ll_to_utm_ups(E_WGS_84,
2601                 (double)(-((y - 32400000l )/360000.0)),
2602                 (double)((x - 64800000l )/360000.0),
2603                 northing,
2604                 easting,
2605                 zone,
2606                 zone_len);
2607   zone[zone_len-1] = '\0';
2608 }
2609 
2610 
2611 
2612 
2613 
2614 // Convert UTM to Xastir lat/lon
convert_UTM_to_xastir(double easting,double northing,char * zone,long * x,long * y)2615 void convert_UTM_to_xastir(double easting, double northing, char *zone, long *x, long *y)
2616 {
2617   double lat, lon;
2618 
2619   utm_ups_to_ll(E_WGS_84,
2620                 northing,
2621                 easting,
2622                 zone,
2623                 &lat,
2624                 &lon);
2625 
2626   // Reverse latitude to fit our coordinate system then convert to
2627   // Xastir units.
2628   *y = (long)(lat * -360000.0) + 32400000l;
2629 
2630   // Convert longitude to xastir units
2631   *x = (long)(lon *  360000.0) + 64800000l;
2632 }
2633 
2634 
2635 
2636 
2637 // convert latitude from long to string
2638 // Input is in Xastir coordinate system
2639 //
2640 // CONVERT_LP_NOSP      = DDMM.MMN
2641 // CONVERT_HP_NOSP      = DDMM.MMMN
2642 // CONVERT_VHP_NOSP     = DDMM.MMMMN
2643 // CONVERT_LP_NORMAL    = DD MM.MMN
2644 // CONVERT_HP_NORMAL    = DD MM.MMMN
2645 // CONVERT_UP_TRK       = NDD MM.MMMM
2646 // CONVERT_DEC_DEG      = DD.DDDDDN
2647 // CONVERT_DMS_NORMAL   = DD MM SS.SN
2648 // CONVERT_DMS_NORMAL_FORMATED   = DD'MM'SS.SN
2649 // CONVERT_HP_NORMAL_FORMATED   = DD'MM.MMMMN
2650 //
convert_lat_l2s(long lat,char * str,int str_len,int type)2651 void convert_lat_l2s(long lat, char *str, int str_len, int type)
2652 {
2653   char ns;
2654   float deg, min, sec;
2655   int ideg, imin;
2656   long temp;
2657 
2658 
2659   str[0] = '\0';
2660   deg = (float)(lat - 32400000l) / 360000.0;
2661 
2662   // Switch to integer arithmetic to avoid floating-point
2663   // rounding errors.
2664   temp = (long)(deg * 100000);
2665 
2666   ns = 'S';
2667   if (temp <= 0)
2668   {
2669     ns = 'N';
2670     temp = labs(temp);
2671   }
2672 
2673   ideg = (int)temp / 100000;
2674   min = (temp % 100000) * 60.0 / 100000.0;
2675 
2676   // Again switch to integer arithmetic to avoid floating-point
2677   // rounding errors.
2678   temp = (long)(min * 1000);
2679   imin = (int)(temp / 1000);
2680   sec = (temp % 1000) * 60.0 / 1000.0;
2681 
2682   switch (type)
2683   {
2684 
2685     case(CONVERT_LP_NOSP): /* do low P w/no space */
2686       xastir_snprintf(str,
2687                       str_len,
2688                       "%02d%05.2f%c",
2689                       ideg,
2690 //                min+0.001, // Correct possible unbiased rounding
2691                       min,
2692                       ns);
2693       break;
2694 
2695     case(CONVERT_LP_NORMAL): /* do low P normal */
2696       xastir_snprintf(str,
2697                       str_len,
2698                       "%02d %05.2f%c",
2699                       ideg,
2700 //                min+0.001, // Correct possible unbiased rounding
2701                       min,
2702                       ns);
2703       break;
2704 
2705     case(CONVERT_HP_NOSP): /* do HP w/no space */
2706       xastir_snprintf(str,
2707                       str_len,
2708                       "%02d%06.3f%c",
2709                       ideg,
2710 //                min+0.0001, // Correct possible unbiased rounding
2711                       min,
2712                       ns);
2713       break;
2714 
2715     case(CONVERT_VHP_NOSP): /* do Very HP w/no space */
2716       xastir_snprintf(str,
2717                       str_len,
2718                       "%02d%07.4f%c",
2719                       ideg,
2720 //                min+0.00001, // Correct possible unbiased rounding
2721                       min,
2722                       ns);
2723       break;
2724 
2725     case(CONVERT_UP_TRK): /* for tracklog files */
2726       xastir_snprintf(str,
2727                       str_len,
2728                       "%c%02d %07.4f",
2729                       ns,
2730                       ideg,
2731 //                min+0.00001); // Correct possible unbiased rounding
2732                       min);
2733       break;
2734 
2735     case(CONVERT_DEC_DEG):
2736       xastir_snprintf(str,
2737                       str_len,
2738                       "%08.5f%c",
2739 //                (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
2740                       ideg+min/60.0,
2741                       ns);
2742       break;
2743 
2744     case(CONVERT_DMS_NORMAL):
2745       xastir_snprintf(str,
2746                       str_len,
2747                       "%02d %02d %04.1f%c",
2748                       ideg,
2749                       imin,
2750 //                sec+0.01, // Correct possible unbiased rounding
2751                       sec,
2752                       ns);
2753       break;
2754 
2755     case(CONVERT_DMS_NORMAL_FORMATED):
2756       xastir_snprintf(str,
2757                       str_len,
2758                       "%02d%c%02d\'%04.1f%c",
2759                       ideg,
2760                       0xb0,       // Degree symbol
2761                       imin,
2762 //                sec+0.01, // Correct possible unbiased rounding
2763                       sec,
2764                       ns);
2765       break;
2766 
2767     case(CONVERT_HP_NORMAL_FORMATED):
2768       xastir_snprintf(str,
2769                       str_len,
2770                       "%02d%c%06.3f%c",
2771                       ideg,
2772                       0xb0,         // Degree symbol
2773 //                min+0.0001, // Correct possible unbiased roundin
2774                       min,
2775                       ns);
2776       break;
2777 
2778     case(CONVERT_HP_NORMAL):
2779     default: /* do HP normal */
2780       xastir_snprintf(str,
2781                       str_len,
2782                       "%02d %06.3f%c",
2783                       ideg,
2784 //                min+0.0001, // Correct possible unbiased rounding
2785                       min,
2786                       ns);
2787       break;
2788   }
2789 }
2790 
2791 
2792 
2793 
2794 
2795 // convert longitude from long to string
2796 // Input is in Xastir coordinate system
2797 //
2798 // CONVERT_LP_NOSP      = DDDMM.MME
2799 // CONVERT_HP_NOSP      = DDDMM.MMME
2800 // CONVERT_VHP_NOSP     = DDDMM.MMMME
2801 // CONVERT_LP_NORMAL    = DDD MM.MME
2802 // CONVERT_HP_NORMAL    = DDD MM.MMME
2803 // CONVERT_UP_TRK       = EDDD MM.MMMM
2804 // CONVERT_DEC_DEG      = DDD.DDDDDE
2805 // CONVERT_DMS_NORMAL   = DDD MM SS.SN
2806 // CONVERT_DMS_NORMAL_FORMATED   = DDD'MM'SS.SN
2807 //
convert_lon_l2s(long lon,char * str,int str_len,int type)2808 void convert_lon_l2s(long lon, char *str, int str_len, int type)
2809 {
2810   char ew;
2811   float deg, min, sec;
2812   int ideg, imin;
2813   long temp;
2814 
2815   str[0] = '\0';
2816   deg = (float)(lon - 64800000l) / 360000.0;
2817 
2818   // Switch to integer arithmetic to avoid floating-point rounding
2819   // errors.
2820   temp = (long)(deg * 100000);
2821 
2822   ew = 'E';
2823   if (temp <= 0)
2824   {
2825     ew = 'W';
2826     temp = labs(temp);
2827   }
2828 
2829   ideg = (int)temp / 100000;
2830   min = (temp % 100000) * 60.0 / 100000.0;
2831 
2832   // Again switch to integer arithmetic to avoid floating-point
2833   // rounding errors.
2834   temp = (long)(min * 1000);
2835   imin = (int)(temp / 1000);
2836   sec = (temp % 1000) * 60.0 / 1000.0;
2837 
2838   switch(type)
2839   {
2840 
2841     case(CONVERT_LP_NOSP): /* do low P w/nospacel */
2842       xastir_snprintf(str,
2843                       str_len,
2844                       "%03d%05.2f%c",
2845                       ideg,
2846 //                min+0.001, // Correct possible unbiased rounding
2847                       min,
2848                       ew);
2849       break;
2850 
2851     case(CONVERT_LP_NORMAL): /* do low P normal */
2852       xastir_snprintf(str,
2853                       str_len,
2854                       "%03d %05.2f%c",
2855                       ideg,
2856 //                min+0.001, // Correct possible unbiased rounding
2857                       min,
2858                       ew);
2859       break;
2860 
2861     case(CONVERT_HP_NOSP): /* do HP w/nospace */
2862       xastir_snprintf(str,
2863                       str_len,
2864                       "%03d%06.3f%c",
2865                       ideg,
2866 //                min+0.0001, // Correct possible unbiased rounding
2867                       min,
2868                       ew);
2869       break;
2870 
2871     case(CONVERT_VHP_NOSP): /* do Very HP w/nospace */
2872       xastir_snprintf(str,
2873                       str_len,
2874                       "%03d%07.4f%c",
2875                       ideg,
2876 //                min+0.00001, // Correct possible unbiased rounding
2877                       min,
2878                       ew);
2879       break;
2880 
2881     case(CONVERT_UP_TRK): /* for tracklog files */
2882       xastir_snprintf(str,
2883                       str_len,
2884                       "%c%03d %07.4f",
2885                       ew,
2886                       ideg,
2887 //                min+0.00001); // Correct possible unbiased rounding
2888                       min);
2889       break;
2890 
2891     case(CONVERT_DEC_DEG):
2892       xastir_snprintf(str,
2893                       str_len,
2894                       "%09.5f%c",
2895 //                (ideg+min/60.0)+0.000001, // Correct possible unbiased rounding
2896                       ideg+min/60.0,
2897                       ew);
2898       break;
2899 
2900     case(CONVERT_DMS_NORMAL):
2901       xastir_snprintf(str,
2902                       str_len,
2903                       "%03d %02d %04.1f%c",
2904                       ideg,
2905                       imin,
2906 //                sec+0.01, // Correct possible unbiased rounding
2907                       sec,
2908                       ew);
2909       break;
2910 
2911     case(CONVERT_DMS_NORMAL_FORMATED):
2912       xastir_snprintf(str,
2913                       str_len,
2914                       "%03d%c%02d\'%04.1f%c",
2915                       ideg,
2916                       0xb0,       // Degree symbol
2917                       imin,
2918 //                sec+0.01, // Correct possible unbiased rounding
2919                       sec,
2920                       ew);
2921       break;
2922 
2923     case(CONVERT_HP_NORMAL_FORMATED):
2924       xastir_snprintf(str,
2925                       str_len,
2926                       "%03d%c%06.3f%c",
2927                       ideg,
2928                       0xb0,         // Degree symbol
2929 //                min+0.0001, // Correct possible unbiased rounding
2930                       min,
2931                       ew);
2932       break;
2933 
2934     case(CONVERT_HP_NORMAL):
2935     default: /* do HP normal */
2936       xastir_snprintf(str,
2937                       str_len,
2938                       "%03d %06.3f%c",
2939                       ideg,
2940 //                min+0.0001, // Correct possible unbiased rounding
2941                       min,
2942                       ew);
2943       break;
2944   }
2945 }
2946 
2947 
2948 
2949 
2950 
2951 /* convert latitude from string to long with 1/100 sec resolution */
2952 //
2953 // Input is in [D]DMM.MM[MM]N format (degrees/decimal
2954 // minutes/direction)
2955 //
convert_lat_s2l(char * lat)2956 long convert_lat_s2l(char *lat)        /* N=0�, Ctr=90�, S=180� */
2957 {
2958   long centi_sec;
2959   char copy[15];
2960   char n[15];
2961   char *p;
2962   char offset;
2963 
2964 
2965   // Find the decimal point if present
2966   p = strstr(lat, ".");
2967 
2968   if (p == NULL)  // No decimal point found
2969   {
2970     return(0l);
2971   }
2972 
2973   memset(copy, '\0', sizeof(copy));
2974   offset = p - lat;   // Arithmetic on pointers
2975   switch (offset)
2976   {
2977     case 0:     // .MM[MM]N
2978       return(0l); // Bad, no degrees or minutes
2979       break;
2980     case 1:     // M.MM[MM]N
2981       return(0l); // Bad, no degrees
2982       break;
2983     case 2:     // MM.MM[MM]N
2984       return(0l); // Bad, no degrees
2985       break;
2986     case 3:     // DMM.MM[MM]N
2987       xastir_snprintf(copy,
2988                       sizeof(copy),
2989                       "0%s",  // Add a leading '0'
2990                       lat);
2991       break;
2992     case 4:     // DDMM.MM[MM]N
2993       xastir_snprintf(copy,
2994                       sizeof(copy),
2995                       "%s",   // Copy verbatim
2996                       lat);
2997       break;
2998     default:
2999       break;
3000   }
3001 
3002   copy[14] = '\0';
3003   centi_sec=0l;
3004   if (copy[4]=='.'
3005       && (   (char)toupper((int)copy[ 5])=='N'
3006              || (char)toupper((int)copy[ 6])=='N'
3007              || (char)toupper((int)copy[ 7])=='N'
3008              || (char)toupper((int)copy[ 8])=='N'
3009              || (char)toupper((int)copy[ 9])=='N'
3010              || (char)toupper((int)copy[10])=='N'
3011              || (char)toupper((int)copy[11])=='N'
3012              || (char)toupper((int)copy[ 5])=='S'
3013              || (char)toupper((int)copy[ 6])=='S'
3014              || (char)toupper((int)copy[ 7])=='S'
3015              || (char)toupper((int)copy[ 8])=='S'
3016              || (char)toupper((int)copy[ 9])=='S'
3017              || (char)toupper((int)copy[10])=='S'
3018              || (char)toupper((int)copy[11])=='S'))
3019   {
3020 
3021     substr(n, copy, 2);       // degrees
3022     centi_sec=atoi(n)*60*60*100;
3023 
3024     substr(n, copy+2, 2);     // minutes
3025     centi_sec += atoi(n)*60*100;
3026 
3027     substr(n, copy+5, 4);     // fractional minutes
3028     // Keep the fourth digit if present, as it resolves to 0.6
3029     // of a 1/100 sec resolution.  Two counts make one count in
3030     // the Xastir coordinate system.
3031 
3032     // Extend the digits to full precision by adding zeroes on
3033     // the end.
3034     strncat(n, "0000", sizeof(n) - 1 - strlen(n));
3035 
3036     // Get rid of the N/S character
3037     if (!isdigit((int)n[2]))
3038     {
3039       n[2] = '0';
3040     }
3041     if (!isdigit((int)n[3]))
3042     {
3043       n[3] = '0';
3044     }
3045 
3046     // Terminate substring at the correct digit
3047     n[4] = '\0';
3048 //fprintf(stderr,"Lat: %s\n", n);
3049 
3050     // Add 0.5 (Poor man's rounding)
3051     centi_sec += (long)((atoi(n) * 0.6) + 0.5);
3052 
3053     if (       (char)toupper((int)copy[ 5])=='N'
3054                || (char)toupper((int)copy[ 6])=='N'
3055                || (char)toupper((int)copy[ 7])=='N'
3056                || (char)toupper((int)copy[ 8])=='N'
3057                || (char)toupper((int)copy[ 9])=='N'
3058                || (char)toupper((int)copy[10])=='N'
3059                || (char)toupper((int)copy[11])=='N')
3060     {
3061       centi_sec = -centi_sec;
3062     }
3063 
3064     centi_sec += 90*60*60*100;
3065   }
3066   return(centi_sec);
3067 }
3068 
3069 
3070 
3071 
3072 
3073 /* convert longitude from string to long with 1/100 sec resolution */
3074 //
3075 // Input is in [DD]DMM.MM[MM]W format (degrees/decimal
3076 // minutes/direction).
3077 //
convert_lon_s2l(char * lon)3078 long convert_lon_s2l(char *lon)       /* W=0�, Ctr=180�, E=360� */
3079 {
3080   long centi_sec;
3081   char copy[16];
3082   char n[16];
3083   char *p;
3084   char offset;
3085 
3086 
3087   // Find the decimal point if present
3088   p = strstr(lon, ".");
3089 
3090   if (p == NULL)  // No decimal point found
3091   {
3092     return(0l);
3093   }
3094 
3095   memset(copy, '\0', sizeof(copy));
3096   offset = p - lon;   // Arithmetic on pointers
3097   switch (offset)
3098   {
3099     case 0:     // .MM[MM]N
3100       return(0l); // Bad, no degrees or minutes
3101       break;
3102     case 1:     // M.MM[MM]N
3103       return(0l); // Bad, no degrees
3104       break;
3105     case 2:     // MM.MM[MM]N
3106       return(0l); // Bad, no degrees
3107       break;
3108     case 3:     // DMM.MM[MM]N
3109       xastir_snprintf(copy,
3110                       sizeof(copy),
3111                       "00%s",  // Add two leading zeroes
3112                       lon);
3113       break;
3114     case 4:     // DDMM.MM[MM]N
3115       xastir_snprintf(copy,
3116                       sizeof(copy),
3117                       "0%s",   // Add leading '0'
3118                       lon);
3119       break;
3120     case 5:     // DDDMM.MM[MM]N
3121       xastir_snprintf(copy,
3122                       sizeof(copy),
3123                       "%s",   // Copy verbatim
3124                       lon);
3125       break;
3126     default:
3127       break;
3128   }
3129 
3130   copy[15] = '\0';
3131   centi_sec=0l;
3132   if (copy[5]=='.'
3133       && (   (char)toupper((int)copy[ 6])=='W'
3134              || (char)toupper((int)copy[ 7])=='W'
3135              || (char)toupper((int)copy[ 8])=='W'
3136              || (char)toupper((int)copy[ 9])=='W'
3137              || (char)toupper((int)copy[10])=='W'
3138              || (char)toupper((int)copy[11])=='W'
3139              || (char)toupper((int)copy[12])=='W'
3140              || (char)toupper((int)copy[ 6])=='E'
3141              || (char)toupper((int)copy[ 7])=='E'
3142              || (char)toupper((int)copy[ 8])=='E'
3143              || (char)toupper((int)copy[ 9])=='E'
3144              || (char)toupper((int)copy[10])=='E'
3145              || (char)toupper((int)copy[11])=='E'
3146              || (char)toupper((int)copy[12])=='E'))
3147   {
3148 
3149     substr(n,copy,3);    // degrees 013
3150     centi_sec=atoi(n)*60*60*100;
3151 
3152     substr(n,copy+3,2);  // minutes 26
3153     centi_sec += atoi(n)*60*100;
3154     // 01326.66E  01326.660E
3155 
3156     substr(n,copy+6,4);  // fractional minutes 66E 660E or 6601
3157     // Keep the fourth digit if present, as it resolves to 0.6
3158     // of a 1/100 sec resolution.  Two counts make one count in
3159     // the Xastir coordinate system.
3160 
3161     // Extend the digits to full precision by adding zeroes on
3162     // the end.
3163     strncat(n, "0000", sizeof(n) - 1 - strlen(n));
3164 
3165     // Get rid of the E/W character
3166     if (!isdigit((int)n[2]))
3167     {
3168       n[2] = '0';
3169     }
3170     if (!isdigit((int)n[3]))
3171     {
3172       n[3] = '0';
3173     }
3174 
3175     n[4] = '\0';    // Make sure substring is terminated
3176 //fprintf(stderr,"Lon: %s\n", n);
3177 
3178     // Add 0.5 (Poor man's rounding)
3179     centi_sec += (long)((atoi(n) * 0.6) + 0.5);
3180 
3181     if (       (char)toupper((int)copy[ 6])=='W'
3182                || (char)toupper((int)copy[ 7])=='W'
3183                || (char)toupper((int)copy[ 8])=='W'
3184                || (char)toupper((int)copy[ 9])=='W'
3185                || (char)toupper((int)copy[10])=='W'
3186                || (char)toupper((int)copy[11])=='W'
3187                || (char)toupper((int)copy[12])=='W')
3188     {
3189       centi_sec = -centi_sec;
3190     }
3191 
3192     centi_sec +=180*60*60*100;;
3193   }
3194   return(centi_sec);
3195 }
3196 
3197 
3198 
3199 
3200 
3201 /*
3202  *  Convert latitude from Xastir format to radian
3203  */
convert_lat_l2r(long lat)3204 double convert_lat_l2r(long lat)
3205 {
3206   double ret;
3207   double degrees;
3208 
3209   degrees = (double)(lat / (100.0*60.0*60.0)) -90.0;
3210   ret = (-1)*degrees*(M_PI/180.0);
3211   return(ret);
3212 }
3213 
3214 
3215 
3216 
3217 
3218 /*
3219  *  Convert longitude from Xastir format to radian
3220  */
convert_lon_l2r(long lon)3221 double convert_lon_l2r(long lon)
3222 {
3223   double ret;
3224   double degrees;
3225 
3226   degrees = (double)(lon / (100.0*60.0*60.0)) -180.0;
3227   ret = (-1)*degrees*(M_PI/180.0);
3228   return(ret);
3229 }
3230 
3231 
3232 
3233 
3234 
3235 // Distance calculation (Great Circle) using the Haversine formula
3236 // (2-parameter arctan version), which gives better accuracy than
3237 // the "Law of Cosines" for short distances.  It should be
3238 // equivalent to the "Law of Cosines for Spherical Trigonometry" for
3239 // longer distances.  Haversine is a great-circle calculation.
3240 //
3241 //
3242 // Inputs:  lat1/long1/lat2/long2 in radians (double)
3243 //
3244 // Outputs: Distance in meters between them (double)
3245 //
calc_distance_haversine_radian(double lat1,double lon1,double lat2,double lon2)3246 double calc_distance_haversine_radian(double lat1, double lon1, double lat2, double lon2)
3247 {
3248   double dlon, dlat;
3249   double a, c, d;
3250   double R = EARTH_RADIUS_METERS;
3251 #define square(x) (x)*(x)
3252 
3253 
3254   dlon = lon2 - lon1;
3255   dlat = lat2 - lat1;
3256   a = square((sin(dlat/2.0))) + cos(lat1) * cos(lat2) * square((sin(dlon/2.0)));
3257   c = 2.0 * atan2(sqrt(a), sqrt(1.0-a));
3258   d = R * c;
3259 
3260   return(d);
3261 }
3262 
3263 
3264 
3265 
3266 
3267 // Distance calculation (Great Circle) using the Haversine formula
3268 // (2-parameter arctan version), which gives better accuracy than
3269 // the "Law of Cosines" for short distances.  It should be
3270 // equivalent to the "Law of Cosines for Spherical Trigonometry" for
3271 // longer distances.  Haversine is a great-circle calculation.
3272 //
3273 //
3274 // Inputs:  lat1/long1/lat2/long2 in Xastir coordinate system (long)
3275 //
3276 // Outputs: Distance in meters between them (double)
3277 //
calc_distance_haversine(long lat1,long lon1,long lat2,long lon2)3278 double calc_distance_haversine(long lat1, long lon1, long lat2, long lon2)
3279 {
3280   double r_lat1,r_lat2;
3281   double r_lon1,r_lon2;
3282 
3283   r_lat1 = convert_lat_l2r(lat1);
3284   r_lon1 = convert_lon_l2r(lon1);
3285   r_lat2 = convert_lat_l2r(lat2);
3286   r_lon2 = convert_lon_l2r(lon2);
3287 
3288   return(calc_distance_haversine_radian(r_lat1, r_lon1, r_lat2, r_lon2));
3289 }
3290 
3291 
3292 
3293 
3294 
3295 /*
3296  *  Calculate distance in meters between two locations
3297  *
3298  *  What type of calculation is this, Rhumb Line distance or Great
3299  *  Circle distance?
3300  *  Answer:  "Law of Cosines for Spherical Trigonometry", which is a
3301  *  great-circle calculation.
3302  *
3303  *
3304  * Inputs:  lat1/long1/lat2/long2 in Xastir coordinate system (long)
3305  *
3306  * Outputs: Distance in meters between them (double)
3307  *
3308  */
calc_distance_law_of_cosines(long lat1,long lon1,long lat2,long lon2)3309 double calc_distance_law_of_cosines(long lat1, long lon1, long lat2, long lon2)
3310 {
3311   double r_lat1,r_lat2;
3312   double r_lon1,r_lon2;
3313   double r_d;
3314 
3315 
3316   r_lat1 = convert_lat_l2r(lat1);
3317   r_lon1 = convert_lon_l2r(lon1);
3318   r_lat2 = convert_lat_l2r(lat2);
3319   r_lon2 = convert_lon_l2r(lon2);
3320   r_d = acos(sin(r_lat1) * sin(r_lat2) + cos(r_lat1) * cos(r_lat2) * cos(r_lon1-r_lon2));
3321 
3322 //fprintf(stderr,"Law of Cosines Distance: %f\n",
3323 //    r_d*180*60/M_PI*1852);
3324 //fprintf(stderr,"     Haversine Distance: %f\n\n",
3325 //    calc_distance_haversine(lat1, lon1, lat2, lon2));
3326 
3327   return(r_d*180*60/M_PI*1852);
3328 }
3329 
3330 
3331 
3332 
3333 
3334 // Here's where we choose which of the above two functions get used.
3335 //
calc_distance(long lat1,long lon1,long lat2,long lon2)3336 double calc_distance(long lat1, long lon1, long lat2, long lon2)
3337 {
3338 //    return(calc_distance_law_of_cosines(lat1, lon1, lat2, lon2));
3339   return(calc_distance_haversine(lat1, lon1, lat2, lon2));
3340 }
3341 
3342 
3343 
3344 
3345 
3346 /*
3347  *  Calculate distance between two locations in nautical miles and course from loc2 to loc1
3348  *
3349  *  What type of calculation is this, Rhumb Line distance or Great
3350  *  Circle distance?
3351  *  Answer:  "Law of Cosines for Spherical Trigonometry", which is a
3352  *  great-circle calculation, or Haversine, also a great-circle
3353  *  calculation.
3354  *
3355  *  NOTE:  The angle returned is a separate calculation, but using
3356  *  the unit sphere distance in it's calculation.  A great circle
3357  *  bearing is computed, not a Rhumb-line bearing.
3358  *
3359  *
3360  * Inputs:  lat1/long1/lat2/long2 in Xastir coordinate system (long)
3361  *          Length of course_deg string (int)
3362  *
3363  * Outputs: Distance in nautical miles between them (double).
3364  *          course_deg (string)
3365  *
3366  */
calc_distance_course(long lat1,long lon1,long lat2,long lon2,char * course_deg,int course_deg_length)3367 double calc_distance_course(long lat1, long lon1, long lat2, long lon2, char *course_deg, int course_deg_length)
3368 {
3369   double ret;
3370   double r_lat1, r_lat2;
3371   double r_lon1, r_lon2;
3372   double r_d, r_c, r_m;
3373 
3374   r_lat1 = convert_lat_l2r(lat1);
3375   r_lon1 = convert_lon_l2r(lon1);
3376   r_lat2 = convert_lat_l2r(lat2);
3377   r_lon2 = convert_lon_l2r(lon2);
3378 
3379 
3380   // Compute the distance.  We have a choice between using Law of
3381   // Cosines or Haversine Formula here.
3382 
3383   // 1) Law of Cosines for Spherical Trigonometry.  This is
3384   // unreliable for small distances because the inverse cosine is
3385   // ill-conditioned.  A computer carrying seven significant
3386   // digits can't distinguish the cosines of distances smaller
3387   // than about one minute of arc.
3388 //    r_d = acos(sin(r_lat1) * sin(r_lat2) + cos(r_lat1) * cos(r_lat2) * cos(r_lon1-r_lon2));
3389 
3390 
3391   // 2) Haversine Formula.  Returns answer in meters.
3392   r_m = calc_distance_haversine_radian(r_lat1, r_lon1, r_lat2, r_lon2);
3393   //
3394   // Conversion from distance in meters back to unit sphere.  This
3395   // is needed for the course calculation below as well as the
3396   // later scaling up to feet/meters or miles/km.
3397   r_d = r_m / EARTH_RADIUS_METERS;
3398 
3399 
3400   // Compute the great-circle bearing
3401   if (cos(r_lat1) < 0.0000000001)
3402   {
3403     if (r_lat1>0.0)
3404     {
3405       r_c=M_PI;
3406     }
3407     else
3408     {
3409       r_c=0.0;
3410     }
3411   }
3412   else
3413   {
3414     if (sin((r_lon2-r_lon1))<0.0)
3415     {
3416       r_c = acos((sin(r_lat2)-sin(r_lat1)*cos(r_d))/(sin(r_d)*cos(r_lat1)));
3417     }
3418     else
3419     {
3420       r_c = (2*M_PI) - acos((sin(r_lat2)-sin(r_lat1)*cos(r_d))/(sin(r_d)*cos(r_lat1)));
3421     }
3422 
3423   }
3424 
3425   // Return the course
3426   xastir_snprintf(course_deg,
3427                   course_deg_length,
3428                   "%.1f",
3429                   (180.0/M_PI)*r_c);
3430 
3431   // Return the distance (nautical miles?)
3432   ret = r_d*180*60/M_PI;
3433 
3434   /*
3435   // Convert from nautical miles to feet
3436   fprintf(stderr,"Law of Cosines Distance: %fft\t%fmi\n",
3437       ret*5280.0*1.15078, ret*1.15078);
3438   // Convert from meters to feet
3439   fprintf(stderr,"     Haversine Distance: %fft\t%fmi\n\n",
3440       calc_distance_haversine(lat1, lon1, lat2, lon2)*3.28084,
3441       calc_distance_haversine(lat1, lon1, lat2, lon2)/1000/1.609344);
3442   */
3443 
3444   return(ret);
3445 }
3446 
3447 
3448 
3449 
3450 
3451 //*****************************************************************
3452 // distance_from_my_station - compute distance from my station and
3453 //       course with a given call
3454 //
3455 // return distance and course
3456 //
3457 // Returns 0.0 for distance if station not found in database or the
3458 // station hasn't sent out a posit yet.
3459 //*****************************************************************
3460 
distance_from_my_station(char * call_sign,char * course_deg)3461 double distance_from_my_station(char *call_sign, char *course_deg)
3462 {
3463   DataRow *p_station;
3464   double distance;
3465   float value;
3466   long l_lat, l_lon;
3467 
3468   distance = 0.0;
3469   l_lat = convert_lat_s2l(my_lat);
3470   l_lon = convert_lon_s2l(my_long);
3471   p_station = NULL;
3472   if (search_station_name(&p_station,call_sign,1))
3473   {
3474     // Check whether we have a posit yet for this station
3475     if ( (p_station->coord_lat == 0l)
3476          && (p_station->coord_lon == 0l) )
3477     {
3478       distance = 0.0;
3479     }
3480     else
3481     {
3482       value = (float)calc_distance_course(l_lat,
3483                                           l_lon,
3484                                           p_station->coord_lat,
3485                                           p_station->coord_lon,
3486                                           course_deg,
3487                                           sizeof(course_deg));
3488 
3489       if (english_units)
3490       {
3491         distance = value * 1.15078;  // nautical miles to miles
3492       }
3493       else
3494       {
3495         distance = value * 1.852;  // nautical miles to km
3496       }
3497     }
3498 //        fprintf(stderr,"DistFromMy: %s %s -> %f\n",temp_lat,temp_long,distance);
3499   }
3500   else    // Station not found
3501   {
3502     distance = 0.0;
3503   }
3504 
3505   //fprintf(stderr,"Distance for %s: %f\n", call_sign, distance);
3506 
3507   return(distance);
3508 }
3509 
3510 
3511 
3512 
3513 
3514 /*********************************************************************/
3515 /* convert_bearing_to_name - converts a bearing in degrees to        */
3516 /*   name for the bearing.  Expects the degrees as a text string     */
3517 /*   since that's what the preceding functions output.               */
3518 /* Set the opposite flag true for the inverse bearing (from vs to)   */
3519 /*********************************************************************/
3520 
3521 static struct
3522 {
3523   double low,high;
3524   char *dircode,*lang;
3525 } directions[] =
3526 {
3527   {327.5,360,"N","SPCHDIRN00"},
3528   {0,22.5,"N","SPCHDIRN00"},
3529   {22.5,67.5,"NE","SPCHDIRNE0"},
3530   {67.5,112.5,"E","SPCHDIRE00"},
3531   {112.5,157.5,"SE","SPCHDIRSE0"},
3532   {157.5,202.5,"S","SPCHDIRS00"},
3533   {202.5,247.5,"SW","SPCHDIRSW0"},
3534   {247.5,292.5,"W","SPCHDIRW00"},
3535   {292.5,327.5,"NW","SPCHDIRNW0"},
3536 };
3537 
3538 
3539 
convert_bearing_to_name(char * bearing,int opposite)3540 char *convert_bearing_to_name(char *bearing, int opposite)
3541 {
3542   double deg = atof(bearing);
3543   int i;
3544 
3545   if (opposite)
3546   {
3547     if (deg > 180)
3548     {
3549       deg -= 180.0;
3550     }
3551     else if (deg <= 180)
3552     {
3553       deg += 180.0;
3554     }
3555   }
3556   for (i = 0; i < (int)( sizeof(directions)/sizeof(directions[0]) ); i++)
3557   {
3558     if (deg >= directions[i].low && deg < directions[i].high)
3559     {
3560       return langcode(directions[i].lang);
3561     }
3562   }
3563   return "?";
3564 }
3565 
3566 
3567 
3568 
3569 
3570 // Calculate new position based on distance and angle.
3571 //
3572 // Input:   lat/long in Xastir coordinate system (100ths of seconds)
3573 //          distance in nautical miles
3574 //          angle in � true
3575 //
3576 // Outputs: *x_long, *y_lat in Xastir coordinate system (100ths of
3577 //           seconds)
3578 //
3579 //
3580 // From http://home.t-online.de/home/h.umland/Chapter12.pdf
3581 //
3582 // Dead-reckoning using distance in km, course C:
3583 // Lat_B� = Lat_A� + ( (360/40031.6) * distance * cos C )
3584 //
3585 // Dead-reckoning using distance in nm, course C:
3586 // Lat_B� = Lat_A� + ( (distance/60) * cos C )
3587 //
3588 // Average of two latitudes (required for next two equations)
3589 // Lat_M� = (Lat_A� + Lat_B�) / 2
3590 //
3591 // Dead-reckoning using distance in km, course C:
3592 // Lon_B� = Lon_A� + ( (360/40031.6) * distance * (sin C / cos
3593 // Lat_M) )
3594 //
3595 // Dead-reckoning using distance in nm, course C:
3596 // Lon_B� = Lon_A� + ( (distance/60) * (sin C / cos Lat_M) )
3597 //
3598 // If resulting longitude exceeds +/- 180�, subtract/add 360�.
3599 //
compute_DR_position(long x_long,long y_lat,double range,double course,long * x_long2,long * y_lat2)3600 void compute_DR_position(long x_long,   // input
3601                          long y_lat,     // input
3602                          double range,   // input in nautical miles
3603                          double course,  // input in � true
3604                          long *x_long2,  // output
3605                          long *y_lat2)   // output
3606 {
3607   double bearing_radians, lat_M_radians;
3608   float lat_A, lat_B, lon_A, lon_B, lat_M;
3609   int ret;
3610   unsigned long x_u_long, y_u_lat;
3611 
3612 
3613 //fprintf(stderr,"Distance:%fnm, Course:%f,  Time:%d\n",
3614 //    range,
3615 //    course,
3616 //    (int)(sec_now() - p_station->sec_heard));
3617 
3618   // Bearing in radians
3619   bearing_radians = (double)((course/360.0) * 2.0 * M_PI);
3620 
3621   // Convert lat/long to floats
3622   ret = convert_from_xastir_coordinates( &lon_A,
3623                                          &lat_A,
3624                                          x_long,
3625                                          y_lat);
3626 
3627   // Check if conversion ok
3628   if (!ret)
3629   {
3630     // Problem during conversion.  Exit without changes.
3631     *x_long2 = x_long;
3632     *y_lat2 = y_lat;
3633     return;
3634   }
3635 
3636   // Compute new latitude
3637   lat_B = (float)((double)(lat_A) + (range/60.0) * cos(bearing_radians));
3638 
3639   // Compute mid-range latitude
3640   lat_M = (lat_A + lat_B) / 2.0;
3641 
3642   // Convert lat_M to radians
3643   lat_M_radians = (double)((lat_M/360.0) * 2.0 * M_PI);
3644 
3645   // Compute new longitude
3646   lon_B = (float)((double)(lon_A)
3647                   + (range/60.0) * ( sin(bearing_radians) / cos(lat_M_radians)) );
3648 
3649   // Test for out-of-bounds longitude, correct if so.
3650   if (lon_B < -360.0)
3651   {
3652     lon_B = lon_B + 360.0;
3653   }
3654   if (lon_B >  360.0)
3655   {
3656     lon_B = lon_B - 360.0;
3657   }
3658 
3659 //fprintf(stderr,"Lat:%f,  Lon:%f\n", lat_B, lon_B);
3660 
3661   ret = convert_to_xastir_coordinates(&x_u_long,
3662                                       &y_u_lat,
3663                                       lon_B,
3664                                       lat_B);
3665 
3666   // Check if conversion ok
3667   if (!ret)
3668   {
3669     // Problem during conversion.  Exit without changes.
3670     *x_long2 = x_long;
3671     *y_lat2 = y_lat;
3672     return;
3673   }
3674 
3675   // Convert from unsigned long to long
3676   *x_long2 = (long)x_u_long;
3677   *y_lat2  = (long)y_u_lat;
3678 }
3679 
3680 
3681 
3682 
3683 
3684 // Calculate new position based on speed/course/modified-time.
3685 // We'll call it from Create_object_item_tx_string() and from the
3686 // modify object/item routines to calculate a new position and stuff
3687 // it into the record along with the modification time before we
3688 // start off in a new direction.
3689 //
3690 // Input:   *p_station
3691 //
3692 // Outputs: *x_long, *y_lat in Xastir coordinate system (100ths of
3693 //           seconds)
3694 //
3695 //
3696 // From http://home.t-online.de/home/h.umland/Chapter12.pdf
3697 //
3698 // Dead-reckoning using distance in km, course C:
3699 // Lat_B� = Lat_A� + ( (360/40031.6) * distance * cos C )
3700 //
3701 // Dead-reckoning using distance in nm, course C:
3702 // Lat_B� = Lat_A� + ( (distance/60) * cos C )
3703 //
3704 // Average of two latitudes (required for next two equations)
3705 // Lat_M� = (Lat_A� + Lat_B�) / 2
3706 //
3707 // Dead-reckoning using distance in km, course C:
3708 // Lon_B� = Lon_A� + ( (360/40031.6) * distance * (sin C / cos
3709 // Lat_M) )
3710 //
3711 // Dead-reckoning using distance in nm, course C:
3712 // Lon_B� = Lon_A� + ( (distance/60) * (sin C / cos Lat_M) )
3713 //
3714 // If resulting longitude exceeds +/- 180�, subtract/add 360�.
3715 //
3716 //
3717 // Possible Problems/Changes:
3718 // --------------------------
3719 // *) Change to using last_modified_time for DR.  Also tweak the
3720 //    code so that we don't do incremental DR and use our own
3721 //    decoded objects to update everything.  If we keep the
3722 //    last_modified_time and the last_modified_position separate
3723 //    DR'ed objects/items, we can always use those instead of the
3724 //    other variables if we have a non-zero speed.
3725 //
3726 // *) Make sure not to corrupt our position of the object when we
3727 //    receive the packet back via loopback/RF/internet.  In
3728 //    particular the position and the last_modified_time should stay
3729 //    constant in this case so that dead-reckoning can continue to
3730 //    move the object consistently, plus we won't compound errors as
3731 //    we go.
3732 //
3733 // *) A server Xastir sees empty strings on it's server port when
3734 //    these objects are transmitted to it.  Investigate.  It
3735 //    sometimes does it when speed is 0, but it's not consistent.
3736 //
3737 // *) Get the last_modified_time embedded into the logfile so that
3738 //    we don't "lose time" if we shut down for a bit.  DR'ed objects
3739 //    will be at the proper positions when we start back up.
3740 //
compute_current_DR_position(DataRow * p_station,long * x_long,long * y_lat)3741 void compute_current_DR_position(DataRow *p_station, long *x_long, long *y_lat)
3742 {
3743   int my_course = 0;  // In � true
3744   double range = 0.0;
3745   double bearing_radians, lat_M_radians;
3746   float lat_A, lat_B, lon_A, lon_B, lat_M;
3747   int ret;
3748   unsigned long x_u_long, y_u_lat;
3749   time_t secs_now;
3750 
3751 
3752   secs_now=sec_now();
3753 
3754 
3755   // Check whether we have course in the current data
3756   //
3757   if ( (strlen(p_station->course)>0) && (atof(p_station->course) > 0) )
3758   {
3759 
3760     my_course = atoi(p_station->course);    // In � true
3761   }
3762   //
3763   // Else check whether the previous position had a course.  Note
3764   // that newest_trackpoint if it exists should be the same as the
3765   // current data, so we have to go back one further trackpoint.
3766   // Make sure in this case that this trackpoint has occurred
3767   // within the dead-reckoning timeout period though, else ignore
3768   // it.
3769   //
3770   else if ( (p_station->newest_trackpoint != NULL)
3771             && (p_station->newest_trackpoint->prev != NULL)
3772             && (p_station->newest_trackpoint->prev->course != -1)   // Undefined
3773             && ( (secs_now-p_station->newest_trackpoint->prev->sec) < dead_reckoning_timeout) )
3774   {
3775 
3776     // In � true
3777     my_course = p_station->newest_trackpoint->prev->course;
3778   }
3779 
3780 
3781   // Get distance in nautical miles from the current data
3782   //
3783   if ( (strlen(p_station->speed)>0) && (atof(p_station->speed) >= 0) )
3784   {
3785 
3786     // Speed is in knots (same as nautical miles/hour)
3787     range = (double)( (sec_now() - p_station->sec_heard)
3788                       * ( atof(p_station->speed) / 3600.0 ) );
3789   }
3790   //
3791   // Else check whether the previous position had speed.  Note
3792   // that newest_trackpoint if it exists should be the same as the
3793   // current data, so we have to go back one further trackpoint.
3794   //
3795   else if ( (p_station->newest_trackpoint != NULL)
3796             && (p_station->newest_trackpoint->prev != NULL)
3797             && (p_station->newest_trackpoint->prev->speed != -1) // Undefined
3798             && ( (secs_now-p_station->newest_trackpoint->prev->sec) < dead_reckoning_timeout) )
3799   {
3800 
3801     // Speed is in units of 0.1km/hour.  Different than above!
3802     range = (double)( (sec_now() - p_station->sec_heard)
3803                       * ( p_station->newest_trackpoint->prev->speed / 10 * 0.5399568 / 3600.0 ) );
3804   }
3805 
3806 
3807 //fprintf(stderr,"Distance:%fnm, Course:%d,  Time:%d\n",
3808 //    range,
3809 //    my_course,
3810 //    (int)(sec_now() - p_station->sec_heard));
3811 
3812   // Bearing in radians
3813   bearing_radians = (double)((my_course/360.0) * 2.0 * M_PI);
3814 
3815   // Convert lat/long to floats
3816   ret = convert_from_xastir_coordinates( &lon_A,
3817                                          &lat_A,
3818                                          p_station->coord_lon,
3819                                          p_station->coord_lat);
3820 
3821   // Check if conversion ok
3822   if (!ret)
3823   {
3824     // Problem during conversion.  Exit without changes.
3825     *x_long = p_station->coord_lon;
3826     *y_lat = p_station->coord_lat;
3827     return;
3828   }
3829 
3830   // Compute new latitude
3831   lat_B = (float)((double)(lat_A) + (range/60.0) * cos(bearing_radians));
3832 
3833   // Compute mid-range latitude
3834   lat_M = (lat_A + lat_B) / 2.0;
3835 
3836   // Convert lat_M to radians
3837   lat_M_radians = (double)((lat_M/360.0) * 2.0 * M_PI);
3838 
3839   // Compute new longitude
3840   lon_B = (float)((double)(lon_A)
3841                   + (range/60.0) * ( sin(bearing_radians) / cos(lat_M_radians)) );
3842 
3843   // Test for out-of-bounds longitude, correct if so.
3844   if (lon_B < -360.0)
3845   {
3846     lon_B = lon_B + 360.0;
3847   }
3848   if (lon_B >  360.0)
3849   {
3850     lon_B = lon_B - 360.0;
3851   }
3852 
3853 //fprintf(stderr,"Lat:%f,  Lon:%f\n", lat_B, lon_B);
3854 
3855   ret = convert_to_xastir_coordinates(&x_u_long,
3856                                       &y_u_lat,
3857                                       lon_B,
3858                                       lat_B);
3859 
3860   // Check if conversion ok
3861   if (!ret)
3862   {
3863     // Problem during conversion.  Exit without changes.
3864     *x_long = p_station->coord_lon;
3865     *y_lat = p_station->coord_lat;
3866     return;
3867   }
3868 
3869   // Convert from unsigned long to long
3870   *x_long = (long)x_u_long;
3871   *y_lat  = (long)y_u_lat;
3872 }
3873 
3874 
3875 
3876 
3877 
filethere(char * fn)3878 int filethere(char *fn)
3879 {
3880   FILE *f;
3881   int ret;
3882 
3883   ret =0;
3884   f=fopen(fn,"r");
3885   if (f != NULL)
3886   {
3887     ret=1;
3888     (void)fclose(f);
3889   }
3890   return(ret);
3891 }
3892 
3893 
3894 
3895 
3896 
filecreate(char * fn)3897 int filecreate(char *fn)
3898 {
3899   FILE *f;
3900   int ret;
3901 
3902   if (! filethere(fn))     // If no file
3903   {
3904 
3905     ret = 0;
3906     fprintf(stderr,"Making user %s file\n", fn);
3907     f=fopen(fn,"w+");   // Create it
3908     if (f != NULL)
3909     {
3910       ret=1;          // We were successful
3911       (void)fclose(f);
3912     }
3913     return(ret);        // Couldn't create file for some reason
3914   }
3915   return(1);  // File already exists
3916 }
3917 
3918 
3919 
3920 
3921 
file_time(char * fn)3922 time_t file_time(char *fn)
3923 {
3924   struct stat file_status;
3925 
3926   if(stat(fn,&file_status)==0)
3927   {
3928     return((time_t)file_status.st_ctime);
3929   }
3930 
3931   return(-1);
3932 }
3933 
3934 
3935 
3936 
3937 
3938 // Function written by Adam Hahn, AI4QB.  Contributed to the public
3939 // domain.  We've modified it from his initial code so any bugs are
3940 // our fault.
copy_file(char * infilename,char * outfilename)3941 int copy_file(char *infilename, char *outfilename)
3942 {
3943   FILE *infile, *outfile;
3944   char *buffer;
3945   size_t numread = 0;
3946 
3947 
3948   if ((infile = fopen(infilename,"rb")) > (FILE *)0)
3949   {
3950 
3951     if ((outfile = fopen(outfilename,"wb")) > (FILE *)0)
3952     {
3953       buffer = (char *)malloc(1024);
3954 
3955       while (!feof(infile))
3956       {
3957         numread = fread(buffer, 1, 1024, infile);
3958         fwrite(buffer, 1, numread, outfile);
3959       }
3960       free(buffer);
3961       fflush(outfile);
3962       fclose(outfile);
3963     }
3964     else
3965     {
3966       fprintf(stderr,"Error opening destination file %s for writing", outfilename);
3967       fclose(infile);
3968       return(1);
3969     }
3970     fclose(infile);
3971   }
3972   else
3973   {
3974     fprintf(stderr,"Error opening source file %s for reading", infilename);
3975     return(1);
3976   }
3977   return 0;
3978 }
3979 
3980 
3981 
3982 
3983 
3984 // used by log_data
rotate_file(char * file,int max_keep)3985 void rotate_file(char *file, int max_keep )
3986 {
3987   int i;
3988   char file_a[MAX_FILENAME];
3989   char file_b[MAX_FILENAME];
3990   struct stat file_status;
3991 
3992   if (debug_level & 1)
3993   {
3994     fprintf(stderr, "Rotating: %s. Will keep %d \n", file, max_keep);
3995   }
3996 
3997   for(i=max_keep; i>=1; i--)
3998   {
3999 
4000     if (debug_level & 1)
4001     {
4002       fprintf(stderr, "rotate: %s : %s\n", file_b, file_a);
4003     }
4004 
4005     xastir_snprintf(file_a,sizeof(file_a),"%s.%d",file,i);
4006     xastir_snprintf(file_b,sizeof(file_b),"%s.%d",file,i-1);
4007 
4008     unlink (file_a);
4009     if (stat(file_a, &file_status) == 0)
4010     {
4011       // We got good status.  That means it didn't get deleted!
4012       fprintf(stderr,
4013               "Couldn't delete file '%s': %s",
4014               file_a,strerror(errno));
4015       return;
4016     }
4017 
4018     // Rename previous to next
4019     //
4020     // Check whether file_b exists
4021     if (stat(file_b, &file_status) == 0)
4022     {
4023       if (!S_ISREG(file_status.st_mode))
4024       {
4025         fprintf(stderr,
4026                 "Couldn't stat %s\n",
4027                 file_b);
4028         break;
4029       }
4030       if ( rename (file_b, file_a) )
4031       {
4032         fprintf(stderr,
4033                 "Couldn't rename %s to %s, cancelling log_rotate()\n",
4034                 file_b,
4035                 file_a);
4036         return;
4037       }
4038     }
4039 
4040   }
4041 
4042   if (debug_level & 1)
4043   {
4044     fprintf(stderr, "rotate: %s : %s\n", file, file_a);
4045   }
4046 
4047   if ( rename (file, file_a) )
4048   {
4049     fprintf(stderr,
4050             "Couldn't rename %s to %s, cancelling log_rotate()\n",
4051             file,
4052             file_a);
4053   }
4054 
4055   return;
4056 }
4057 
4058 
4059 
4060 
4061 
fetch_file_line(FILE * f,char * line)4062 char *fetch_file_line(FILE *f, char *line)
4063 {
4064   char cin;
4065   int pos;
4066 
4067 
4068   pos = 0;
4069   line[0] = '\0';
4070 
4071   while (!feof(f))
4072   {
4073 
4074     // Read one character at a time
4075     if (fread(&cin,1,1,f) == 1)
4076     {
4077 
4078       if (pos < MAX_LINE_SIZE)
4079       {
4080         if (cin != (char)13)    // CR
4081         {
4082           line[pos++] = cin;
4083         }
4084       }
4085 
4086       if (cin == (char)10)   // Found LF as EOL char
4087       {
4088         line[pos++] = '\0'; // Always add a terminating zero after last char
4089         pos = 0;          // start next line
4090         return(line);
4091       }
4092     }
4093   }
4094 
4095   // Must be end of file
4096   line[pos] = '\0';
4097   return(line);
4098 }
4099 
4100 
4101 
4102 
4103 
4104 // Restore weather alerts so that we have a clear picture of the
4105 // current state.  Check timestamps on the file.  If relatively
4106 // current, read the file in.
4107 //
load_wx_alerts_from_log_working_sub(time_t time_now,char * filename)4108 void load_wx_alerts_from_log_working_sub(time_t time_now, char *filename)
4109 {
4110   time_t file_timestamp;
4111   int file_age;
4112   int expire_limit;   // In seconds
4113   char line[MAX_LINE_SIZE+1];
4114   FILE *f;
4115 
4116 
4117   expire_limit = 60 * 60 * 24 * 15;   // 15 days
4118 //    expire_limit = 60 * 60 * 24 * 1;   // 1 day
4119 
4120   file_timestamp = file_time(filename);
4121 
4122   if (file_timestamp == -1)
4123   {
4124 //        fprintf(stderr,"File %s doesn't exist\n", filename);
4125     return;
4126   }
4127 
4128   file_age = time_now - file_timestamp;
4129 
4130   if ( file_age > expire_limit)
4131   {
4132 //        fprintf(stderr,"Old file: %s, skipping...\n", filename);
4133     return;
4134   }
4135 
4136 //    fprintf(stderr,"File is current: %s\n", filename);
4137 
4138   // Read the file in, as it exists and is relatively new.
4139 
4140   // Check timestamps before each log line and skip those that are
4141   // old.  Lines in the file should look about like this:
4142   //
4143   // # 1157027319  Thu Aug 31 05:28:39 PDT 2006
4144   // OUNSWO>APRS::SKYOUN   :OUN Check For Activation VCEAB
4145   // # 1157027319  Thu Aug 31 05:28:39 PDT 2006
4146   // LZKFFS>APRS::NWS_ADVIS:311324z,FLOOD,ARC67-147 V1PAA
4147   //
4148   // We could try to use the regular read_file and read_file_ptr
4149   // scheme for reading in the log file, but we'd have to modify
4150   // it in two ways:  We need to keep from bringing up the
4151   // interfaces so we'd need to set a flag when done reading and
4152   // then start them, plus we'd need to have it check the
4153   // timestamps and skip old ones.  Instead we'll do it all on our
4154   // own here so that we can control everything ourselves.
4155 
4156   f = fopen(filename, "r");
4157   if (!f)
4158   {
4159     fprintf(stderr,"Wx Alert log file could not be opened for reading\n");
4160     return;
4161   }
4162 
4163   while (!feof(f))    // Read until end of file
4164   {
4165 
4166     (void)fetch_file_line(f, line);
4167 
4168 restart_sync:
4169 
4170     if (line[0] == '\0')
4171     {
4172       // Empty line found, skip
4173     }
4174 
4175     else if (line[0] == '#')   // Timestamp line, check the date
4176     {
4177       time_t line_stamp;
4178       int line_age;
4179 
4180       if (strlen(line) < 3)   // Line is too short, skip
4181       {
4182         goto restart_sync;
4183       }
4184 
4185       line_stamp = atoi(&line[2]);
4186       line_age = time_now - line_stamp;
4187 //fprintf(stderr, "Age: %d\t", line_age);
4188 
4189       if ( line_age < expire_limit)
4190       {
4191         // Age is good, read next line and process it
4192 
4193         (void)fetch_file_line(f, line);
4194 
4195         if (line[0] != '#')   // It's a packet, not a timestamp line
4196         {
4197 //fprintf(stderr,"%s\n",line);
4198           decode_ax25_line(line,'F',-1, 1);   // Decode the packet
4199         }
4200         else
4201         {
4202           goto restart_sync;
4203         }
4204       }
4205     }
4206   }
4207   if (feof(f))   // Close file if at the end
4208   {
4209     (void)fclose(f);
4210   }
4211 }
4212 
4213 
4214 
4215 
4216 
4217 // Restore weather alerts so that we have a clear picture of the
4218 // current state.  Do this before we start the interfaces.  Only
4219 // reload if the log files datestamps are relatively current.
4220 //
4221 // Check timestamps on each file in turn.  If relatively
4222 // current, read them in the correct order:
4223 // wx_alert.log.3
4224 // wx_alert.log.2
4225 // wx_alert.log.1
4226 // wx.alert.log
4227 //
load_wx_alerts_from_log(void)4228 void load_wx_alerts_from_log(void)
4229 {
4230   time_t time_now;
4231   char filename[MAX_FILENAME];
4232   char logfile_path[MAX_VALUE];
4233 
4234   get_user_base_dir(LOGFILE_WX_ALERT,logfile_path, sizeof(logfile_path));
4235 
4236   time_now = sec_now();
4237 
4238   fprintf(stderr,"*** Reading WX Alert log files\n");
4239 
4240   // wx_alert.log.3
4241   xastir_snprintf(filename,
4242                   sizeof(filename),
4243                   "%s.3",
4244                   logfile_path );
4245   load_wx_alerts_from_log_working_sub(time_now, filename);
4246 
4247   // wx_alert.log.2
4248   xastir_snprintf(filename,
4249                   sizeof(filename),
4250                   "%s.2",
4251                   logfile_path );
4252   load_wx_alerts_from_log_working_sub(time_now, filename);
4253 
4254   // wx_alert.log.1
4255   xastir_snprintf(filename,
4256                   sizeof(filename),
4257                   "%s.1",
4258                   logfile_path );
4259   load_wx_alerts_from_log_working_sub(time_now, filename);
4260 
4261   // wx_alert.log
4262   xastir_snprintf(filename,
4263                   sizeof(filename),
4264                   "%s",
4265                   logfile_path );
4266   load_wx_alerts_from_log_working_sub(time_now, filename);
4267 
4268   fill_in_new_alert_entries();
4269 
4270   fprintf(stderr,"*** Done with WX Alert log files\n");
4271 }
4272 
4273 
4274 
4275 
4276 
4277 // Note that the length of "line" can be up to MAX_DEVICE_BUFFER,
4278 // which is currently set to 4096.
4279 //
log_data(char * file,char * line)4280 void log_data(char *file, char *line)
4281 {
4282   FILE *f;
4283   struct stat file_status;
4284   int reset_setuid = 0 ;
4285 
4286 
4287   // Check for "# Tickle" first, don't log it if found.
4288   // It's an idle string designed to keep the socket active.
4289   if ( (strncasecmp(line, "#Tickle", 7)==0)
4290        || (strncasecmp(line, "# Tickle", 8) == 0) )
4291   {
4292     return;
4293   }
4294   else
4295   {
4296     char temp[200];
4297     char timestring[100+1];
4298     struct tm *time_now;
4299     time_t secs_now;
4300 
4301     // Fetch the current date/time string
4302 //        get_timestamp(timestring);
4303     secs_now=sec_now();
4304 
4305     time_now = localtime(&secs_now);
4306 
4307     (void)strftime(timestring,100,"%a %b %d %H:%M:%S %Z %Y",time_now);
4308 
4309     xastir_snprintf(temp,
4310                     sizeof(temp),
4311                     "# %ld  %s",
4312                     (unsigned long)secs_now,
4313                     timestring);
4314 
4315     // Change back to the base directory
4316 
4317 // This call corrupts the "file" variable.  Commented it out as we
4318 // don't appear to need it anyway.  The complete root-anchored
4319 // path/filename are passed to us in the "file" parameter.
4320 //
4321 //        chdir(get_user_base_dir(""));
4322 
4323     // check size and rotate if too big
4324 
4325     if (stat(file, &file_status)==0)
4326     {
4327 
4328 //            if (debug_level & 1) {
4329 //                fprintf(stderr, "log_data(): logfile size: %ld \n",(long) file_status.st_size);
4330 //            }
4331 
4332       if ((file_status.st_size + strlen(temp) + strlen(line) )> MAX_LOGFILE_SIZE)
4333       {
4334         if (debug_level & 1)
4335         {
4336           fprintf(stderr, "log_data(): calling rotate_file()\n");
4337         }
4338 
4339         rotate_file(file,3);
4340       }
4341 
4342     }
4343     else
4344     {
4345       // ENOENT is ok -- we make the file below
4346       if (errno != ENOENT )
4347       {
4348         fprintf(stderr,"Couldn't stat log file '%s': %s\n",
4349                 file,strerror(errno));
4350         return;
4351       }
4352 
4353     }
4354 
4355     if (getuid() != geteuid())
4356     {
4357       reset_setuid=1;
4358       DISABLE_SETUID_PRIVILEGE;
4359     }
4360 
4361     f=fopen(file,"a");
4362     if (f!=NULL)
4363     {
4364       fprintf(f,"%s\n", temp); // Write the timestamp line
4365       fprintf(f,"%s\n",line);  // Write the data line
4366       (void)fclose(f);
4367     }
4368     else
4369     {
4370       fprintf(stderr,"Couldn't open file for appending: %s\n", file);
4371     }
4372 
4373     if(reset_setuid)
4374     {
4375       ENABLE_SETUID_PRIVILEGE;
4376     }
4377   }
4378 }
4379 
4380 
4381 
4382 
4383 
4384 //
4385 // Tactical callsign logging
4386 //
4387 // Logging function called from the Assign Tactical Call right-click
4388 // menu option and also from db.c:fill_in_tactical_call.  Each
4389 // tactical assignment is logged as one line.
4390 //
4391 // We need to check for identical callsigns in the file, deleting
4392 // lines that have the same name and adding new records to the end.
4393 // Actually  BAD IDEA!  We want to keep the history of the tactical
4394 // calls so that we can trace the changes later.
4395 //
4396 // Best method:  Look for lines containing matching callsigns and
4397 // comment them out, adding the new tactical callsign at the end of
4398 // the file.
4399 //
4400 // Do we need to use a special marker to mean NO tactical callsign
4401 // is assigned?  This is for when we had a tactical call but now
4402 // we're removing it.  The reload_tactical_calls() routine below
4403 // would need to match.  Since we're using comma-delimited files, we
4404 // can just check for an empty string instead.
4405 //
log_tactical_call(char * call_sign,char * tactical_call_sign)4406 void log_tactical_call(char *call_sign, char *tactical_call_sign)
4407 {
4408   char file[MAX_VALUE];
4409   FILE *f;
4410 
4411 
4412   // Add it to our in-memory hash so that if stations expire and
4413   // then appear again they get assigned the same tac-call.
4414   add_tactical_to_hash(call_sign, tactical_call_sign);
4415 
4416 
4417   get_user_base_dir("config/tactical_calls.log", file, sizeof(file));
4418 
4419   f=fopen(file,"a");
4420   if (f!=NULL)
4421   {
4422 
4423     if (tactical_call_sign == NULL)
4424     {
4425       fprintf(f,"%s,\n",call_sign);
4426     }
4427     else
4428     {
4429       fprintf(f,"%s,%s\n",call_sign,tactical_call_sign);
4430     }
4431 
4432     (void)fclose(f);
4433 
4434     if (debug_level & 1)
4435     {
4436       fprintf(stderr,
4437               "Saving tactical call to file: %s:%s",
4438               call_sign,
4439               tactical_call_sign);
4440     }
4441   }
4442   else
4443   {
4444     fprintf(stderr,"Couldn't open file for appending: %s\n", file);
4445   }
4446 }
4447 
4448 
4449 
4450 
4451 
4452 //
4453 // Function to load saved tactical calls back into xastir.  This
4454 // is called on startup.  This implements persistent tactical calls
4455 // across xastir restarts.
4456 //
4457 // Here we create a hash lookup and store one record for each valid
4458 // line read from the tactical calls log file.  The key for each
4459 // hash entry is the callsign-SSID.  Here we simply read them in and
4460 // create the hash.  When a new station is heard on the air, it is
4461 // checked against this hash and the tactical call field filled in
4462 // if there is a match.
4463 //
4464 // Note that the length of "line" can be up to max_device_buffer,
4465 // which is currently set to 4096.
4466 //
reload_tactical_calls(void)4467 void reload_tactical_calls(void)
4468 {
4469   char file[MAX_VALUE];
4470   FILE *f;
4471   char line[300+1];
4472 
4473 
4474   get_user_base_dir("config/tactical_calls.log", file, sizeof(file));
4475 
4476   f=fopen(file,"r");
4477   if (f!=NULL)
4478   {
4479 
4480     while (fgets(line, 300, f) != NULL)
4481     {
4482 
4483       if (debug_level & 1)
4484       {
4485         fprintf(stderr,"loading tactical calls from file: %s",line);
4486       }
4487 
4488       if (line[0] != '#')     // skip comment lines
4489       {
4490         char *ptr;
4491 
4492         // we're dealing with comma-separated files, so
4493         // break the two pieces at the comma.
4494         ptr = index(line,',');
4495 
4496         if (ptr != NULL)
4497         {
4498           char *ptr2;
4499 
4500 
4501           ptr[0] = '\0';  // terminate the callsign
4502           ptr++;  // point to the tactical callsign
4503 
4504           // check for lf
4505           ptr2 = index(ptr,'\n');
4506           if (ptr2 != NULL)
4507           {
4508             ptr2[0] = '\0';
4509           }
4510 
4511           // check for cr
4512           ptr2 = index(ptr,'\r');
4513           if (ptr2 != NULL)
4514           {
4515             ptr2[0] = '\0';
4516           }
4517 
4518           if (debug_level & 1)
4519           {
4520             fprintf(stderr, "Call=%s,\tTactical=%s\n", line, ptr);
4521           }
4522 
4523           //                   call tac-call
4524           add_tactical_to_hash(line, ptr);
4525         }
4526       }
4527     }
4528     (void)fclose(f);
4529   }
4530   else
4531   {
4532     if (debug_level & 1)
4533     {
4534       fprintf(stderr,"couldn't open file for reading: %s\n", file);
4535     }
4536   }
4537 
4538   /*
4539       if (tactical_hash) {
4540           fprintf(stderr,"Stations w/tactical calls defined: %d\n",
4541               hashtable_count(tactical_hash));
4542       }
4543   */
4544 
4545 }
4546 
4547 
4548 
4549 
4550 
4551 // Returns time in seconds since the Unix epoch.
4552 //
sec_now(void)4553 time_t sec_now(void)
4554 {
4555   time_t timenw;
4556   time_t ret;
4557 
4558   ret = time(&timenw);
4559   return(ret);
4560 }
4561 
4562 
4563 
4564 
4565 
get_time(char * time_here)4566 char *get_time(char *time_here)
4567 {
4568   struct tm *time_now;
4569   time_t timenw;
4570 
4571   (void)time(&timenw);
4572   time_now = localtime(&timenw);
4573   (void)strftime(time_here,MAX_TIME,"%m%d%Y%H%M%S",time_now);
4574   return(time_here);
4575 }
4576 
4577 
4578 
4579 
4580 
4581 ///////////////////////////////////  Utilities  ////////////////////////////////////////////////////
4582 
4583 
4584 /*
4585  *  Check for valid path and change it to TAPR format
4586  *  Add missing asterisk for stations that we must have heard via a digi
4587  *  Extract port for KAM TNCs
4588  *  Handle igate injection ID formats: "callsign-ssid,I" & "callsign-0ssid"
4589  *
4590  * TAPR-2 Format:
4591  * KC2ELS-1*>SX0PWT,RELAY,WIDE:`2`$l##>/>"4)}
4592  *
4593  * AEA Format:
4594  * KC2ELS-1*>RELAY>WIDE>SX0PWT:`2`$l##>/>"4)}
4595  */
4596 
valid_path(char * path)4597 int valid_path(char *path)
4598 {
4599   int i,len,hops,j;
4600   int type,ast,allast,ins;
4601   char ch;
4602 
4603 
4604   len  = (int)strlen(path);
4605   type = 0;       // 0: unknown, 1: AEA '>', 2: TAPR2 ',', 3: mixed
4606   hops = 1;
4607   ast  = 0;
4608   allast = 0;
4609 
4610   // There are some multi-port TNCs that deliver the port at the end
4611   // of the path. For now we discard this information. If there is
4612   // multi-port TNC support some day, we should write the port into
4613   // our database.
4614   // KAM:        /V /H
4615   // KPC-9612:   /0 /1 /2
4616   if (len > 2 && path[len-2] == '/')
4617   {
4618     ch = path[len-1];
4619     if (ch == 'V' || ch == 'H' || ch == '0' || ch == '1' || ch == '2')
4620     {
4621       path[len-2] = '\0';
4622       len  = (int)strlen(path);
4623     }
4624   }
4625 
4626 
4627   // One way of adding igate injection ID is to add "callsign-ssid,I".
4628   // We need to remove the ",I" portion so it doesn't count as another
4629   // digi here.  This should be at the end of the path.
4630   if (len > 2 && path[len-2] == ',' && path[len-1] == 'I')    // Found ",I"
4631   {
4632     //fprintf(stderr,"%s\n",path);
4633     //fprintf(stderr,"Found ',I'\n");
4634     path[len-2] = '\0';
4635     len  = (int)strlen(path);
4636     //fprintf(stderr,"%s\n\n",path);
4637   }
4638   // Now look for the same thing but with a '*' character at the end.
4639   // This should be at the end of the path.
4640   if (len > 3 && path[len-3] == ',' && path[len-2] == 'I' && path[len-1] == '*')    // Found ",I*"
4641   {
4642     //fprintf(stderr,"%s\n",path);
4643     //fprintf(stderr,"Found ',I*'\n");
4644     path[len-3] = '\0';
4645     len  = (int)strlen(path);
4646     //fprintf(stderr,"%s\n\n",path);
4647   }
4648 
4649 
4650   // Another method of adding igate injection ID is to add a '0' in front of
4651   // the SSID.  For WE7U it would change to WE7U-00, for WE7U-15 it would
4652   // change to WE7U-015.  Take out this zero so the rest of the decoding will
4653   // work.  This should be at the end of the path.
4654   // Also look for the same thing but with a '*' character at the end.
4655   if (len > 6)
4656   {
4657     for (i=len-1; i>len-6; i--)
4658     {
4659       if (path[i] == '-' && path[i+1] == '0')
4660       {
4661         //fprintf(stderr,"%s\n",path);
4662         for (j=i+1; j<len; j++)
4663         {
4664           path[j] = path[j+1];    // Shift everything left by one
4665         }
4666         len = (int)strlen(path);
4667         //fprintf(stderr,"%s\n\n",path);
4668       }
4669       // Check whether we just chopped off the '0' from "-0".
4670       // If so, chop off the dash as well.
4671       if (path[i] == '-' && path[i+1] == '\0')
4672       {
4673         //fprintf(stderr,"%s\tChopping off dash\n",path);
4674         path[i] = '\0';
4675         len = (int)strlen(path);
4676         //fprintf(stderr,"%s\n",path);
4677       }
4678       // Check for "-*", change to '*' only
4679       if (path[i] == '-' && path[i+1] == '*')
4680       {
4681         //fprintf(stderr,"%s\tChopping off dash\n",path);
4682         path[i] = '*';
4683         path[i+1] = '\0';
4684         len = (int)strlen(path);
4685         //fprintf(stderr,"%s\n",path);
4686       }
4687       // Check for "-0" or "-0*".  Change to "" or "*".
4688       if ( path[i] == '-' && path[i+1] == '0' )
4689       {
4690         //fprintf(stderr,"%s\tShifting left by two\n",path);
4691         for (j=i; j<len; j++)
4692         {
4693           path[j] = path[j+2];    // Shift everything left by two
4694         }
4695         len = (int)strlen(path);
4696         //fprintf(stderr,"%s\n",path);
4697       }
4698     }
4699   }
4700 
4701 
4702   for (i=0,j=0; i<len; i++)
4703   {
4704     ch = path[i];
4705 
4706     if (ch == '>' || ch == ',')     // found digi call separator
4707     {
4708       // We're at the start of a callsign entry in the path
4709 
4710       if (ast > 1 || (ast == 1 && i-j > 10) || (ast == 0 && (i == j || i-j > 9)))
4711       {
4712         if (debug_level & 1)
4713         {
4714           fprintf(stderr, "valid_path(): Bad Path: More than one * in call or wrong call size\n");
4715         }
4716         return(0);              // more than one asterisk in call or wrong call size
4717       }
4718       ast = 0;                    // reset local asterisk counter
4719 
4720       j = i+1;                    // set to start of next call
4721       if (ch == ',')
4722       {
4723         type |= 0x02;  // set TAPR2 flag
4724       }
4725       else
4726       {
4727         type |= 0x01;  // set AEA flag (found '>')
4728       }
4729       hops++;                     // count hops
4730     }
4731 
4732     else                            // digi call character or asterisk
4733     {
4734       // We're in the middle of a callsign entry
4735 
4736       if (ch == '*')
4737       {
4738         ast++;                  // count asterisks in call
4739         allast++;               // count asterisks in path
4740       }
4741       else if ((ch <'A' || ch > 'Z')      // Not A-Z
4742                && (ch <'a' || ch > 'z')    // Not a-z
4743                && (ch <'0' || ch > '9')    // Not 0-9
4744                && ch != '-')
4745       {
4746         // Note that Q-construct and internet callsigns can
4747         // have a-z in them, AX.25 callsigns cannot unless
4748         // they are in a 3rd-party packet.
4749 
4750         if (debug_level & 1)
4751         {
4752           fprintf(stderr, "valid_path: Bad Path: Anti-loop stuff from aprsd or lower-case chars found\n");
4753         }
4754         return(0);          // wrong character in path
4755       }
4756     }
4757   }
4758   if (ast > 1 || (ast == 1 && i-j > 10) || (ast == 0 && (i == j || i-j > 9)))
4759   {
4760     if (debug_level & 1)
4761     {
4762       fprintf(stderr, "valid_path: Bad Path: More than one * or wrong call size (2)\n");
4763     }
4764     return(0);                      // more than one asterisk or wrong call size
4765   }
4766 
4767   if (type == 0x03)
4768   {
4769     if (debug_level & 1)
4770     {
4771       fprintf(stderr, "valid_path: Bad Path: Wrong format, both > and , in path\n");
4772     }
4773     return(0);                      // wrong format, both '>' and ',' in path
4774   }
4775 
4776   if (hops > 9)                       // [APRS Reference chapter 3]
4777   {
4778     if (debug_level & 1)
4779     {
4780       fprintf(stderr, "valid_path: Bad Path: hops > 9\n");
4781     }
4782     return(0);                      // too much hops, destination + 0-8 digipeater addresses
4783   }
4784 
4785   if (type == 0x01)
4786   {
4787     int delimiters[20];
4788     int k = 0;
4789     char dest[15];
4790     char rest[100];
4791 
4792     for (i=0; i<len; i++)
4793     {
4794       if (path[i] == '>')
4795       {
4796         path[i] = ',';          // Exchange separator character
4797         delimiters[k++] = i;    // Save the delimiter indexes
4798       }
4799     }
4800 
4801     // We also need to move the destination callsign to the end.
4802     // AEA has them in a different order than TAPR-2 format.
4803     // We'll move the destination address between delimiters[0]
4804     // and [1] to the end of the string.
4805 
4806     //fprintf(stderr,"Orig. Path:%s\n",path);
4807     // Save the destination
4808     xastir_snprintf(dest,sizeof(dest),"%s",&path[delimiters[--k]+1]);
4809     dest[strlen(path) - delimiters[k] - 1] = '\0'; // Terminate it
4810     dest[14] = '\0';    // Just to make sure
4811     path[delimiters[k]] = '\0'; // Delete it from the original path
4812     //fprintf(stderr,"Destination: %s\n",dest);
4813 
4814     // TAPR-2 Format:
4815     // KC2ELS-1*>SX0PWT,RELAY,WIDE:`2`$l##>/>"4)}
4816     //
4817     // AEA Format:
4818     // KC2ELS-1*>RELAY>WIDE>SX0PWT:`2`$l##>/>"4)}
4819     //          9     15   20
4820 
4821     // We now need to insert the destination into the middle of
4822     // the string.  Save part of it in another variable first.
4823     xastir_snprintf(rest,
4824                     sizeof(rest),
4825                     "%s",
4826                     path);
4827     //fprintf(stderr,"Rest:%s\n",rest);
4828     xastir_snprintf(path,len+1,"%s,%s",dest,rest);
4829     //fprintf(stderr,"New Path:%s\n",path);
4830   }
4831 
4832   if (allast < 1)                     // try to insert a missing asterisk
4833   {
4834     ins  = 0;
4835     hops = 0;
4836 
4837     for (i=0; i<len; i++)
4838     {
4839 
4840       for (j=i; j<len; j++)               // search for separator
4841       {
4842         if (path[j] == ',')
4843         {
4844           break;
4845         }
4846       }
4847 
4848       if (hops > 0 && (j - i) == 5)       // WIDE3
4849       {
4850         if (  path[ i ] == 'W' && path[i+1] == 'I' && path[i+2] == 'D'
4851               && path[i+3] == 'E' && path[i+4] >= '0' && path[i+4] <= '9')
4852         {
4853           ins = j;
4854         }
4855       }
4856 
4857       /*
4858       Don't do this!  It can mess up relay/wide1-1 digipeating by adding
4859       an asterisk later in the path than the first unused digi.
4860                   if (hops > 0 && (j - i) == 7) {     // WIDE3-2
4861                       if (  path[ i ] == 'W' && path[i+1] == 'I' && path[i+2] == 'D'
4862                          && path[i+3] == 'E' && path[i+4] >= '0' && path[i+4] <= '9'
4863                          && path[i+5] == '-' && path[i+6] >= '0' && path[i+6] <= '9'
4864                          && (path[i+4] != path[i+6]) ) {
4865                           ins = j;
4866                       }
4867                   }
4868       */
4869 
4870       if (hops > 0 && (j - i) == 6)       // TRACE3
4871       {
4872         if (  path[ i ] == 'T' && path[i+1] == 'R' && path[i+2] == 'A'
4873               && path[i+3] == 'C' && path[i+4] == 'E'
4874               && path[i+5] >= '0' && path[i+5] <= '9')
4875         {
4876           if (hops == 1)
4877           {
4878             ins = j;
4879           }
4880           else
4881           {
4882             ins = i-1;
4883           }
4884         }
4885       }
4886 
4887       /*
4888       Don't do this!  It can mess up relay/wide1-1 digipeating by adding
4889       an asterisk later in the path than the first unused digi.
4890                   if (hops > 0 && (j - i) == 8) {     // TRACE3-2
4891                       if (  path[ i ] == 'T' && path[i+1] == 'R' && path[i+2] == 'A'
4892                          && path[i+3] == 'C' && path[i+4] == 'E' && path[i+5] >= '0'
4893                          && path[i+5] <= '9' && path[i+6] == '-' && path[i+7] >= '0'
4894                          && path[i+7] <= '9' && (path[i+5] != path[i+7]) ) {
4895                           if (hops == 1)
4896                               ins = j;
4897                           else
4898                               ins = i-1;
4899                       }
4900                   }
4901       */
4902 
4903       hops++;
4904       i = j;                      // skip to start of next call
4905     }
4906     if (ins > 0)
4907     {
4908       for (i=len; i>=ins; i--)
4909       {
4910         path[i+1] = path[i];    // generate space for '*'
4911         // we work on a separate path copy which is long enough to do it
4912       }
4913       path[ins] = '*';            // and insert it
4914     }
4915   }
4916   return(1);  // Path is good
4917 }
4918 
4919 
4920 
4921 
4922 
4923 /*
4924  *  Check for a valid AX.25 call
4925  *      Valid calls consist of up to 6 uppercase alphanumeric characters
4926  *      plus optional SSID (four-bit integer)       [APRS Reference, AX.25 Reference]
4927  *
4928  * We originally required at least one number so-as to get rid of calls like
4929  * "NOCALL", but got rid of that code.
4930  */
valid_call(char * call)4931 int valid_call(char *call)
4932 {
4933   int len, ok;
4934   int i, del, has_chr;
4935   char c;
4936 
4937   has_chr = 0;
4938   ok      = 1;
4939   len = (int)strlen(call);
4940 
4941   if (len == 0)
4942   {
4943     return(0);  // wrong size
4944   }
4945 
4946   while (call[0]=='c' && call[1]=='m' && call[2]=='d' && call[3]==':')
4947   {
4948     // Erase TNC prompts from beginning of callsign.  This may
4949     // not be the right place to do this, but it came in handy
4950     // here, so that's where I put it. -- KB6MER
4951 
4952     if (debug_level & 1)
4953     {
4954       char filtered_data[MAX_LINE_SIZE+1];
4955 
4956       xastir_snprintf(filtered_data,
4957                       sizeof(filtered_data),
4958                       "%s",
4959                       call);
4960       makePrintable(filtered_data);
4961       fprintf(stderr,"valid_call: Command Prompt removed from: %s",
4962               filtered_data);
4963     }
4964 
4965     for(i=0; call[i+4]; i++)
4966     {
4967       call[i]=call[i+4];
4968     }
4969 
4970     call[i++]=0;
4971     call[i++]=0;
4972     call[i++]=0;
4973     call[i++]=0;
4974     len=strlen(call);
4975 
4976     if (debug_level & 1)
4977     {
4978       char filtered_data[MAX_LINE_SIZE+1];
4979 
4980       xastir_snprintf(filtered_data,
4981                       sizeof(filtered_data),
4982                       "%s",
4983                       call);
4984       makePrintable(filtered_data);
4985       fprintf(stderr," result: %s", filtered_data);
4986     }
4987   }
4988 
4989   if (len > 9)
4990   {
4991     return(0);  // Too long for valid call (6-2 max e.g. KB6MER-12)
4992   }
4993 
4994   del = 0;
4995   for (i=len-2; ok && i>0 && i>=len-3; i--)   // search for optional SSID
4996   {
4997     if (call[i] =='-')
4998     {
4999       del = i;  // found the delimiter
5000     }
5001   }
5002   if (del)                                    // we have a SSID, so check it
5003   {
5004     if (len-del == 2)                       // 2 char SSID
5005     {
5006       if (call[del+1] < '1' || call[del+1] > '9')                         //  -1 ...  -9
5007       {
5008         del = 0;
5009       }
5010     }
5011     else                                    // 3 char SSID
5012     {
5013       if (call[del+1] != '1' || call[del+2] < '0' || call[del+2] > '5')   // -10 ... -15
5014       {
5015         del = 0;
5016       }
5017     }
5018   }
5019 
5020   if (del)
5021   {
5022     len = del;  // length of base call
5023   }
5024 
5025   for (i=0; ok && i<len; i++)                 // check for uppercase alphanumeric
5026   {
5027     c = call[i];
5028 
5029     if (c >= 'A' && c <= 'Z')
5030     {
5031       has_chr = 1;  // we need at least one char
5032     }
5033     else if (c >= '0' && c <= '9')
5034     {
5035       // We originally required at least one number
5036     }
5037     else
5038     {
5039       ok = 0;  // wrong character in call
5040     }
5041   }
5042 
5043 //    if (!has_num || !has_chr)                 // with this we also discard NOCALL etc.
5044   if (!has_chr)
5045   {
5046     ok = 0;
5047   }
5048 
5049   ok = (ok && strcmp(call,"NOCALL") != 0);    // check for errors
5050   ok = (ok && strcmp(call,"ERROR!") != 0);
5051   ok = (ok && strcmp(call,"WIDE")   != 0);
5052   ok = (ok && strcmp(call,"RELAY")  != 0);
5053   ok = (ok && strcmp(call,"MAIL")   != 0);
5054 
5055   return(ok);
5056 }
5057 
5058 
5059 
5060 
5061 
5062 /*
5063  *  Check for a valid internet name.
5064  *  Accept darned-near anything here as long as it is the proper
5065  *  length and printable.
5066  */
valid_inet_name(char * name,char * info,char * origin,int origin_size)5067 int valid_inet_name(char *name, char *info, char *origin, int origin_size)
5068 {
5069   int len, i, ok, oknws, okbom;
5070   char *ptr;
5071 
5072   len = (int)strlen(name);
5073 
5074   if (len > 9 || len == 0)            // max 9 printable ASCII characters
5075   {
5076     return(0);  // wrong size
5077   }
5078 
5079   for (i=0; i<len; i++)
5080     if (!isprint((int)name[i]))
5081     {
5082       return(0);  // not printable
5083     }
5084 
5085   // Modifies "origin" if a match found
5086   //
5087   if (len >= 5 && strncmp(name,"aprsd",5) == 0)
5088   {
5089     xastir_snprintf(origin, origin_size, "INET");
5090     origin[4] = '\0';   // Terminate it
5091     return(1);                      // aprsdXXXX is ok
5092   }
5093 
5094   // Modifies "origin" if a match found
5095   if (len == 6)                       // check for NWS or BOM
5096   {
5097     ok = 1;
5098     oknws = 0;
5099     okbom = 0;
5100     for (i=0; i<6; i++)
5101       if (name[i] <'A' || name[i] > 'Z')  // 6 uppercase characters
5102       {
5103         ok = 0;
5104       }
5105     ok = ok && (info != NULL);      // check if we can test info
5106     if (ok)
5107     {
5108       ptr = strstr(info,":NWS-"); // "NWS-" in info field (non-compressed alert)
5109       oknws = (ptr != NULL);
5110 
5111       if (!oknws)
5112       {
5113         ptr = strstr(info,":NWS_"); // "NWS_" in info field (compressed alert)
5114         oknws = (ptr != NULL);
5115       }
5116 
5117       // If we've got here, it's not an NWS message, let's see if its a BOM message
5118       if (!oknws)
5119       {
5120         ptr = strstr(info,":BOM-"); // "BOM-" in info field (compressed alert)
5121         okbom = (ptr != NULL);
5122       }
5123 
5124       if (!okbom)
5125       {
5126         ptr = strstr(info,":BOM_"); // "BOM_" in info field (compressed alert)
5127         okbom = (ptr != NULL);
5128       }
5129     }
5130 
5131     // Depending on whether we had an NWS or BOM message, se the origin appropriately
5132     if (oknws)
5133     {
5134       xastir_snprintf(origin, origin_size, "INET-NWS");
5135       origin[8] = '\0';
5136       return(1);                      // weather alerts
5137     }
5138     if (okbom)
5139     {
5140       xastir_snprintf(origin, origin_size, "INET-BOM");
5141       origin[8] = '\0';
5142       return(1);                      // weather alerts
5143     }
5144   }
5145 
5146   return(1);  // Accept anything else if we get to this point in
5147   // the code.  After all, the message came from the
5148   // internet, not from RF.
5149 }
5150 
5151 
5152 
5153 
5154 
5155 /*
5156  *  Keep track of last six digis that echo my transmission
5157  */
upd_echo(char * path)5158 void upd_echo(char *path)
5159 {
5160   int i,j,len;
5161 
5162   if (echo_digis[5][0] != '\0')
5163   {
5164     for (i=0; i<5; i++)
5165     {
5166       xastir_snprintf(echo_digis[i],
5167                       MAX_CALLSIGN+1,
5168                       "%s",
5169                       echo_digis[i+1]);
5170 
5171     }
5172     echo_digis[5][0] = '\0';
5173   }
5174   for (i=0,j=0; i < (int)strlen(path); i++)
5175   {
5176     if (path[i] == '*')
5177     {
5178       break;
5179     }
5180     if (path[i] == ',')
5181     {
5182       j=i;
5183     }
5184   }
5185   if (j > 0)
5186   {
5187     j++;  // first char of call
5188   }
5189   if (i > 0 && i-j <= 9)
5190   {
5191     len = i-j;
5192     for (i=0; i<5; i++)     // look for free entry
5193     {
5194       if (echo_digis[i][0] == '\0')
5195       {
5196         break;
5197       }
5198     }
5199     substr(echo_digis[i],path+j,len);
5200   }
5201 }
5202 
5203 
5204 
5205 
5206 
5207 // dl9sau:
5208 // I liked to have xastir to compute the locator along with the normal coordinates.
5209 // The algorithm derives from dk5sg's util/qth.c (wampes)
5210 //
5211 // This computes Maidenhead grid coordinates and is used for both
5212 // the status line ("text2" widget) and the Coordinate Calculator at
5213 // present.
5214 //
sec_to_loc(long longitude,long latitude)5215 char *sec_to_loc(long longitude, long latitude)
5216 {
5217   static char buf[7];
5218   char *loc = buf;
5219 
5220   // database.h:    long coord_lon;                     // Xastir coordinates 1/100 sec, 0 = 180�W
5221   // database.h:    long coord_lat;                     // Xastir coordinates 1/100 sec, 0 =  90�N
5222 
5223   longitude /= 100L;
5224   latitude  =  2L * 90L * 3600L - 1L - (latitude / 100L);
5225 
5226   *loc++ = (char) (longitude / 72000L + 'A');
5227   longitude = longitude % 72000L;
5228   *loc++ = (char) (latitude  / 36000L + 'A');
5229   latitude  = latitude %  36000L;
5230   *loc++ = (char) (longitude /  7200L + '0');
5231   longitude = longitude %  7200L;
5232   *loc++ = (char) (latitude  /  3600L + '0');
5233   latitude  = latitude %   3600L;
5234   *loc++ = (char) (longitude /   300L + 'a');
5235   *loc++ = (char) (latitude  /   150L + 'a');
5236   *loc   = 0;   // Terminate the string
5237   return buf;
5238 }
5239 
5240 
5241 
5242 
5243 
5244 /*
5245  *  Substring function WITH a terminating NULL char, needs a string of at least size+1
5246  */
substr(char * dest,char * src,int size)5247 void substr(char *dest, char *src, int size)
5248 {
5249   memcpy(dest, src, size+1);
5250   dest[size] = '\0';  // Terminate string
5251 }
5252 
5253 
5254 
5255 
5256 
5257 
to_upper(char * data)5258 char *to_upper(char *data)
5259 {
5260   int i;
5261 
5262   for(i=strlen(data)-1; i>=0; i--)
5263     if(islower((int)data[i]))
5264     {
5265       data[i]=toupper((int)data[i]);
5266     }
5267 
5268   return(data);
5269 }
5270 
5271 
5272 
5273 
5274 
to_lower(char * data)5275 char *to_lower(char *data)
5276 {
5277   int i;
5278 
5279   for(i=strlen(data)-1; i>=0; i--)
5280     if(isupper((int)data[i]))
5281     {
5282       data[i]=tolower((int)data[i]);
5283     }
5284 
5285   return(data);
5286 }
5287 
5288 
5289 
5290 
5291 
is_num_chr(char ch)5292 int is_num_chr(char ch)
5293 {
5294   return((int)isdigit(ch));
5295 }
5296 
5297 
5298 
5299 
5300 
5301 
is_num_or_sp(char ch)5302 int is_num_or_sp(char ch)
5303 {
5304   return((int)((ch >= '0' && ch <= '9') || ch == ' '));
5305 }
5306 
5307 
5308 
5309 
5310 
5311 
is_xnum_or_dash(char * data,int max)5312 int is_xnum_or_dash(char *data, int max)
5313 {
5314   int i;
5315   int ok;
5316 
5317   ok=1;
5318   for(i=0; i<max; i++)
5319     if(!(isxdigit((int)data[i]) || data[i]=='-'))
5320     {
5321       ok=0;
5322       break;
5323     }
5324 
5325   return(ok);
5326 }
5327 
5328 
5329 
5330 
5331 
5332 // not used
is_num_stuff(char * data,int max)5333 int is_num_stuff(char *data, int max)
5334 {
5335   int i;
5336   int ok;
5337 
5338   ok=1;
5339   for(i=0; i<max; i++)
5340     if(!(isdigit((int)data[i]) || data[i]=='-' || data[i]=='+' || data[i]=='.'))
5341     {
5342       ok=0;
5343       break;
5344     }
5345 
5346   return(ok);
5347 }
5348 
5349 
5350 
5351 
5352 
5353 // not used
is_num(char * data)5354 int is_num(char *data)
5355 {
5356   int i;
5357   int ok;
5358 
5359   ok=1;
5360   for(i=strlen(data)-1; i>=0; i--)
5361     if(!isdigit((int)data[i]))
5362     {
5363       ok=0;
5364       break;
5365     }
5366 
5367   return(ok);
5368 }
5369 
5370 
5371 
5372 
5373 
5374 //--------------------------------------------------------------------
5375 //Removes all control codes ( < 1Ch ) from a string and set the 8th bit to zero
removeCtrlCodes(char * cp)5376 void removeCtrlCodes(char *cp)
5377 {
5378   int i,j;
5379   int len = (int)strlen(cp);
5380   unsigned char *ucp = (unsigned char *)cp;
5381 
5382   for (i=0, j=0; i<=len; i++)
5383   {
5384     ucp[i] &= 0x7f;                 // Clear 8th bit
5385     if ( (ucp[i] >= (unsigned char)0x1c)           // Check for printable plus the Mic-E codes
5386          || ((char)ucp[i] == '\0') )   // or terminating 0
5387     {
5388       ucp[j++] = ucp[i] ;  // Copy to temp if printable
5389     }
5390   }
5391 }
5392 
5393 
5394 
5395 
5396 
5397 //--------------------------------------------------------------------
5398 //Removes all control codes ( <0x20 or >0x7e ) from a string, including
5399 // CR's, LF's, tab's, etc.
5400 //
makePrintable(char * cp)5401 void makePrintable(char *cp)
5402 {
5403   int i,j;
5404   int len = (int)strlen(cp);
5405   unsigned char *ucp = (unsigned char *)cp;
5406 
5407   for (i=0, j=0; i<=len; i++)
5408   {
5409     ucp[i] &= 0x7f;                 // Clear 8th bit
5410     if ( ((ucp[i] >= (unsigned char)0x20) && (ucp[i] <= (unsigned char)0x7e))
5411          || ((char)ucp[i] == '\0') )     // Check for printable or terminating 0
5412     {
5413       ucp[j++] = ucp[i] ;  // Copy to (possibly) new location if printable
5414     }
5415   }
5416 }
5417 
5418 
5419 
5420 
5421 
5422 
5423 //----------------------------------------------------------------------
5424 // Implements safer mutex locking.  Posix-compatible mutex's will lock
5425 // up a thread if lock's/unlock's aren't in perfect pairs.  They also
5426 // allow one thread to unlock a mutex that was locked by a different
5427 // thread.
5428 // Remember to keep this thread-safe.  Need dynamic storage (no statics)
5429 // and thread-safe calls.
5430 //
5431 // In order to keep track of process ID's in a portable way, we probably
5432 // can't use the process ID stored inside the pthread_mutex_t struct.
5433 // There's no easy way to get at it.  For that reason we'll create
5434 // another struct that contains both the process ID and a pointer to
5435 // the pthread_mutex_t, and pass pointers to those structs around in
5436 // our programs instead.  The new struct is "xastir_mutex".
5437 //
5438 
5439 
5440 // Function to initialize the new xastir_mutex
5441 // objects before use.
5442 //
init_critical_section(xastir_mutex * lock)5443 void init_critical_section(xastir_mutex *lock)
5444 {
5445 #ifdef MUTEX_DEBUG
5446   pthread_mutexattr_t attr;
5447 
5448   // Note that this stuff is not POSIX, so is considered non-portable.
5449   // Exists in Linux Threads implementation only?
5450 
5451 # ifdef __LSB__
5452   // LSB VERSION (Linux Standard Base)
5453   // Note that we _must_ use the newer pthread function in this
5454   // case per LSB specs.
5455   (void)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
5456 # else   // __LSB__
5457   // NON_LSB VERSION
5458   // Check first for newer pthread function
5459 #  ifdef HAVE_PTHREAD_MUTEXATTR_SETTYPE
5460   (void)pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
5461 #  else
5462   // Use older, deprecated pthread function
5463   (void)pthread_mutexattr_setkind_np(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
5464 #  endif  // HAVE_PTHREAD_MUTEXATTR_SETTYPE
5465 # endif  // __LSB__
5466 
5467   (void)pthread_mutexattr_init(&attr);
5468   (void)pthread_mutex_init(&lock->lock, &attr);
5469 #else   // MUTEX_DEBUG
5470   (void)pthread_mutex_init(&lock->lock, NULL); // Set to default POSIX-compatible type
5471 #endif  // MUTEX_DEBUG
5472 
5473   lock->threadID = 0;  // No thread has locked it yet
5474 }
5475 
5476 
5477 
5478 
5479 
5480 // Function which uses xastir_mutex objects to lock a
5481 // critical shared section of code or variables.  Makes
5482 // sure that only one thread can access the critical section
5483 // at a time.  If there are no problems, it returns zero.
5484 //
begin_critical_section(xastir_mutex * lock,char * text)5485 int begin_critical_section(xastir_mutex *lock, char *text)
5486 {
5487   pthread_t calling_thread;
5488   int problems;
5489 #ifdef MUTEX_DEBUG
5490   int ret;
5491 #endif  // MUTEX_DEBUG
5492 
5493   problems = 0;
5494 
5495   // Note that /usr/include/bits/pthreadtypes.h has the
5496   // definition for pthread_mutex_t, and it includes the
5497   // owner thread number:  _pthread_descr pthread_mutex_t.__m_owner
5498   // It's probably not portable to use it though.
5499 
5500   // Get thread number we're currently running under
5501   calling_thread = pthread_self();
5502 
5503   if (pthread_equal( lock->threadID, calling_thread ))
5504   {
5505     // We tried to lock something that we already have the lock on.
5506     fprintf(stderr,"%s:Warning:This thread already has the lock on this resource\n", text);
5507     problems++;
5508     return(0);  // Return the OK code.  Skip trying the lock.
5509   }
5510 
5511   //if (lock->threadID != 0)
5512   //{
5513   // We tried to lock something that another thread has the lock on.
5514   // This is normal operation.  The pthread_mutex_lock call below
5515   // will put our process to sleep until the mutex is unlocked.
5516   //}
5517 
5518   // Try to lock it.
5519 #ifdef MUTEX_DEBUG
5520   // Instead of blocking the thread, we'll loop here until there's
5521   // no deadlock, printing out status as we go.
5522   do
5523   {
5524     ret = pthread_mutex_lock(&lock->lock);
5525     if (ret == EDEADLK)     // Normal operation.  At least two threads want this lock.
5526     {
5527       fprintf(stderr,"%s:pthread_mutex_lock(&lock->lock) == EDEADLK\n", text);
5528       sched_yield();  // Yield the cpu to another thread
5529     }
5530     else if (ret == EINVAL)
5531     {
5532       fprintf(stderr,"%s:pthread_mutex_lock(&lock->lock) == EINVAL\n", text);
5533       fprintf(stderr,"Forgot to initialize the mutex object...\n");
5534       problems++;
5535       sched_yield();  // Yield the cpu to another thread
5536     }
5537   }
5538   while (ret == EDEADLK);
5539 
5540   if (problems)
5541   {
5542     fprintf(stderr,"Returning %d to calling thread\n", problems);
5543   }
5544 #else   // MUTEX_DEBUG
5545   pthread_mutex_lock(&lock->lock);
5546 #endif  // MUTEX_DEBUG
5547 
5548   // Note:  This can only be set AFTER we already have the mutex lock.
5549   // Otherwise there may be contention with other threads for setting
5550   // this variable.
5551   //
5552   lock->threadID = calling_thread;    // Save the threadID that we used
5553   // to lock this resource
5554 
5555   return(problems);
5556 }
5557 
5558 
5559 
5560 
5561 
5562 // Function which ends the locking of a critical section
5563 // of code.  If there are no problems, it returns zero.
5564 //
end_critical_section(xastir_mutex * lock,char * text)5565 int end_critical_section(xastir_mutex *lock, char *text)
5566 {
5567   pthread_t calling_thread;
5568   int problems;
5569 #ifdef MUTEX_DEBUG
5570   int ret;
5571 #endif  // MUTEX_DEBUG
5572 
5573   problems = 0;
5574 
5575   // Get thread number we're currently running under
5576   calling_thread = pthread_self();
5577 
5578   if (lock->threadID == 0)
5579   {
5580     // We have a problem.  This resource hasn't been locked.
5581     fprintf(stderr,"%s:Warning:Trying to unlock a resource that hasn't been locked:%ld\n",
5582             text,
5583             (long int)lock->threadID);
5584     problems++;
5585   }
5586 
5587   if (! pthread_equal( lock->threadID, calling_thread ))
5588   {
5589     // We have a problem.  We just tried to unlock a mutex which
5590     // a different thread originally locked.  Not good.
5591     fprintf(stderr,"%s:Trying to unlock a resource that a different thread locked originally: %ld:%ld\n",
5592             text,
5593             (long int)lock->threadID,
5594             (long int)calling_thread);
5595     problems++;
5596   }
5597 
5598 
5599   // We need to clear this variable BEFORE we unlock the mutex, 'cuz
5600   // other threads might be waiting to lock the mutex.
5601   //
5602   lock->threadID = 0; // We're done with this lock.
5603 
5604 
5605   // Try to unlock it.  Compare the thread identifier we used before to make
5606   // sure we should.
5607 
5608 #ifdef MUTEX_DEBUG
5609   // EPERM error: We're trying to unlock something that we don't have a lock on.
5610   ret = pthread_mutex_unlock(&lock->lock);
5611 
5612   if (ret == EPERM)
5613   {
5614     fprintf(stderr,"%s:pthread_mutex_unlock(&lock->lock) == EPERM\n", text);
5615     problems++;
5616   }
5617   else if (ret == EINVAL)
5618   {
5619     fprintf(stderr,"%s:pthread_mutex_unlock(&lock->lock) == EINVAL\n", text);
5620     fprintf(stderr,"Someone forgot to initialize the mutex object\n");
5621     problems++;
5622   }
5623 
5624   if (problems)
5625   {
5626     fprintf(stderr,"Returning %d to calling thread\n", problems);
5627   }
5628 #else   // MUTEX_DEBUG
5629   (void)pthread_mutex_unlock(&lock->lock);
5630 #endif  // MUTEX_DEBUG
5631 
5632   return(problems);
5633 }
5634 
5635 
5636 
5637 
5638 
5639 #ifdef TIMING_DEBUG
time_mark(int start)5640 void time_mark(int start)
5641 {
5642   static struct timeval t_start;
5643   struct timeval t_cur;
5644   long sec, usec;
5645 
5646   if (start)
5647   {
5648     gettimeofday(&t_start, NULL);
5649     fprintf(stderr,"\nstart: 0.000000s");
5650   }
5651   else
5652   {
5653     gettimeofday(&t_cur, NULL);
5654     sec  = t_cur.tv_sec  - t_start.tv_sec;
5655     usec = t_cur.tv_usec - t_start.tv_usec;
5656     if (usec < 0)
5657     {
5658       sec--;
5659       usec += 1000000;
5660     }
5661     fprintf(stderr,"time:  %ld.%06lds\n", sec, usec);
5662   }
5663 }
5664 #endif  // TIMING_DEBUG
5665 
5666 
5667 
5668 
5669 
5670 // Function which adds commas to callsigns (and other abbreviations?)
5671 // in order to make the text sound better when run through a Text-to-
5672 // Speech system.  We try to change only normal amateur callsigns.
5673 // If we find a number in the text before a dash is found, we
5674 // consider it to be a normal callsign.  We don't add commas to the
5675 // SSID portion of a call.
spell_it_out(char * text,int max_length)5676 void spell_it_out(char *text, int max_length)
5677 {
5678   char buffer[2000];
5679   int i = 0;
5680   int j = 0;
5681   int number_found_before_dash = 0;
5682   int dash_found = 0;
5683 
5684   while (text[i] != '\0')
5685   {
5686 
5687     if (text[i] == '-')
5688     {
5689       dash_found++;
5690     }
5691 
5692     if (is_num_chr(text[i]) && !dash_found)
5693     {
5694       number_found_before_dash++;
5695     }
5696 
5697     buffer[j++] = text[i];
5698 
5699     if (!dash_found) // Don't add commas to SSID
5700     {
5701       buffer[j++] = ',';
5702     }
5703 
5704     i++;
5705   }
5706   buffer[j] = '\0';
5707 
5708   // Only use the new string if it kind'a looks like a callsign
5709   if (number_found_before_dash)
5710   {
5711     xastir_snprintf(text, max_length, "%s", buffer);
5712   }
5713 }
5714 
5715 
5716 
5717 
5718 
5719 #define kKey 0x73e2 // This is the seed for the key
5720 
5721 
5722 
5723 
5724 
doHash(char * theCall)5725 static short doHash(char *theCall)
5726 {
5727   char rootCall[10]; // need to copy call to remove ssid from parse
5728   char *p1 = rootCall;
5729   short hash;
5730   short i,len;
5731   char *ptr = rootCall;
5732 
5733   while ((*theCall != '-') && (*theCall != '\0'))
5734   {
5735     *p1++ = toupper((int)(*theCall++));
5736   }
5737   *p1 = '\0';
5738 
5739   hash = kKey; // Initialize with the key value
5740   i = 0;
5741   len = (short)strlen(rootCall);
5742 
5743   while (i<len)  // Loop through the string two bytes at a time
5744   {
5745     hash ^= (unsigned char)(*ptr++)<<8; // xor high byte with accumulated hash
5746     hash ^= (*ptr++); // xor low byte with accumulated hash
5747     i += 2;
5748   }
5749   return (short)(hash & 0x7fff); // mask off the high bit so number is always positive
5750 }
5751 
5752 
5753 
5754 
5755 
checkHash(char * theCall,short theHash)5756 short checkHash(char *theCall, short theHash)
5757 {
5758   return (short)(doHash(theCall) == theHash);
5759 }
5760 
5761 
5762 
5763 
5764 
5765 // Breaks up a string into substrings using an arbitrary character
5766 // as the delimiter.  Makes each entry in the array of char ptrs
5767 // point to one substring.  Modifies incoming string and cptr[]
5768 // array.  Send a character constant string to it and you'll get an
5769 // instant segfault (the function can't modify a char constant
5770 // string).  Use this function instead of strtok().
5771 //
split_string(char * data,char * cptr[],int max,char search_char)5772 void split_string( char *data, char *cptr[], int max, char search_char )
5773 {
5774   int ii;
5775   char *temp;
5776   char *current = data;
5777 
5778 
5779   // NULL each char pointer
5780   for (ii = 0; ii < max; ii++)
5781   {
5782     cptr[ii] = NULL;
5783   }
5784 
5785   // Save the beginning substring address
5786   cptr[0] = current;
5787 
5788   for (ii = 1; ii < max; ii++)
5789   {
5790     temp = strchr(current,search_char);  // Find next search character
5791 
5792     if(!temp)   // No search characters found
5793     {
5794       return; // All done with string
5795     }
5796 
5797     // Store pointer to next substring in array
5798     cptr[ii] = &temp[1];
5799     current  = &temp[1];
5800 
5801     // Overwrite search character  with end-of-string char and bump
5802     // pointer by one.
5803     temp[0] = '\0';
5804   }
5805 }
5806 
5807 
5808 
5809 
5810 
5811 // Function to check for proper relations between the n-N
5812 // numbers.  Called from check_unproto_path() function below.
5813 //
check_n_N(int prev,int last)5814 int check_n_N (int prev, int last)
5815 {
5816   int bad_path = 0;
5817 
5818 
5819   if (prev < last)
5820   {
5821     // Whoa, n < N, not good
5822     bad_path = 1;
5823   }
5824   else if (last > MAX_WIDES)
5825   {
5826     // N is greater than MAX_WIDES
5827     bad_path = 1;
5828   }
5829   else if (prev > MAX_WIDES)
5830   {
5831     // n is greater than MAX_WIDES
5832     bad_path = 1;
5833   }
5834   else if (last == 0)
5835   {
5836     // N is 0, it's a used-up digipeater slot
5837     bad_path = 1;
5838   }
5839   if (debug_level & 1 && bad_path)
5840   {
5841     fprintf(stderr,"n-N wrong\n");
5842   }
5843 
5844   return(bad_path);
5845 }
5846 
5847 
5848 
5849 
5850 
5851 // This function checks to make sure an unproto path falls within
5852 // the socially acceptable values, such as only one RELAY or
5853 // WIDE1-1 which may appear only as the first option, use of WIDE4-4
5854 // and higher should be questioned, etc.
5855 //
5856 // "MAX_WIDES" is defined in util.h
5857 //
5858 // Returns:
5859 //  1 = bad path
5860 //  0 = good path
5861 //
check_unproto_path(char * data)5862 int check_unproto_path ( char *data )
5863 {
5864   char *tmpdata;
5865   char *tmp;
5866   char *ViaCalls[10];
5867   int bad_path, ii, have_relay, have_wide, have_widen;
5868   int have_trace, have_tracen;
5869   int lastp = 0;
5870   int prevp = 0;
5871   int last = 0;
5872   int prev = 0;
5873   int total_digi_length = 0;
5874 
5875 
5876   if (debug_level & 1)
5877   {
5878     fprintf(stderr, "Input string: %s\n", data);
5879   }
5880 
5881   bad_path = ii = have_relay = have_wide = 0;
5882   have_widen = have_trace = have_tracen = 0;
5883 
5884   // Remember to free() tmpdata before we return
5885 #ifdef HAVE_STRNDUP
5886   tmpdata = (char *)strndup(data, strlen(data));
5887 #else
5888   tmpdata = (char *)strdup(data);
5889 #endif
5890   (void)to_upper(tmpdata);
5891   if ((tmp = strchr(tmpdata, '/')))
5892   {
5893     *tmp ='\0';  // Only check VHF portion of path
5894   }
5895   split_string(tmpdata, ViaCalls, 10, ',');
5896 
5897   for (ii = 0; ii < 10; ii++)
5898   {
5899     lastp = 0;
5900     prevp = 0;
5901     last = 0;
5902     prev = 0;
5903 
5904 
5905     // Check for empty string in this slot
5906     if (ViaCalls[ii] == NULL)
5907     {
5908       // We're done.  Exit the loop with whatever flags were
5909       // set in previous iterations.
5910       if (debug_level & 1)
5911       {
5912         fprintf(stderr,"NULL string, slot %d\n", ii);
5913       }
5914       break;
5915     }
5916     else
5917     {
5918       if (debug_level & 1)
5919       {
5920         fprintf(stderr,"%s string, slot %d\n", ViaCalls[ii], ii);
5921       }
5922     }
5923 
5924     lastp = strlen(ViaCalls[ii]) - 1;
5925     prevp = lastp - 2;
5926     // Snag the N character, convert to integer
5927     last  = ViaCalls[ii][lastp] - 0x30;
5928 
5929     if (prevp >= 0)
5930     {
5931       // Snag the n character, convert to integer
5932       prev = ViaCalls[ii][prevp] - 0x30;
5933     }
5934 
5935     // Check whether RELAY appears later in the path
5936     if (!strncmp(ViaCalls[ii], "RELAY", 5))
5937     {
5938       have_relay++;
5939       total_digi_length++;
5940       if (ii != 0)
5941       {
5942         // RELAY should not appear after the first item in a
5943         // path!
5944         bad_path = 1;
5945         if (debug_level & 1)
5946         {
5947           fprintf(stderr,"RELAY appears later in the path\n");
5948         }
5949         break;
5950       }
5951     }
5952 
5953     // Check whether WIDE1-1 appears later in the path (the new
5954     // "RELAY")
5955     else if (!strncmp(ViaCalls[ii], "WIDE1-1", 7))
5956     {
5957       have_relay++;
5958       total_digi_length++;
5959       if (ii != 0)
5960       {
5961         // WIDE1-1 should not appear after the first item in
5962         // a path!
5963         bad_path = 1;
5964         if (debug_level & 1)
5965         {
5966           fprintf(stderr,"WIDE1-1 appears later in the path\n");
5967         }
5968         break;
5969       }
5970     }
5971 
5972     // Check whether WIDE2-1 appears first in the path.  This is
5973     // fine, but don't trigger an error later because of another
5974     // WIDEn-N after an initial WIDE2-1.  This is to allow paths
5975     // like "WIDE2-1,WIDE-2-2" or "WIDE2-1,MD2-2"
5976     else if ( (ii == 0) && (!strncmp(ViaCalls[ii], "WIDE2-1", 7)) )
5977     {
5978       total_digi_length++;
5979       if (debug_level & 1)
5980       {
5981         fprintf(stderr,"Found initial WIDE2-1 (a good thing)\n");
5982       }
5983     }
5984 
5985     // Check for WIDE/TRACE/WIDEn-N/TRACEn-N in the path
5986     else if (strstr(ViaCalls[ii], "WIDE") || strstr(ViaCalls[ii], "TRACE"))
5987     {
5988       // This is some variant of WIDE or TRACE, could be
5989       // WIDE/WIDEn-N/TRACE/TRACEn-N
5990       int is_wide = 0;
5991 
5992 
5993       if (strstr(ViaCalls[ii], "WIDE"))
5994       {
5995         is_wide++;
5996       }
5997 
5998       // Check for WIDEn-N or TRACEn-N
5999       if (strstr(ViaCalls[ii], "-"))
6000       {
6001         // This is a WIDEn-N or TRACEn-N callsign
6002 
6003 
6004         // Here we are adding the unused portion of the
6005         // WIDEn-N/TRACEn-N to the total_digi_length
6006         // variable.  We use the unused portion because that
6007         // way we're not fooled if people start with a
6008         // number for N that is higher/lower than n.  The
6009         // initial thought was to grab the higher of n or N,
6010         // but those lines are commented out here.
6011         //
6012         //if (last > prev)
6013         total_digi_length += last;
6014         //else
6015         //    total_digi_length += prev;
6016 
6017 
6018         // Check for previous WIDEn-N/TRACEn-N
6019         if (have_widen || have_tracen)
6020         {
6021           // Already have a large area via
6022           bad_path = 1;
6023           if (debug_level & 1)
6024           {
6025             fprintf(stderr,"Previous WIDEn-N or TRACEn-N\n");
6026           }
6027           break;
6028         }
6029 
6030         // Perform WIDEn-N checks
6031         if (is_wide)
6032         {
6033           if (debug_level & 1)
6034           {
6035             fprintf(stderr,"Found wideN-n at slot %d\n", ii);
6036           }
6037 
6038           if (strcmp(ViaCalls[ii], "WIDE1-1") !=0) // Home station, RELAY replacement
6039           {
6040             have_widen++;  // Note: We mark "have_relay" for
6041           }
6042           // "WIDE1-1" instead of "have_widen"
6043 
6044           // We know its a WIDEn-N, time to find out what n is
6045           if (strlen(ViaCalls[ii]) != 7)
6046           {
6047             // This should be WIDEn-N and should be
6048             // exactly 7 characters
6049             bad_path = 1;
6050             if (debug_level & 1)
6051             {
6052               fprintf(stderr,"String length of widen-N is not 7 characters, slot %d\n", ii);
6053             }
6054             break;
6055           }
6056 
6057           // Check for proper relations between the n-N
6058           // numbers.
6059           if ( check_n_N(prev, last) )
6060           {
6061             bad_path = 1;
6062             if (debug_level & 1)
6063             {
6064               fprintf(stderr,"In WIDEn-N checks, slot %d\n", ii);
6065             }
6066             break;
6067           }
6068         }
6069 
6070         // Perform similar checks for TRACEn-N
6071         else
6072         {
6073           if (debug_level & 1)
6074           {
6075             fprintf(stderr,"Found traceN-n, slot %d\n", ii);
6076           }
6077           have_tracen++;
6078 
6079           // We know its a TRACEn-N time to find out what
6080           // n is
6081           if (strlen(ViaCalls[ii]) != 8)
6082           {
6083             // This should be TRACEn-N and should be
6084             // exactly 8 characters
6085             bad_path = 1;
6086             if (debug_level & 1)
6087             {
6088               fprintf(stderr,"String length of tracen-N is not 8 characters, slot %d\n", ii);
6089             }
6090             break;
6091           }
6092 
6093           // Check for proper relations between the n-N
6094           // numbers.
6095           if ( check_n_N(prev, last) )
6096           {
6097             bad_path = 1;
6098             if (debug_level & 1)
6099             {
6100               fprintf(stderr,"In TRACEn-N checks, slot %d\n", ii);
6101             }
6102             break;
6103           }
6104         }
6105 
6106       }
6107 
6108       else
6109       {
6110         // We know we have a WIDE or TRACE in this callsign slot
6111         total_digi_length++;
6112         if (is_wide)
6113         {
6114           have_wide++;
6115         }
6116         else
6117         {
6118           have_trace++;
6119         }
6120 
6121         if (have_relay && (ii > MAX_WIDES))
6122         {
6123           // RELAY and more than "MAX_WIDES" WIDE/TRACE calls
6124 // Here we could check have_wide/have_trace to see what the actual
6125 // count of WIDE/TRACE calls is at this point...
6126           bad_path = 1;
6127           if (debug_level & 1)
6128           {
6129             fprintf(stderr,"Have relay and ii > MAX_WIDES\n");
6130           }
6131           break;
6132         }
6133         else if (!have_relay && ii > (MAX_WIDES - 1))
6134         {
6135           // More than WIDE,WIDE,WIDE or TRACE,TRACE,TRACE
6136 // Here we could check have_wide/have_trace to see what the actual
6137 // count of WIDE/TRACE calls is at this point...
6138           bad_path = 1;
6139           if (debug_level & 1)
6140           {
6141             fprintf(stderr,"No relay, but ii > MAX_WIDES-1\n");
6142           }
6143           break;
6144         }
6145         else if (have_widen || have_tracen)
6146         {
6147           // WIDE/TRACE after something other than RELAY
6148           // or WIDE1-1
6149           bad_path = 1;
6150           if (debug_level & 1)
6151           {
6152             fprintf(stderr,"WIDE or TRACE after something other than RELAY or WIDE1-1\n");
6153           }
6154           break;
6155         }
6156       }
6157     }
6158 
6159     // Not RELAY/WIDE/TRACE/WIDEn-N/TRACEn-N call
6160     else
6161     {
6162       // Might as well stub this out, could be anything at
6163       // this point, a LINKn-N or LANn-N or a explicit
6164       // callsign
6165       if (!strstr(ViaCalls[ii], "-"))
6166       {
6167         /*
6168                         // We do not have an SSID, treat it as a RELAY
6169                         if (have_relay) {
6170                             bad_path = 1;
6171                             if (debug_level & 1)
6172                                 fprintf(stderr,"No SSID\n");
6173                             break;
6174                         }
6175         */
6176 
6177         have_relay++;
6178       }
6179 
6180       else
6181       {
6182         // Could be a LAN or LINK or explicit call, check
6183         // for a digit preceding the dash
6184 
6185         if (prev > 0 && prev <= 9)      // Found a digit
6186         {
6187           // We've found an n-N */
6188           if (have_widen || have_tracen)
6189           {
6190             // Already have a previous wide path
6191             bad_path = 1;
6192             if (debug_level & 1)
6193             {
6194               fprintf(stderr,"Found n-N and previous WIDEn-N or TRACEn-N\n");
6195             }
6196             break;
6197           }
6198 
6199           // Check for proper relations between the n-N
6200           // numbers.
6201           if ( check_n_N(prev, last) )
6202           {
6203             bad_path = 1;
6204             if (debug_level & 1)
6205             {
6206               fprintf(stderr,"In OTHER checks\n");
6207             }
6208             break;
6209           }
6210 
6211           if (debug_level & 1)
6212           {
6213             fprintf(stderr,"Found wideN-n, slot %d\n", ii);
6214           }
6215           have_widen++;
6216         }
6217         else
6218         {
6219           /*
6220                               // Must be an explicit callsign, treat as relay
6221                               if (have_relay) {
6222                                   bad_path = 1;
6223                                   if (debug_level & 1)
6224                                       fprintf(stderr,"\n");
6225                                   break;
6226                               }
6227           */
6228           have_relay++;
6229         }
6230       }
6231     }
6232   }   // End of for loop
6233 
6234   if (debug_level & 1)
6235   {
6236     fprintf(stderr,"Total digi length: %d\n", total_digi_length);
6237   }
6238 
6239   if (total_digi_length > MAX_WIDES + 1)
6240   {
6241 
6242     if (debug_level & 1)
6243     {
6244       fprintf(stderr,"Total digi count is too high!\n");
6245     }
6246 
6247     bad_path = 1;
6248   }
6249 
6250   // Free the memory we allocated with strndup()
6251   free(tmpdata);
6252   return(bad_path);
6253 
6254 }   // End of check_unproto_path
6255 
6256 
6257 
6258 
6259 
6260 // Set string printed out by segfault handler
set_dangerous(char * ptr)6261 void set_dangerous( char *ptr )
6262 {
6263   xastir_snprintf(dangerous_operation,
6264                   sizeof(dangerous_operation),
6265                   "%s",
6266                   ptr);
6267 }
6268 
6269 
6270 
6271 
6272 
6273 // Clear string printed out by segfault handler
clear_dangerous(void)6274 void clear_dangerous(void)
6275 {
6276   dangerous_operation[0] = '\0';
6277 }
6278 
6279 
6280 
6281 
6282 
6283 // Write out a WKT file
xastirWriteWKT(char * filename)6284 void xastirWriteWKT(char *filename)
6285 {
6286   // This "WKT" string describes the coordinate system we use in Xastir.
6287   // We'll use this string to write out ".prj" files to associate with
6288   // shapefiles we create from GPS or APRS tracks.
6289   char Xastir_WKT[] = "GEOGCS[\"GCS_WGS_1984\",DATUM[\"D_WGS_1984\",SPHEROID[\"WGS_84\",6378137,298.257223563]],PRIMEM[\"Greenwich\",0],UNIT[\"Degree\",0.017453292519943295]]";
6290 
6291   FILE *f;
6292 
6293   f=fopen(filename,"w"); // open for write
6294   if (f != NULL)
6295   {
6296     fprintf(f,"%s\n",Xastir_WKT);
6297     fclose(f);
6298   }
6299   else
6300   {
6301     fprintf(stderr,"Could not open file %s for writing\n",filename);
6302   }
6303 }
6304 
6305 
6306 
6307 
6308 
6309 // makeMultiline
6310 // Create an APRS multiline string given an array of lat/lon pairs.
6311 //
6312 // Allocates memory that must be freed by the caller.
6313 //
6314 // lon and lat are arrays.  lonObj, latObj are return values of object
6315 // location (point from which offsets are computed).
6316 //
6317 // lineType is 0 for a closed polygon, 1 for a polyline
6318 //
6319 // colorStyle is a character as defined in the wxsvr.net multiline protocol
6320 // web site at wxsvr.net.
6321 //
6322 // character | color | style
6323 //   a          red     solid
6324 //   b          red     dashed
6325 //   c          red     double-dashed
6326 //   d          yellow  solid
6327 //   e          yellow  dashed
6328 //   f          yellow  double-dashed
6329 //   g          blue    solid
6330 //   h          blue    dashed
6331 //   i          blue    double-dashed
6332 //   j          green   solid
6333 //   k          green   dashed
6334 //   l          green   double-dashed
6335 
6336 // Returns a null pointer if user requested too many vertices, or if scale
6337 // is out of range, or if we fail to malloc the string.
6338 //
6339 // One could pass only a list of lat/lons here and get back a point at which
6340 // to create an object (at the centroid) and a string representing the
6341 // multiline.
6342 #define minFun(a,b) ( ((a)<(b))?(a):(b))
6343 #define maxFun(a,b) ( ((a)>(b))?(a):(b))
6344 
makeMultiline(int numPairs,double * lon,double * lat,char colorStyle,int lineType,char * sqnc,double * lonCentr,double * latCentr)6345 char * makeMultiline(int numPairs, double *lon, double *lat, char colorStyle,
6346                      int lineType, char* sqnc,
6347                      double *lonCentr, double *latCentr  )
6348 {
6349 
6350   char * returnString;
6351 
6352   // the APRS spec requires a max of 43 chars in the comment section of
6353   // objects, which leaves room for only so many vertices in a multiline
6354   //   number allowed= (43-(6-5))/2=16
6355   // 43chars - 6 for the sequence number- 5 for the starting pattern leaves
6356   // 32 characters for lat/lon pairs, or 16 pairs
6357 
6358   if ( numPairs > 16)
6359   {
6360     returnString = NULL;
6361   }
6362   else
6363   {
6364     double minLat, minLon;
6365     double maxLat, maxLon;
6366     int iPair;
6367     double scale1,scale2,scale;
6368 
6369     // find min/max of arrays
6370     minLat=minLon=180;
6371     maxLat=maxLon=-180;
6372 
6373     for ( iPair=0; iPair < numPairs; iPair++)
6374     {
6375       minLon = minFun(minLon,lon[iPair]);
6376       minLat = minFun(minLat,lat[iPair]);
6377       maxLon = maxFun(maxLon,lon[iPair]);
6378       maxLat = maxFun(maxLat,lat[iPair]);
6379     }
6380 
6381     *lonCentr = (maxLon+minLon)/2;
6382     *latCentr = (maxLat+minLat)/2;
6383 
6384     // Compute scale:
6385     // The scale is the value that makes the maximum or minimum offset
6386     // map to +44 or -45.  Pick the scale factor that keeps the
6387     // offsets in that range:
6388 
6389     if (maxLat > maxLon)
6390     {
6391       scale1= (maxLat-*latCentr)/44.0;
6392     }
6393     else
6394     {
6395       scale1= (maxLon-*lonCentr)/44.0;
6396     }
6397 
6398     if (minLat < minLon)
6399     {
6400       scale2 = (minLat-*latCentr)/(-45.0);
6401     }
6402     else
6403     {
6404       scale2 = (minLon-*lonCentr)/(-45.0);
6405     }
6406 
6407     scale = maxFun(scale1,scale2);
6408 
6409     if (scale < .0001)
6410     {
6411       scale=0.0001;
6412     }
6413 
6414     if (scale > 1)
6415     {
6416       // Out of range, no shape returned
6417       returnString = NULL;
6418     }
6419     else
6420     {
6421       // Not all systems have a log10(), but they all have log()
6422       // So let's stick with natural logs
6423       double ln10=log(10.0);
6424       // KLUDGE:  the multiline spec says we use
6425       // 20*(int)(log10(scale/.0001)) to generate the scale char,
6426       // but this means we'll often produce real scales that are smaller
6427       // than the one we just calculated, which means we'd produce
6428       // offsets outside the (-45,44) allowed range.  So kludge and
6429       // add 1 to the value
6430       int lnscalefac=20*log(scale/.0001)/ln10+1;
6431       int rsMaxLen=numPairs*2+6+4+1;
6432       int stringOffset=0;
6433 
6434       // Now recompute the scale to be the one we actually transmitted
6435       // This pretty much means we'll never have the best precision
6436       // we could possibly have, but it'll be close enough
6437       scale=pow(10,(double)lnscalefac/20-4);
6438 
6439       // We're ready to produce the multiline string.  So get on with it
6440 
6441       // multiline string is "}CTS" (literal "}" followed by
6442       // line Color-style specifier, followed by open/closed
6443       // Type specifier, followed by Scale character), followed
6444       // by even number of character pairs, followed by "{seqnc"
6445       // (sequence number).
6446 
6447 
6448       returnString=malloc(sizeof(char)*rsMaxLen);
6449 
6450       if (returnString != NULL)
6451       {
6452         returnString[stringOffset++]=' ';
6453         returnString[stringOffset++]='}';
6454         returnString[stringOffset++]=colorStyle;
6455         returnString[stringOffset++]= (lineType == 0)?'0':'1';
6456 
6457         returnString[stringOffset++] = lnscalefac+33;
6458 
6459         for ( iPair=0; iPair<numPairs; ++iPair)
6460         {
6461           double latOffset=lat[iPair]-*latCentr;
6462           // the wxsvr protocol is Western Hemisphere-Centric,
6463           // and treats positive offsets in longitude as being
6464           // west of the reference point.  So have to reverse
6465           // the sense of direction here.
6466           // This will yield positive offsets if lonCenter is
6467           // negative (west) and lon[iPair] is more negative
6468           // (more west)
6469           double lonOffset=*lonCentr-lon[iPair];
6470 
6471           returnString[stringOffset++]=
6472             (char)((int)(latOffset/scale)+78);
6473           returnString[stringOffset++]=
6474             (char)((int)(lonOffset/scale)+78);
6475         }
6476         returnString[stringOffset++]='{';
6477         strncpy(&(returnString[stringOffset]),sqnc,6);
6478         returnString[rsMaxLen-1] = '\0'; // Just to be safe
6479       }
6480     }
6481   }
6482   return (returnString);
6483 }
6484 
6485 
6486