1 /* $NetBSD: propdelay.c,v 1.2 2003/12/04 16:23:34 drochner Exp $ */
3 /* propdelay.c,v 3.1 1993/07/06 01:05:24 jbj Exp
4 * propdelay - compute propagation delays
6 * cc -o propdelay propdelay.c -lm
8 * "Time and Frequency Users' Manual", NBS Technical Note 695 (1977).
12 * This can be used to get a rough idea of the HF propagation delay
13 * between two points (usually between you and the radio station).
16 * propdelay latitudeA longitudeA latitudeB longitudeB
18 * where points A and B are the locations in question. You obviously
19 * need to know the latitude and longitude of each of the places.
20 * The program expects the latitude to be preceded by an 'n' or 's'
21 * and the longitude to be preceded by an 'e' or 'w'. It understands
22 * either decimal degrees or degrees:minutes:seconds. Thus to compute
23 * the delay between the WWVH (21:59:26N, 159:46:00W) and WWV (40:40:49N,
24 * 105:02:27W) you could use:
26 * propdelay n21:59:26 w159:46 n40:40:49 w105:02:27
28 * By default it prints out a summer (F2 average virtual height 350 km) and
29 * winter (F2 average virtual height 250 km) number. The results will be
30 * quite approximate but are about as good as you can do with HF time anyway.
31 * You might pick a number between the values to use, or use the summer
32 * value in the summer and switch to the winter value when the static
33 * above 10 MHz starts to drop off in the fall. You can also use the
34 * -h switch if you want to specify your own virtual height.
38 * propdelay -W n45:17:47 w75:45:22
40 * to find the propagation delays to WWV and WWVH (from CHU in this
43 * propdelay -C n40:40:49 w105:02:27
45 * to find the delays to CHU, and a
47 * propdelay -G n52:03:17 w98:34:18
49 * to find delays to GOES via each of the three satellites.
55 #include "ntp_stdlib.h"
57 extern double sin (double);
58 extern double cos (double);
59 extern double acos (double);
60 extern double tan (double);
61 extern double atan (double);
62 extern double sqrt (double);
64 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
69 #define EARTHRADIUS (6370.0) /* raduis of earth (km) */
70 #define LIGHTSPEED (299800.0) /* speed of light, km/s */
71 #define PI (3.1415926536)
72 #define RADPERDEG (PI/180.0) /* radians per degree */
73 #define MILE (1.609344) /* km in a mile */
75 #define SUMMERHEIGHT (350.0) /* summer height in km */
76 #define WINTERHEIGHT (250.0) /* winter height in km */
78 #define SATHEIGHT (6.6110 * 6378.0) /* geosync satellite height in km
79 from centre of earth */
81 #define WWVLAT "n40:40:49"
82 #define WWVLONG "w105:02:27"
84 #define WWVHLAT "n21:59:26"
85 #define WWVHLONG "w159:46:00"
87 #define CHULAT "n45:17:47"
88 #define CHULONG "w75:45:22"
90 #define GOES_UP_LAT "n37:52:00"
91 #define GOES_UP_LONG "w75:27:00"
92 #define GOES_EAST_LONG "w75:00:00"
93 #define GOES_STBY_LONG "w105:00:00"
94 #define GOES_WEST_LONG "w135:00:00"
95 #define GOES_SAT_LAT "n00:00:00"
97 char *wwvlat
= WWVLAT
;
98 char *wwvlong
= WWVLONG
;
100 char *wwvhlat
= WWVHLAT
;
101 char *wwvhlong
= WWVHLONG
;
103 char *chulat
= CHULAT
;
104 char *chulong
= CHULONG
;
106 char *goes_up_lat
= GOES_UP_LAT
;
107 char *goes_up_long
= GOES_UP_LONG
;
108 char *goes_east_long
= GOES_EAST_LONG
;
109 char *goes_stby_long
= GOES_STBY_LONG
;
110 char *goes_west_long
= GOES_WEST_LONG
;
111 char *goes_sat_lat
= GOES_SAT_LAT
;
122 static void doit (double, double, double, double, double, char *);
123 static double latlong (char *, int);
124 static double greatcircle (double, double, double, double);
125 static double waveangle (double, double, int);
126 static double propdelay (double, double, int);
127 static int finddelay (double, double, double, double, double, double *);
128 static void satdoit (double, double, double, double, double, double, char *);
129 static void satfinddelay (double, double, double, double, double *);
130 static double satpropdelay (double);
133 * main - parse arguments and handle options
148 while ((c
= ntp_getopt(argc
, argv
, "dh:CWG")) != EOF
)
155 height
= atof(ntp_optarg
);
157 (void) fprintf(stderr
, "height %s unlikely\n",
175 if (errflg
|| (!(Cflag
|| Wflag
|| Gflag
) && ntp_optind
+4 != argc
) ||
176 ((Cflag
|| Wflag
|| Gflag
) && ntp_optind
+2 != argc
)) {
177 (void) fprintf(stderr
,
178 "usage: %s [-d] [-h height] lat1 long1 lat2 long2\n",
180 (void) fprintf(stderr
," - or -\n");
181 (void) fprintf(stderr
,
182 "usage: %s -CWG [-d] lat long\n",
188 if (!(Cflag
|| Wflag
|| Gflag
)) {
189 lat1
= latlong(argv
[ntp_optind
], 1);
190 long1
= latlong(argv
[ntp_optind
+ 1], 0);
191 lat2
= latlong(argv
[ntp_optind
+ 2], 1);
192 long2
= latlong(argv
[ntp_optind
+ 3], 0);
194 doit(lat1
, long1
, lat2
, long2
, height
, "");
196 doit(lat1
, long1
, lat2
, long2
, (double)SUMMERHEIGHT
,
197 "summer propagation, ");
198 doit(lat1
, long1
, lat2
, long2
, (double)WINTERHEIGHT
,
199 "winter propagation, ");
203 * Compute delay from WWV
205 lat1
= latlong(argv
[ntp_optind
], 1);
206 long1
= latlong(argv
[ntp_optind
+ 1], 0);
207 lat2
= latlong(wwvlat
, 1);
208 long2
= latlong(wwvlong
, 0);
210 doit(lat1
, long1
, lat2
, long2
, height
, "WWV ");
212 doit(lat1
, long1
, lat2
, long2
, (double)SUMMERHEIGHT
,
213 "WWV summer propagation, ");
214 doit(lat1
, long1
, lat2
, long2
, (double)WINTERHEIGHT
,
215 "WWV winter propagation, ");
219 * Compute delay from WWVH
221 lat2
= latlong(wwvhlat
, 1);
222 long2
= latlong(wwvhlong
, 0);
224 doit(lat1
, long1
, lat2
, long2
, height
, "WWVH ");
226 doit(lat1
, long1
, lat2
, long2
, (double)SUMMERHEIGHT
,
227 "WWVH summer propagation, ");
228 doit(lat1
, long1
, lat2
, long2
, (double)WINTERHEIGHT
,
229 "WWVH winter propagation, ");
232 lat1
= latlong(argv
[ntp_optind
], 1);
233 long1
= latlong(argv
[ntp_optind
+ 1], 0);
234 lat2
= latlong(chulat
, 1);
235 long2
= latlong(chulong
, 0);
237 doit(lat1
, long1
, lat2
, long2
, height
, "CHU ");
239 doit(lat1
, long1
, lat2
, long2
, (double)SUMMERHEIGHT
,
240 "CHU summer propagation, ");
241 doit(lat1
, long1
, lat2
, long2
, (double)WINTERHEIGHT
,
242 "CHU winter propagation, ");
245 lat1
= latlong(goes_up_lat
, 1);
246 long1
= latlong(goes_up_long
, 0);
247 lat3
= latlong(argv
[ntp_optind
], 1);
248 long3
= latlong(argv
[ntp_optind
+ 1], 0);
250 lat2
= latlong(goes_sat_lat
, 1);
252 long2
= latlong(goes_west_long
, 0);
253 satdoit(lat1
, long1
, lat2
, long2
, lat3
, long3
,
254 "GOES Delay via WEST");
256 long2
= latlong(goes_stby_long
, 0);
257 satdoit(lat1
, long1
, lat2
, long2
, lat3
, long3
,
258 "GOES Delay via STBY");
260 long2
= latlong(goes_east_long
, 0);
261 satdoit(lat1
, long1
, lat2
, long2
, lat3
, long3
,
262 "GOES Delay via EAST");
270 * doit - compute a delay and print it
285 hops
= finddelay(lat1
, long1
, lat2
, long2
, h
, &delay
);
286 printf("%sheight %g km, hops %d, delay %g seconds\n",
287 str
, h
, hops
, delay
);
292 * latlong - decode a latitude/longitude value
310 * Must be north or south
312 if (*str
== 'N' || *str
== 'n')
314 else if (*str
== 'S' || *str
== 's')
320 * East is positive, west is negative
322 if (*str
== 'E' || *str
== 'e')
324 else if (*str
== 'W' || *str
== 'w')
333 colon
= strchr(str
, ':');
346 colon
= strchr(cp
, ':');
353 arg
+= atof(buf
) / divby
;
357 arg
+= atof(cp
) / divby
;
366 (void) printf("latitude/longitude %s = %g\n", str
, arg
);
373 * greatcircle - compute the great circle distance in kilometers
386 l1r
= lat1
* RADPERDEG
;
387 l2r
= lat2
* RADPERDEG
;
388 dg
= EARTHRADIUS
* acos(
389 (cos(l1r
) * cos(l2r
) * cos((long2
-long1
)*RADPERDEG
))
390 + (sin(l1r
) * sin(l2r
)));
393 "greatcircle lat1 %g long1 %g lat2 %g long2 %g dist %g\n",
394 lat1
, long1
, lat2
, long2
, dg
);
400 * waveangle - compute the wave angle for the given distance, virtual
401 * height and number of hops.
413 theta
= dg
/ (EARTHRADIUS
* (double)(2 * n
));
414 delta
= atan((h
/ (EARTHRADIUS
* sin(theta
))) + tan(theta
/2)) - theta
;
416 printf("waveangle dist %g height %g hops %d angle %g\n",
417 dg
, h
, n
, delta
/ RADPERDEG
);
423 * propdelay - compute the propagation delay
436 theta
= dg
/ (EARTHRADIUS
* (double)(2 * n
));
437 phi
= (PI
/2.0) - atan((h
/ (EARTHRADIUS
* sin(theta
))) + tan(theta
/2));
438 td
= dg
/ (LIGHTSPEED
* sin(phi
));
440 printf("propdelay dist %g height %g hops %d time %g\n",
447 * finddelay - find the propagation delay
459 double dg
; /* great circle distance */
460 double delta
; /* wave angle */
461 int n
; /* number of hops */
463 dg
= greatcircle(lat1
, long1
, lat2
, long2
);
465 printf("great circle distance %g km %g miles\n", dg
, dg
/MILE
);
468 while ((delta
= waveangle(dg
, h
, n
)) < 0.0) {
470 printf("tried %d hop%s, no good\n", n
, n
>1?"s":"");
474 printf("%d hop%s okay, wave angle is %g\n", n
, n
>1?"s":"",
477 *delay
= propdelay(dg
, h
, n
);
482 * satdoit - compute a delay and print it
495 double up_delay
,down_delay
;
497 satfinddelay(lat1
, long1
, lat2
, long2
, &up_delay
);
498 satfinddelay(lat3
, long3
, lat2
, long2
, &down_delay
);
500 printf("%s, delay %g seconds\n", str
, up_delay
+ down_delay
);
504 * satfinddelay - calculate the one-way delay time between a ground station
516 double dg
; /* great circle distance */
518 dg
= greatcircle(lat1
, long1
, lat2
, long2
);
520 *delay
= satpropdelay(dg
);
524 * satpropdelay - calculate the one-way delay time between a ground station
536 theta
= dg
/ (EARTHRADIUS
);
537 k1
= EARTHRADIUS
* sin(theta
);
538 k2
= SATHEIGHT
- (EARTHRADIUS
* cos(theta
));
540 printf("Theta %g k1 %g k2 %g\n", theta
, k1
, k2
);
541 dist
= sqrt(k1
*k1
+ k2
*k2
);
542 td
= dist
/ LIGHTSPEED
;
544 printf("propdelay dist %g height %g time %g\n", dg
, dist
, td
);