xref: /freebsd/contrib/ntp/clockstuff/propdelay.c (revision 9034852c84a13f0e3b5527e1c886ca94b2863b2b)
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
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
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
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
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
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
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
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
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
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
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