xref: /freebsd/contrib/ntp/clockstuff/propdelay.c (revision b626f5a73a48f44a31a200291b141e1da408a2ff)
1c0b746e5SOllivier Robert /* propdelay.c,v 3.1 1993/07/06 01:05:24 jbj Exp
2c0b746e5SOllivier Robert  * propdelay - compute propagation delays
3c0b746e5SOllivier Robert  *
4c0b746e5SOllivier Robert  * cc -o propdelay propdelay.c -lm
5c0b746e5SOllivier Robert  *
6c0b746e5SOllivier Robert  * "Time and Frequency Users' Manual", NBS Technical Note 695 (1977).
7c0b746e5SOllivier Robert  */
8c0b746e5SOllivier Robert 
9c0b746e5SOllivier Robert /*
10c0b746e5SOllivier Robert  * This can be used to get a rough idea of the HF propagation delay
11c0b746e5SOllivier Robert  * between two points (usually between you and the radio station).
12c0b746e5SOllivier Robert  * The usage is
13c0b746e5SOllivier Robert  *
14c0b746e5SOllivier Robert  * propdelay latitudeA longitudeA latitudeB longitudeB
15c0b746e5SOllivier Robert  *
16c0b746e5SOllivier Robert  * where points A and B are the locations in question.  You obviously
17c0b746e5SOllivier Robert  * need to know the latitude and longitude of each of the places.
18c0b746e5SOllivier Robert  * The program expects the latitude to be preceded by an 'n' or 's'
19c0b746e5SOllivier Robert  * and the longitude to be preceded by an 'e' or 'w'.  It understands
20c0b746e5SOllivier Robert  * either decimal degrees or degrees:minutes:seconds.  Thus to compute
21c0b746e5SOllivier Robert  * the delay between the WWVH (21:59:26N, 159:46:00W) and WWV (40:40:49N,
22c0b746e5SOllivier Robert  * 105:02:27W) you could use:
23c0b746e5SOllivier Robert  *
24c0b746e5SOllivier Robert  * propdelay n21:59:26 w159:46 n40:40:49 w105:02:27
25c0b746e5SOllivier Robert  *
26c0b746e5SOllivier Robert  * By default it prints out a summer (F2 average virtual height 350 km) and
27c0b746e5SOllivier Robert  * winter (F2 average virtual height 250 km) number.  The results will be
28c0b746e5SOllivier Robert  * quite approximate but are about as good as you can do with HF time anyway.
29c0b746e5SOllivier Robert  * You might pick a number between the values to use, or use the summer
30c0b746e5SOllivier Robert  * value in the summer and switch to the winter value when the static
31c0b746e5SOllivier Robert  * above 10 MHz starts to drop off in the fall.  You can also use the
32c0b746e5SOllivier Robert  * -h switch if you want to specify your own virtual height.
33c0b746e5SOllivier Robert  *
34c0b746e5SOllivier Robert  * You can also do a
35c0b746e5SOllivier Robert  *
36c0b746e5SOllivier Robert  * propdelay -W n45:17:47 w75:45:22
37c0b746e5SOllivier Robert  *
38c0b746e5SOllivier Robert  * to find the propagation delays to WWV and WWVH (from CHU in this
39c0b746e5SOllivier Robert  * case), a
40c0b746e5SOllivier Robert  *
41c0b746e5SOllivier Robert  * propdelay -C n40:40:49 w105:02:27
42c0b746e5SOllivier Robert  *
43c0b746e5SOllivier Robert  * to find the delays to CHU, and a
44c0b746e5SOllivier Robert  *
45c0b746e5SOllivier Robert  * propdelay -G n52:03:17 w98:34:18
46c0b746e5SOllivier Robert  *
47c0b746e5SOllivier Robert  * to find delays to GOES via each of the three satellites.
48c0b746e5SOllivier Robert  */
49c0b746e5SOllivier Robert 
502b15cb3dSCy Schubert #ifdef HAVE_CONFIG_H
512b15cb3dSCy Schubert # include <config.h>
522b15cb3dSCy Schubert #endif
53c0b746e5SOllivier Robert #include <stdio.h>
54c0b746e5SOllivier Robert #include <string.h>
55c0b746e5SOllivier Robert 
56c0b746e5SOllivier Robert #include "ntp_stdlib.h"
57c0b746e5SOllivier Robert 
58c0b746e5SOllivier Robert extern	double	sin	(double);
59c0b746e5SOllivier Robert extern	double	cos	(double);
60c0b746e5SOllivier Robert extern	double	acos	(double);
61c0b746e5SOllivier Robert extern	double	tan	(double);
62c0b746e5SOllivier Robert extern	double	atan	(double);
63c0b746e5SOllivier Robert extern	double	sqrt	(double);
64c0b746e5SOllivier Robert 
65c0b746e5SOllivier Robert #define	STREQ(a, b)	(*(a) == *(b) && strcmp((a), (b)) == 0)
66c0b746e5SOllivier Robert 
67c0b746e5SOllivier Robert /*
68c0b746e5SOllivier Robert  * Program constants
69c0b746e5SOllivier Robert  */
70c0b746e5SOllivier Robert #define	EARTHRADIUS	(6370.0)	/* raduis of earth (km) */
71c0b746e5SOllivier Robert #define	LIGHTSPEED	(299800.0)	/* speed of light, km/s */
72c0b746e5SOllivier Robert #define	PI		(3.1415926536)
73c0b746e5SOllivier Robert #define	RADPERDEG	(PI/180.0)	/* radians per degree */
74c0b746e5SOllivier Robert #define MILE		(1.609344)      /* km in a mile */
75c0b746e5SOllivier Robert 
76c0b746e5SOllivier Robert #define	SUMMERHEIGHT	(350.0)		/* summer height in km */
77c0b746e5SOllivier Robert #define	WINTERHEIGHT	(250.0)		/* winter height in km */
78c0b746e5SOllivier Robert 
79c0b746e5SOllivier Robert #define SATHEIGHT	(6.6110 * 6378.0) /* geosync satellite height in km
80c0b746e5SOllivier Robert 					     from centre of earth */
81c0b746e5SOllivier Robert 
82c0b746e5SOllivier Robert #define WWVLAT  "n40:40:49"
83c0b746e5SOllivier Robert #define WWVLONG "w105:02:27"
84c0b746e5SOllivier Robert 
85c0b746e5SOllivier Robert #define WWVHLAT  "n21:59:26"
86c0b746e5SOllivier Robert #define WWVHLONG "w159:46:00"
87c0b746e5SOllivier Robert 
88c0b746e5SOllivier Robert #define CHULAT	"n45:17:47"
89c0b746e5SOllivier Robert #define	CHULONG	"w75:45:22"
90c0b746e5SOllivier Robert 
91c0b746e5SOllivier Robert #define GOES_UP_LAT  "n37:52:00"
92c0b746e5SOllivier Robert #define GOES_UP_LONG "w75:27:00"
93c0b746e5SOllivier Robert #define GOES_EAST_LONG "w75:00:00"
94c0b746e5SOllivier Robert #define GOES_STBY_LONG "w105:00:00"
95c0b746e5SOllivier Robert #define GOES_WEST_LONG "w135:00:00"
96c0b746e5SOllivier Robert #define GOES_SAT_LAT "n00:00:00"
97c0b746e5SOllivier Robert 
98c0b746e5SOllivier Robert char *wwvlat = WWVLAT;
99c0b746e5SOllivier Robert char *wwvlong = WWVLONG;
100c0b746e5SOllivier Robert 
101c0b746e5SOllivier Robert char *wwvhlat = WWVHLAT;
102c0b746e5SOllivier Robert char *wwvhlong = WWVHLONG;
103c0b746e5SOllivier Robert 
104c0b746e5SOllivier Robert char *chulat = CHULAT;
105c0b746e5SOllivier Robert char *chulong = CHULONG;
106c0b746e5SOllivier Robert 
107c0b746e5SOllivier Robert char *goes_up_lat = GOES_UP_LAT;
108c0b746e5SOllivier Robert char *goes_up_long = GOES_UP_LONG;
109c0b746e5SOllivier Robert char *goes_east_long = GOES_EAST_LONG;
110c0b746e5SOllivier Robert char *goes_stby_long = GOES_STBY_LONG;
111c0b746e5SOllivier Robert char *goes_west_long = GOES_WEST_LONG;
112c0b746e5SOllivier Robert char *goes_sat_lat = GOES_SAT_LAT;
113c0b746e5SOllivier Robert 
114c0b746e5SOllivier Robert int hflag = 0;
115c0b746e5SOllivier Robert int Wflag = 0;
116c0b746e5SOllivier Robert int Cflag = 0;
117c0b746e5SOllivier Robert int Gflag = 0;
118c0b746e5SOllivier Robert int height;
119c0b746e5SOllivier Robert 
120*9034852cSGleb Smirnoff char const *progname;
121c0b746e5SOllivier Robert 
122c0b746e5SOllivier Robert static	void	doit		(double, double, double, double, double, char *);
123c0b746e5SOllivier Robert static	double	latlong		(char *, int);
124c0b746e5SOllivier Robert static	double	greatcircle	(double, double, double, double);
125c0b746e5SOllivier Robert static	double	waveangle	(double, double, int);
126c0b746e5SOllivier Robert static	double	propdelay	(double, double, int);
127c0b746e5SOllivier Robert static	int	finddelay	(double, double, double, double, double, double *);
128c0b746e5SOllivier Robert static	void	satdoit		(double, double, double, double, double, double, char *);
129c0b746e5SOllivier Robert static	void	satfinddelay	(double, double, double, double, double *);
130c0b746e5SOllivier Robert static	double	satpropdelay	(double);
131c0b746e5SOllivier Robert 
132c0b746e5SOllivier Robert /*
133c0b746e5SOllivier Robert  * main - parse arguments and handle options
134c0b746e5SOllivier Robert  */
135c0b746e5SOllivier Robert int
main(int argc,char * argv[])136c0b746e5SOllivier Robert main(
137c0b746e5SOllivier Robert 	int argc,
138c0b746e5SOllivier Robert 	char *argv[]
139c0b746e5SOllivier Robert 	)
140c0b746e5SOllivier Robert {
141c0b746e5SOllivier Robert 	int c;
142c0b746e5SOllivier Robert 	int errflg = 0;
143c0b746e5SOllivier Robert 	double lat1, long1;
144c0b746e5SOllivier Robert 	double lat2, long2;
145c0b746e5SOllivier Robert 	double lat3, long3;
146c0b746e5SOllivier Robert 
1472b15cb3dSCy Schubert 	init_lib();
1482b15cb3dSCy Schubert 
149c0b746e5SOllivier Robert 	progname = argv[0];
150c0b746e5SOllivier Robert 	while ((c = ntp_getopt(argc, argv, "dh:CWG")) != EOF)
151c0b746e5SOllivier Robert 	    switch (c) {
152c0b746e5SOllivier Robert 		case 'd':
153c0b746e5SOllivier Robert 		    ++debug;
154c0b746e5SOllivier Robert 		    break;
155c0b746e5SOllivier Robert 		case 'h':
156c0b746e5SOllivier Robert 		    hflag++;
157c0b746e5SOllivier Robert 		    height = atof(ntp_optarg);
158c0b746e5SOllivier Robert 		    if (height <= 0.0) {
159c0b746e5SOllivier Robert 			    (void) fprintf(stderr, "height %s unlikely\n",
160c0b746e5SOllivier Robert 					   ntp_optarg);
161c0b746e5SOllivier Robert 			    errflg++;
162c0b746e5SOllivier Robert 		    }
163c0b746e5SOllivier Robert 		    break;
164c0b746e5SOllivier Robert 		case 'C':
165c0b746e5SOllivier Robert 		    Cflag++;
166c0b746e5SOllivier Robert 		    break;
167c0b746e5SOllivier Robert 		case 'W':
168c0b746e5SOllivier Robert 		    Wflag++;
169c0b746e5SOllivier Robert 		    break;
170c0b746e5SOllivier Robert 		case 'G':
171c0b746e5SOllivier Robert 		    Gflag++;
172c0b746e5SOllivier Robert 		    break;
173c0b746e5SOllivier Robert 		default:
174c0b746e5SOllivier Robert 		    errflg++;
175c0b746e5SOllivier Robert 		    break;
176c0b746e5SOllivier Robert 	    }
177c0b746e5SOllivier Robert 	if (errflg || (!(Cflag || Wflag || Gflag) && ntp_optind+4 != argc) ||
178c0b746e5SOllivier Robert 	    ((Cflag || Wflag || Gflag) && ntp_optind+2 != argc)) {
179c0b746e5SOllivier Robert 		(void) fprintf(stderr,
180c0b746e5SOllivier Robert 			       "usage: %s [-d] [-h height] lat1 long1 lat2 long2\n",
181c0b746e5SOllivier Robert 			       progname);
182c0b746e5SOllivier Robert 		(void) fprintf(stderr," - or -\n");
183c0b746e5SOllivier Robert 		(void) fprintf(stderr,
184c0b746e5SOllivier Robert 			       "usage: %s -CWG [-d] lat long\n",
185c0b746e5SOllivier Robert 			       progname);
186c0b746e5SOllivier Robert 		exit(2);
187c0b746e5SOllivier Robert 	}
188c0b746e5SOllivier Robert 
189c0b746e5SOllivier Robert 
190c0b746e5SOllivier Robert 	if (!(Cflag || Wflag || Gflag)) {
191c0b746e5SOllivier Robert 		lat1 = latlong(argv[ntp_optind], 1);
192c0b746e5SOllivier Robert 		long1 = latlong(argv[ntp_optind + 1], 0);
193c0b746e5SOllivier Robert 		lat2 = latlong(argv[ntp_optind + 2], 1);
194c0b746e5SOllivier Robert 		long2 = latlong(argv[ntp_optind + 3], 0);
195c0b746e5SOllivier Robert 		if (hflag) {
196c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, height, "");
197c0b746e5SOllivier Robert 		} else {
198c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
199c0b746e5SOllivier Robert 			     "summer propagation, ");
200c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
201c0b746e5SOllivier Robert 			     "winter propagation, ");
202c0b746e5SOllivier Robert 		}
203c0b746e5SOllivier Robert 	} else if (Wflag) {
204c0b746e5SOllivier Robert 		/*
205c0b746e5SOllivier Robert 		 * Compute delay from WWV
206c0b746e5SOllivier Robert 		 */
207c0b746e5SOllivier Robert 		lat1 = latlong(argv[ntp_optind], 1);
208c0b746e5SOllivier Robert 		long1 = latlong(argv[ntp_optind + 1], 0);
209c0b746e5SOllivier Robert 		lat2 = latlong(wwvlat, 1);
210c0b746e5SOllivier Robert 		long2 = latlong(wwvlong, 0);
211c0b746e5SOllivier Robert 		if (hflag) {
212c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, height, "WWV  ");
213c0b746e5SOllivier Robert 		} else {
214c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
215c0b746e5SOllivier Robert 			     "WWV  summer propagation, ");
216c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
217c0b746e5SOllivier Robert 			     "WWV  winter propagation, ");
218c0b746e5SOllivier Robert 		}
219c0b746e5SOllivier Robert 
220c0b746e5SOllivier Robert 		/*
221c0b746e5SOllivier Robert 		 * Compute delay from WWVH
222c0b746e5SOllivier Robert 		 */
223c0b746e5SOllivier Robert 		lat2 = latlong(wwvhlat, 1);
224c0b746e5SOllivier Robert 		long2 = latlong(wwvhlong, 0);
225c0b746e5SOllivier Robert 		if (hflag) {
226c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, height, "WWVH ");
227c0b746e5SOllivier Robert 		} else {
228c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
229c0b746e5SOllivier Robert 			     "WWVH summer propagation, ");
230c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
231c0b746e5SOllivier Robert 			     "WWVH winter propagation, ");
232c0b746e5SOllivier Robert 		}
233c0b746e5SOllivier Robert 	} else if (Cflag) {
234c0b746e5SOllivier Robert 		lat1 = latlong(argv[ntp_optind], 1);
235c0b746e5SOllivier Robert 		long1 = latlong(argv[ntp_optind + 1], 0);
236c0b746e5SOllivier Robert 		lat2 = latlong(chulat, 1);
237c0b746e5SOllivier Robert 		long2 = latlong(chulong, 0);
238c0b746e5SOllivier Robert 		if (hflag) {
239c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, height, "CHU ");
240c0b746e5SOllivier Robert 		} else {
241c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
242c0b746e5SOllivier Robert 			     "CHU summer propagation, ");
243c0b746e5SOllivier Robert 			doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
244c0b746e5SOllivier Robert 			     "CHU winter propagation, ");
245c0b746e5SOllivier Robert 		}
246c0b746e5SOllivier Robert 	} else if (Gflag) {
247c0b746e5SOllivier Robert 		lat1 = latlong(goes_up_lat, 1);
248c0b746e5SOllivier Robert 		long1 = latlong(goes_up_long, 0);
249c0b746e5SOllivier Robert 		lat3 = latlong(argv[ntp_optind], 1);
250c0b746e5SOllivier Robert 		long3 = latlong(argv[ntp_optind + 1], 0);
251c0b746e5SOllivier Robert 
252c0b746e5SOllivier Robert 		lat2 = latlong(goes_sat_lat, 1);
253c0b746e5SOllivier Robert 
254c0b746e5SOllivier Robert 		long2 = latlong(goes_west_long, 0);
255c0b746e5SOllivier Robert 		satdoit(lat1, long1, lat2, long2, lat3, long3,
256c0b746e5SOllivier Robert 			"GOES Delay via WEST");
257c0b746e5SOllivier Robert 
258c0b746e5SOllivier Robert 		long2 = latlong(goes_stby_long, 0);
259c0b746e5SOllivier Robert 		satdoit(lat1, long1, lat2, long2, lat3, long3,
260c0b746e5SOllivier Robert 			"GOES Delay via STBY");
261c0b746e5SOllivier Robert 
262c0b746e5SOllivier Robert 		long2 = latlong(goes_east_long, 0);
263c0b746e5SOllivier Robert 		satdoit(lat1, long1, lat2, long2, lat3, long3,
264c0b746e5SOllivier Robert 			"GOES Delay via EAST");
265c0b746e5SOllivier Robert 
266c0b746e5SOllivier Robert 	}
267c0b746e5SOllivier Robert 	exit(0);
268c0b746e5SOllivier Robert }
269c0b746e5SOllivier Robert 
270c0b746e5SOllivier Robert 
271c0b746e5SOllivier Robert /*
272c0b746e5SOllivier Robert  * doit - compute a delay and print it
273c0b746e5SOllivier Robert  */
274c0b746e5SOllivier Robert static void
doit(double lat1,double long1,double lat2,double long2,double h,char * str)275c0b746e5SOllivier Robert doit(
276c0b746e5SOllivier Robert 	double lat1,
277c0b746e5SOllivier Robert 	double long1,
278c0b746e5SOllivier Robert 	double lat2,
279c0b746e5SOllivier Robert 	double long2,
280c0b746e5SOllivier Robert 	double h,
281c0b746e5SOllivier Robert 	char *str
282c0b746e5SOllivier Robert 	)
283c0b746e5SOllivier Robert {
284c0b746e5SOllivier Robert 	int hops;
285c0b746e5SOllivier Robert 	double delay;
286c0b746e5SOllivier Robert 
287c0b746e5SOllivier Robert 	hops = finddelay(lat1, long1, lat2, long2, h, &delay);
288c0b746e5SOllivier Robert 	printf("%sheight %g km, hops %d, delay %g seconds\n",
289c0b746e5SOllivier Robert 	       str, h, hops, delay);
290c0b746e5SOllivier Robert }
291c0b746e5SOllivier Robert 
292c0b746e5SOllivier Robert 
293c0b746e5SOllivier Robert /*
294c0b746e5SOllivier Robert  * latlong - decode a latitude/longitude value
295c0b746e5SOllivier Robert  */
296c0b746e5SOllivier Robert static double
latlong(char * str,int islat)297c0b746e5SOllivier Robert latlong(
298c0b746e5SOllivier Robert 	char *str,
299c0b746e5SOllivier Robert 	int islat
300c0b746e5SOllivier Robert 	)
301c0b746e5SOllivier Robert {
302c0b746e5SOllivier Robert 	register char *cp;
303c0b746e5SOllivier Robert 	register char *bp;
304c0b746e5SOllivier Robert 	double arg;
305ea906c41SOllivier Robert 	double divby;
306c0b746e5SOllivier Robert 	int isneg;
307c0b746e5SOllivier Robert 	char buf[32];
308c0b746e5SOllivier Robert 	char *colon;
309c0b746e5SOllivier Robert 
310c0b746e5SOllivier Robert 	if (islat) {
311c0b746e5SOllivier Robert 		/*
312c0b746e5SOllivier Robert 		 * Must be north or south
313c0b746e5SOllivier Robert 		 */
314c0b746e5SOllivier Robert 		if (*str == 'N' || *str == 'n')
315c0b746e5SOllivier Robert 		    isneg = 0;
316c0b746e5SOllivier Robert 		else if (*str == 'S' || *str == 's')
317c0b746e5SOllivier Robert 		    isneg = 1;
318c0b746e5SOllivier Robert 		else
319c0b746e5SOllivier Robert 		    isneg = -1;
320c0b746e5SOllivier Robert 	} else {
321c0b746e5SOllivier Robert 		/*
322c0b746e5SOllivier Robert 		 * East is positive, west is negative
323c0b746e5SOllivier Robert 		 */
324c0b746e5SOllivier Robert 		if (*str == 'E' || *str == 'e')
325c0b746e5SOllivier Robert 		    isneg = 0;
326c0b746e5SOllivier Robert 		else if (*str == 'W' || *str == 'w')
327c0b746e5SOllivier Robert 		    isneg = 1;
328c0b746e5SOllivier Robert 		else
329c0b746e5SOllivier Robert 		    isneg = -1;
330c0b746e5SOllivier Robert 	}
331c0b746e5SOllivier Robert 
332c0b746e5SOllivier Robert 	if (isneg >= 0)
333c0b746e5SOllivier Robert 	    str++;
334c0b746e5SOllivier Robert 
335c0b746e5SOllivier Robert 	colon = strchr(str, ':');
336c0b746e5SOllivier Robert 	if (colon != NULL) {
337c0b746e5SOllivier Robert 		/*
338c0b746e5SOllivier Robert 		 * in hhh:mm:ss form
339c0b746e5SOllivier Robert 		 */
340c0b746e5SOllivier Robert 		cp = str;
341c0b746e5SOllivier Robert 		bp = buf;
342c0b746e5SOllivier Robert 		while (cp < colon)
343c0b746e5SOllivier Robert 		    *bp++ = *cp++;
344c0b746e5SOllivier Robert 		*bp = '\0';
345c0b746e5SOllivier Robert 		cp++;
346c0b746e5SOllivier Robert 		arg = atof(buf);
347ea906c41SOllivier Robert 		divby = 60.0;
348c0b746e5SOllivier Robert 		colon = strchr(cp, ':');
349c0b746e5SOllivier Robert 		if (colon != NULL) {
350c0b746e5SOllivier Robert 			bp = buf;
351c0b746e5SOllivier Robert 			while (cp < colon)
352c0b746e5SOllivier Robert 			    *bp++ = *cp++;
353c0b746e5SOllivier Robert 			*bp = '\0';
354c0b746e5SOllivier Robert 			cp++;
355ea906c41SOllivier Robert 			arg += atof(buf) / divby;
356ea906c41SOllivier Robert 			divby = 3600.0;
357c0b746e5SOllivier Robert 		}
358c0b746e5SOllivier Robert 		if (*cp != '\0')
359ea906c41SOllivier Robert 		    arg += atof(cp) / divby;
360c0b746e5SOllivier Robert 	} else {
361c0b746e5SOllivier Robert 		arg = atof(str);
362c0b746e5SOllivier Robert 	}
363c0b746e5SOllivier Robert 
364c0b746e5SOllivier Robert 	if (isneg == 1)
365c0b746e5SOllivier Robert 	    arg = -arg;
366c0b746e5SOllivier Robert 
367c0b746e5SOllivier Robert 	if (debug > 2)
368c0b746e5SOllivier Robert 	    (void) printf("latitude/longitude %s = %g\n", str, arg);
369c0b746e5SOllivier Robert 
370c0b746e5SOllivier Robert 	return arg;
371c0b746e5SOllivier Robert }
372c0b746e5SOllivier Robert 
373c0b746e5SOllivier Robert 
374c0b746e5SOllivier Robert /*
375c0b746e5SOllivier Robert  * greatcircle - compute the great circle distance in kilometers
376c0b746e5SOllivier Robert  */
377c0b746e5SOllivier Robert static double
greatcircle(double lat1,double long1,double lat2,double long2)378c0b746e5SOllivier Robert greatcircle(
379c0b746e5SOllivier Robert 	double lat1,
380c0b746e5SOllivier Robert 	double long1,
381c0b746e5SOllivier Robert 	double lat2,
382c0b746e5SOllivier Robert 	double long2
383c0b746e5SOllivier Robert 	)
384c0b746e5SOllivier Robert {
385c0b746e5SOllivier Robert 	double dg;
386c0b746e5SOllivier Robert 	double l1r, l2r;
387c0b746e5SOllivier Robert 
388c0b746e5SOllivier Robert 	l1r = lat1 * RADPERDEG;
389c0b746e5SOllivier Robert 	l2r = lat2 * RADPERDEG;
390c0b746e5SOllivier Robert 	dg = EARTHRADIUS * acos(
391c0b746e5SOllivier Robert 		(cos(l1r) * cos(l2r) * cos((long2-long1)*RADPERDEG))
392c0b746e5SOllivier Robert 		+ (sin(l1r) * sin(l2r)));
393c0b746e5SOllivier Robert 	if (debug >= 2)
394c0b746e5SOllivier Robert 	    printf(
395c0b746e5SOllivier Robert 		    "greatcircle lat1 %g long1 %g lat2 %g long2 %g dist %g\n",
396c0b746e5SOllivier Robert 		    lat1, long1, lat2, long2, dg);
397c0b746e5SOllivier Robert 	return dg;
398c0b746e5SOllivier Robert }
399c0b746e5SOllivier Robert 
400c0b746e5SOllivier Robert 
401c0b746e5SOllivier Robert /*
402c0b746e5SOllivier Robert  * waveangle - compute the wave angle for the given distance, virtual
403c0b746e5SOllivier Robert  *	       height and number of hops.
404c0b746e5SOllivier Robert  */
405c0b746e5SOllivier Robert static double
waveangle(double dg,double h,int n)406c0b746e5SOllivier Robert waveangle(
407c0b746e5SOllivier Robert 	double dg,
408c0b746e5SOllivier Robert 	double h,
409c0b746e5SOllivier Robert 	int n
410c0b746e5SOllivier Robert 	)
411c0b746e5SOllivier Robert {
412c0b746e5SOllivier Robert 	double theta;
413c0b746e5SOllivier Robert 	double delta;
414c0b746e5SOllivier Robert 
415c0b746e5SOllivier Robert 	theta = dg / (EARTHRADIUS * (double)(2 * n));
416c0b746e5SOllivier Robert 	delta = atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2)) - theta;
417c0b746e5SOllivier Robert 	if (debug >= 2)
418c0b746e5SOllivier Robert 	    printf("waveangle dist %g height %g hops %d angle %g\n",
419c0b746e5SOllivier Robert 		   dg, h, n, delta / RADPERDEG);
420c0b746e5SOllivier Robert 	return delta;
421c0b746e5SOllivier Robert }
422c0b746e5SOllivier Robert 
423c0b746e5SOllivier Robert 
424c0b746e5SOllivier Robert /*
425c0b746e5SOllivier Robert  * propdelay - compute the propagation delay
426c0b746e5SOllivier Robert  */
427c0b746e5SOllivier Robert static double
propdelay(double dg,double h,int n)428c0b746e5SOllivier Robert propdelay(
429c0b746e5SOllivier Robert 	double dg,
430c0b746e5SOllivier Robert 	double h,
431c0b746e5SOllivier Robert 	int n
432c0b746e5SOllivier Robert 	)
433c0b746e5SOllivier Robert {
434c0b746e5SOllivier Robert 	double phi;
435c0b746e5SOllivier Robert 	double theta;
436c0b746e5SOllivier Robert 	double td;
437c0b746e5SOllivier Robert 
438c0b746e5SOllivier Robert 	theta = dg / (EARTHRADIUS * (double)(2 * n));
439c0b746e5SOllivier Robert 	phi = (PI/2.0) - atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2));
440c0b746e5SOllivier Robert 	td = dg / (LIGHTSPEED * sin(phi));
441c0b746e5SOllivier Robert 	if (debug >= 2)
442c0b746e5SOllivier Robert 	    printf("propdelay dist %g height %g hops %d time %g\n",
443c0b746e5SOllivier Robert 		   dg, h, n, td);
444c0b746e5SOllivier Robert 	return td;
445c0b746e5SOllivier Robert }
446c0b746e5SOllivier Robert 
447c0b746e5SOllivier Robert 
448c0b746e5SOllivier Robert /*
449c0b746e5SOllivier Robert  * finddelay - find the propagation delay
450c0b746e5SOllivier Robert  */
451c0b746e5SOllivier Robert static int
finddelay(double lat1,double long1,double lat2,double long2,double h,double * delay)452c0b746e5SOllivier Robert finddelay(
453c0b746e5SOllivier Robert 	double lat1,
454c0b746e5SOllivier Robert 	double long1,
455c0b746e5SOllivier Robert 	double lat2,
456c0b746e5SOllivier Robert 	double long2,
457c0b746e5SOllivier Robert 	double h,
458c0b746e5SOllivier Robert 	double *delay
459c0b746e5SOllivier Robert 	)
460c0b746e5SOllivier Robert {
461c0b746e5SOllivier Robert 	double dg;	/* great circle distance */
462c0b746e5SOllivier Robert 	double delta;	/* wave angle */
463c0b746e5SOllivier Robert 	int n;		/* number of hops */
464c0b746e5SOllivier Robert 
465c0b746e5SOllivier Robert 	dg = greatcircle(lat1, long1, lat2, long2);
466c0b746e5SOllivier Robert 	if (debug)
467c0b746e5SOllivier Robert 	    printf("great circle distance %g km %g miles\n", dg, dg/MILE);
468c0b746e5SOllivier Robert 
469c0b746e5SOllivier Robert 	n = 1;
470c0b746e5SOllivier Robert 	while ((delta = waveangle(dg, h, n)) < 0.0) {
471c0b746e5SOllivier Robert 		if (debug)
472c0b746e5SOllivier Robert 		    printf("tried %d hop%s, no good\n", n, n>1?"s":"");
473c0b746e5SOllivier Robert 		n++;
474c0b746e5SOllivier Robert 	}
475c0b746e5SOllivier Robert 	if (debug)
476c0b746e5SOllivier Robert 	    printf("%d hop%s okay, wave angle is %g\n", n, n>1?"s":"",
477c0b746e5SOllivier Robert 		   delta / RADPERDEG);
478c0b746e5SOllivier Robert 
479c0b746e5SOllivier Robert 	*delay = propdelay(dg, h, n);
480c0b746e5SOllivier Robert 	return n;
481c0b746e5SOllivier Robert }
482c0b746e5SOllivier Robert 
483c0b746e5SOllivier Robert /*
484c0b746e5SOllivier Robert  * satdoit - compute a delay and print it
485c0b746e5SOllivier Robert  */
486c0b746e5SOllivier Robert static void
satdoit(double lat1,double long1,double lat2,double long2,double lat3,double long3,char * str)487c0b746e5SOllivier Robert satdoit(
488c0b746e5SOllivier Robert 	double lat1,
489c0b746e5SOllivier Robert 	double long1,
490c0b746e5SOllivier Robert 	double lat2,
491c0b746e5SOllivier Robert 	double long2,
492c0b746e5SOllivier Robert 	double lat3,
493c0b746e5SOllivier Robert 	double long3,
494c0b746e5SOllivier Robert 	char *str
495c0b746e5SOllivier Robert 	)
496c0b746e5SOllivier Robert {
497c0b746e5SOllivier Robert 	double up_delay,down_delay;
498c0b746e5SOllivier Robert 
499c0b746e5SOllivier Robert 	satfinddelay(lat1, long1, lat2, long2, &up_delay);
500c0b746e5SOllivier Robert 	satfinddelay(lat3, long3, lat2, long2, &down_delay);
501c0b746e5SOllivier Robert 
502c0b746e5SOllivier Robert 	printf("%s, delay %g seconds\n", str, up_delay + down_delay);
503c0b746e5SOllivier Robert }
504c0b746e5SOllivier Robert 
505c0b746e5SOllivier Robert /*
506c0b746e5SOllivier Robert  * satfinddelay - calculate the one-way delay time between a ground station
507c0b746e5SOllivier Robert  * and a satellite
508c0b746e5SOllivier Robert  */
509c0b746e5SOllivier Robert static void
satfinddelay(double lat1,double long1,double lat2,double long2,double * delay)510c0b746e5SOllivier Robert satfinddelay(
511c0b746e5SOllivier Robert 	double lat1,
512c0b746e5SOllivier Robert 	double long1,
513c0b746e5SOllivier Robert 	double lat2,
514c0b746e5SOllivier Robert 	double long2,
515c0b746e5SOllivier Robert 	double *delay
516c0b746e5SOllivier Robert 	)
517c0b746e5SOllivier Robert {
518c0b746e5SOllivier Robert 	double dg;	/* great circle distance */
519c0b746e5SOllivier Robert 
520c0b746e5SOllivier Robert 	dg = greatcircle(lat1, long1, lat2, long2);
521c0b746e5SOllivier Robert 
522c0b746e5SOllivier Robert 	*delay = satpropdelay(dg);
523c0b746e5SOllivier Robert }
524c0b746e5SOllivier Robert 
525c0b746e5SOllivier Robert /*
526c0b746e5SOllivier Robert  * satpropdelay - calculate the one-way delay time between a ground station
527c0b746e5SOllivier Robert  * and a satellite
528c0b746e5SOllivier Robert  */
529c0b746e5SOllivier Robert static double
satpropdelay(double dg)530c0b746e5SOllivier Robert satpropdelay(
531c0b746e5SOllivier Robert 	double dg
532c0b746e5SOllivier Robert 	)
533c0b746e5SOllivier Robert {
534c0b746e5SOllivier Robert 	double k1, k2, dist;
535c0b746e5SOllivier Robert 	double theta;
536c0b746e5SOllivier Robert 	double td;
537c0b746e5SOllivier Robert 
538c0b746e5SOllivier Robert 	theta = dg / (EARTHRADIUS);
539c0b746e5SOllivier Robert 	k1 = EARTHRADIUS * sin(theta);
540c0b746e5SOllivier Robert 	k2 = SATHEIGHT - (EARTHRADIUS * cos(theta));
541c0b746e5SOllivier Robert 	if (debug >= 2)
542c0b746e5SOllivier Robert 	    printf("Theta %g k1 %g k2 %g\n", theta, k1, k2);
543c0b746e5SOllivier Robert 	dist = sqrt(k1*k1 + k2*k2);
544c0b746e5SOllivier Robert 	td = dist / LIGHTSPEED;
545c0b746e5SOllivier Robert 	if (debug >= 2)
546c0b746e5SOllivier Robert 	    printf("propdelay dist %g height %g time %g\n", dg, dist, td);
547c0b746e5SOllivier Robert 	return td;
548c0b746e5SOllivier Robert }
549