Imported Upstream version 1.3.0
[debian/splat] / utils / bearing.c
1 /***************************************************************************\
2 *     bearing: Determines distance and azimuth bearing between locations    *
3 *                     specified in a pair of .qth files                     *
4 *                         Last update: 08-Feb-2009                          *
5 *****************************************************************************
6 *      Project started on December 7, 2007 by John A. Magliacane, KD2BD     *
7 *****************************************************************************
8 *                                                                           *
9 * This program is free software; you can redistribute it and/or modify it   *
10 * under the terms of the GNU General Public License as published by the     *
11 * Free Software Foundation; either version 2 of the License or any later    *
12 * version.                                                                  *
13 *                                                                           *
14 * This program is distributed in the hope that it will useful, but WITHOUT  *
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     *
16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License     *
17 * for more details.                                                         *
18 *                                                                           *
19 *****************************************************************************
20 *      gcc -Wall -O3 -s -lm -fomit-frame-pointer bearing.c -o bearing       *
21 \***************************************************************************/
22
23 #include <stdio.h>
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <unistd.h>
29
30 char    string[255];
31
32 double  TWOPI=6.283185307179586, PI=3.141592653589793,
33         deg2rad=1.74532925199e-02, KM_PER_MILE=1.609344;
34
35 struct site {   double lat;
36                 double lon;
37                 double azimuth;
38                 char name[50];
39             }   site;
40
41
42 double arccos(double x, double y)
43 {
44         /* This function implements the arc cosine function,
45            returning a value between 0 and TWOPI. */
46
47         double result=0.0;
48
49         if (y>0.0)
50                 result=acos(x/y);
51
52         if (y<0.0)
53                 result=PI+acos(x/y);
54
55         return result;
56 }
57
58 char *dec2dms(double decimal)
59 {
60         /* Converts decimal degrees to degrees, minutes, seconds,
61            (DMS) and returns the result as a character string. */
62
63         char    sign;
64         int     degrees, minutes, seconds;
65         double  a, b, c, d;
66
67         if (decimal<0.0)
68         {
69                 decimal=-decimal;
70                 sign=-1;
71         }
72
73         else
74                 sign=1;
75
76         a=floor(decimal);
77         b=60.0*(decimal-a);
78         c=floor(b);
79         d=60.0*(b-c);
80
81         degrees=(int)a;
82         minutes=(int)c;
83         seconds=(int)d;
84
85         if (seconds<0)
86                 seconds=0;
87
88         if (seconds>59)
89                 seconds=59;
90
91         string[0]=0;
92         sprintf(string,"%d%c %d\' %d\"", degrees*sign, 176, minutes, seconds);
93         return (string);
94 }
95
96 double Distance(struct site site1, struct site site2)
97 {
98         /* This function returns the great circle distance
99            in miles between any two site locations. */
100
101         double  lat1, lon1, lat2, lon2, distance;
102
103         lat1=site1.lat*deg2rad;
104         lon1=site1.lon*deg2rad;
105         lat2=site2.lat*deg2rad;
106         lon2=site2.lon*deg2rad;
107
108         distance=3959.0*acos(sin(lat1)*sin(lat2)+cos(lat1)*cos(lat2)*cos((lon1)-(lon2)));
109
110         return distance;
111 }
112
113 double Azimuth(struct site source, struct site destination)
114 {
115         /* This function returns the azimuth (in degrees) to the
116            destination as seen from the location of the source. */
117
118         double  dest_lat, dest_lon, src_lat, src_lon,
119                 beta, azimuth, diff, num, den, fraction;
120
121         dest_lat=destination.lat*deg2rad;
122         dest_lon=destination.lon*deg2rad;
123
124         src_lat=source.lat*deg2rad;
125         src_lon=source.lon*deg2rad;
126                 
127         /* Calculate Surface Distance */
128
129         beta=acos(sin(src_lat)*sin(dest_lat)+cos(src_lat)*cos(dest_lat)*cos(src_lon-dest_lon));
130
131         /* Calculate Azimuth */
132
133         num=sin(dest_lat)-(sin(src_lat)*cos(beta));
134         den=cos(src_lat)*sin(beta);
135         fraction=num/den;
136
137         /* Trap potential problems in acos() due to rounding */
138
139         if (fraction>=1.0)
140                 fraction=1.0;
141
142         if (fraction<=-1.0)
143                 fraction=-1.0;
144
145         /* Calculate azimuth */
146
147         azimuth=acos(fraction);
148
149         /* Reference it to True North */
150
151         diff=dest_lon-src_lon;
152
153         if (diff<=-PI)
154                 diff+=TWOPI;
155
156         if (diff>=PI)
157                 diff-=TWOPI;
158
159         if (diff>0.0)
160                 azimuth=TWOPI-azimuth;
161
162         return (azimuth/deg2rad);               
163 }
164
165 double ReadBearing(char *input)
166 {
167         /* This function takes numeric input in the form of a character
168            string, and returns an equivalent bearing in degrees as a
169            decimal number (double).  The input may either be expressed
170            in decimal format (40.139722) or degree, minute, second
171            format (40 08 23).  This function also safely handles
172            extra spaces found either leading, trailing, or
173            embedded within the numbers expressed in the
174            input string.  Decimal seconds are permitted. */
175  
176         double  seconds, bearing=0.0;
177         char    string[20];
178         int     a, b, length, degrees, minutes;
179
180         /* Copy "input" to "string", and ignore any extra
181            spaces that might be present in the process. */
182
183         string[0]=0;
184         length=strlen(input);
185
186         for (a=0, b=0; a<length && a<18; a++)
187         {
188                 if ((input[a]!=32 && input[a]!='\n') || (input[a]==32 && input[a+1]!=32 && input[a+1]!='\n' && b!=0))
189                 {
190                         string[b]=input[a];
191                         b++;
192                 }        
193         }
194
195         string[b]=0;
196
197         /* Count number of spaces in the clean string. */
198
199         length=strlen(string);
200
201         for (a=0, b=0; a<length; a++)
202                 if (string[a]==32)
203                         b++;
204
205         if (b==0)  /* Decimal Format (40.139722) */
206                 sscanf(string,"%lf",&bearing);
207
208         if (b==2)  /* Degree, Minute, Second Format (40 08 23.xx) */
209         {
210                 sscanf(string,"%d %d %lf",&degrees, &minutes, &seconds);
211
212                 bearing=fabs((double)degrees);
213                 bearing+=fabs(((double)minutes)/60.0);
214                 bearing+=fabs(seconds/3600.0);
215
216                 if ((degrees<0) || (minutes<0) || (seconds<0.0))
217                         bearing=-bearing;
218         }
219
220         /* Anything else returns a 0.0 */
221
222         if (bearing>360.0 || bearing<-90.0)
223                 bearing=0.0;
224
225         return bearing;
226 }
227
228 struct site LoadQTH(char *filename)
229 {
230         /* This function reads SPLAT! .qth (site location) files.
231            The latitude and longitude may be expressed either in
232            decimal degrees, or in degree, minute, second format.
233            Antenna height is assumed to be expressed in feet above
234            ground level (AGL), unless followed by the letter 'M',
235            or 'm', or by the word "meters" or "Meters", in which
236            case meters is assumed, and is handled accordingly. */
237
238         int     x;
239         char    string[50], qthfile[255], *s=NULL;
240         struct  site tempsite;
241         FILE    *fd=NULL;
242
243         for (x=0; filename[x]!='.' && filename[x]!=0 && x<250; x++)
244                 qthfile[x]=filename[x];
245
246         qthfile[x]='.';
247         qthfile[x+1]='q';
248         qthfile[x+2]='t';
249         qthfile[x+3]='h';
250         qthfile[x+4]=0;
251
252         tempsite.lat=91.0;
253         tempsite.lon=361.0;
254         tempsite.name[0]=0;
255         tempsite.azimuth=0.0;
256
257         fd=fopen(qthfile,"r");
258
259         if (fd!=NULL)
260         {
261                 /* Site Name */
262                 s=fgets(string,49,fd);
263
264                 /* Strip <CR> and/or <LF> from end of site name */
265
266                 for (x=0; string[x]!=13 && string[x]!=10 && string[x]!=0; tempsite.name[x]=string[x], x++);
267
268                 tempsite.name[x]=0;
269
270                 /* Site Latitude */
271                 s=fgets(string,49,fd);
272                 tempsite.lat=ReadBearing(string);
273
274                 /* Site Longitude */
275                 s=fgets(string,49,fd);
276                 tempsite.lon=ReadBearing(string);
277
278                 fclose(fd);
279         }
280
281         return tempsite;
282 }
283
284 int main(int argc, char *argv[])
285 {
286         int             x, y;
287         unsigned char   sitenum, metric, error;
288         double          distance, azimuth;
289         struct          site point[2];
290
291         if (argc==1)
292         {
293                 fprintf(stdout,"\nProvides distance and azimuth bearing between two site locations.\n");
294                 fprintf(stdout,"\nUsage: bearing site1(.qth) site2(.qth)");
295                 fprintf(stdout,"\n       bearing site1(.qth) site2(.qth) -metric\n\n");
296                 fflush(stdout);
297                 return 1;
298         }
299
300         metric=0;
301         error=0;
302         y=argc-1;
303         sitenum=0;
304
305         point[0].lat=91.0;
306         point[0].lon=361.0;
307         point[1].lat=91.0;
308         point[1].lon=361.0;
309
310         /* Scan for command line arguments */
311
312         for (x=1; x<=y; x++)
313         {
314                 if (strcmp(argv[x],"-metric")==0)
315                 {
316                         metric=1;
317                         x++;
318                 }
319
320                 /* Read Receiver Location */
321
322                 if (x<=y && argv[x][0] && argv[x][0]!='-' && sitenum<2)
323                 {
324                         point[sitenum]=LoadQTH(argv[x]);
325
326                         if (point[sitenum].lat>90.0 || point[sitenum].lon>360.0)
327                                 fprintf(stderr,"\n%c*** \"%s\" is not a valid .qth file!",7,argv[x]);
328                         else
329                                 sitenum++;
330                 }
331         }
332
333         if (sitenum<2)
334         {
335                 fprintf(stderr,"\n%c*** ERROR: Not enough valid sites specified!\n\n",7);
336                 exit (-1);
337         }
338
339         for (x=0; x<sitenum; x++)
340         {
341                 if (point[x].lat>90.0 && point[x].lon>360.0)
342                 {
343                         fprintf(stderr,"\n*** ERROR: site #%d not found!",x+1);
344                         error=1;
345                 }
346         }
347
348         if (error)
349                 return -1;
350
351         else
352         {
353                 /* Perform the calculations and display the results */
354
355                 distance=Distance(point[0],point[1]);
356                 azimuth=Azimuth(point[0],point[1]);
357
358                 printf("\nThe distance between %s and %s is\n",point[0].name, point[1].name);
359
360                 if (metric)
361                         printf("%.2f kilometers",distance*KM_PER_MILE);
362                 else
363                         printf("%.2f miles",distance);
364
365                 printf(" at a bearing of %.2f%c azimuth.\n\n",azimuth, 176);
366         }
367
368         return 0;
369 }