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