FlightGear next
itm.cpp
Go to the documentation of this file.
1/*****************************************************************************\
2 * *
3 * The following code was derived from public domain ITM code available *
4 * at ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp that was released on *
5 * June 26, 2007. It was modified to remove Microsoft Windows "dll-isms", *
6 * redundant and unnecessary #includes, redundant and unnecessary { }'s, *
7 * to initialize uninitialized variables, type cast some variables, *
8 * re-format the code for easier reading, and to replace pow() function *
9 * calls with explicit multiplications wherever possible to increase *
10 * execution speed and improve computational accuracy. *
11 * *
12 *****************************************************************************
13 * *
14 * Added comments that refer to itm.pdf and itmalg.pdf in a way to easy *
15 * searching. *
16 * *
17 * // :0: Blah, page 0 This is the implementation of the code from *
18 * itm.pdf, section "0". The description is *
19 * found on page 0. *
20 * [Alg 0.0] please refer to algorithm 0.0 from itm_alg.pdf *
21 * *
22 * Holger Schurig, DH3HS *
23 * *
24 *****************************************************************************
25 * *
26 * 2019.07.08 - Fix shadowing of static variable in adiff() function *
27 * Ferran Obón Santacana *
28 * *
29\*****************************************************************************/
30
31
32// *************************************
33// C++ routines for this program are taken from
34// a translation of the FORTRAN code written by
35// U.S. Department of Commerce NTIA/ITS
36// Institute for Telecommunication Sciences
37// *****************
38// Irregular Terrain Model (ITM) (Longley-Rice)
39// *************************************
40
41
42
43#include <math.h>
44#include <complex>
45#include <assert.h>
46#include <stdio.h>
47
48
49using namespace std;
50
51namespace ITM {
52
53const double THIRD = (1.0/3.0);
54const double f_0 = 47.7; // 47.7 MHz from [Alg 1.1], to convert frequency into wavenumber and vica versa
55
56
57struct prop_type {
58 // General input
59 double d; // distance between the two terminals
60 double h_g[2]; // antenna structural heights (above ground)
61 double k; // wave number (= radio frequency)
62 double delta_h; // terrain irregularity parameter
63 double N_s; // minimum monthly surface refractivity, n-Units
64 double gamma_e; // earth's effective curvature
65 double Z_g_real; // surface transfer impedance of the ground
66 double Z_g_imag;
67 // Additional input for point-to-point mode
68 double h_e[2]; // antenna effective heights
69 double d_Lj[2]; // horizon distances
70 double theta_ej[2];// horizontal elevation angles
71 int mdp; // controlling mode: -1: point to point, 1 start of area, 0 area continuation
72 // Output
73 int kwx; // error indicator
74 double A_ref; // reference attenuation
75 // used to be propa_type, computed in lrprop()
76 double dx; // scatter distance
77 double ael; // line-of-sight coefficients
78 double ak1; // dito
79 double ak2; // dito
80 double aed; // diffraction coefficients
81 double emd; // dito
82 double aes; // scatter coefficients
83 double ems; // dito
84 double d_Lsj[2]; // smooth earth horizon distances
85 double d_Ls; // d_Lsj[] accumulated
86 double d_L; // d_Lj[] accumulated
87 double theta_e; // theta_ej[] accumulated, total bending angle
88};
89
90
91static
92int mymin(const int &i, const int &j)
93{
94 if (i < j)
95 return i;
96 else
97 return j;
98}
99
100
101static
102int mymax(const int &i, const int &j)
103{
104 if (i > j)
105 return i;
106 else
107 return j;
108}
109
110
111static
112double mymin(const double &a, const double &b)
113{
114 if (a < b)
115 return a;
116 else
117 return b;
118}
119
120
121static
122double mymax(const double &a, const double &b)
123{
124 if (a > b)
125 return a;
126 else
127 return b;
128}
129
130
131static
132double FORTRAN_DIM(const double &x, const double &y)
133{
134 // This performs the FORTRAN DIM function.
135 // result is x-y if x is greater than y; otherwise result is 0.0
136
137 if (x > y)
138 return x - y;
139 else
140 return 0.0;
141}
142
143//#define set_warn(txt, err) printf("%s:%d %s\n", __func__, __LINE__, txt);
144#define set_warn(txt, err)
145
146// :13: single-knife attenuation, page 6
147/*
148 * The attenuation due to a sigle knife edge -- this is an approximation of
149 * a Fresnel integral as a function of v^2. The non-approximated integral
150 * is documented as [Alg 4.21]
151 *
152 * Now, what is "single knife attenuation" ? Googling found some paper
153 * http://www.its.bldrdoc.gov/pub/ntia-rpt/81-86/81-86.pdf, which actually
154 * talks about multi-knife attenuation calculation. However, it says there
155 * that single-knife attenuation models attenuation over the edge of one
156 * isolated hill.
157 *
158 * Note that the arguments to this function aren't v, but v^2
159 */
160static
161double Fn(const double &v_square)
162{
163 double a;
164
165 // [Alg 6.1]
166 if (v_square <= 5.76) // this is the 2.40 from the text, but squared
167 a = 6.02 + 9.11 * sqrt(v_square) - 1.27 * v_square;
168 else
169 a = 12.953 + 4.343 * log(v_square);
170
171 return a;
172}
173
174
175// :14: page 6
176/*
177 * The heigh-gain over a smooth spherical earth -- to be used in the "three
178 * radii" mode. The approximation is that given in in [Alg 6.4ff].
179 */
180static
181double F(const double& x, const double& K)
182{
183 double w, fhtv;
184 if (x <= 200.0) {
185 // F = F_2(x, L), which is defined in [Alg 6.6]
186
187 w = -log(K);
188
189 // XXX the text says "or x * w^3 > 450"
190 if (K < 1e-5 || (x * w * w * w) > 5495.0) {
191 // F_2(x, k) = F_1(x), which is defined in [Alg 6.5]
192 // XXX but this isn't the same as in itm_alg.pdf
193 fhtv = -117.0;
194 if (x > 1.0)
195 fhtv = 17.372 * log(x) + fhtv;
196 } else {
197 // [Alg 6.6], lower part
198 fhtv = 2.5e-5 * x * x / K - 8.686 * w - 15.0;
199 }
200 } else {
201 // [Alg 6.3] and [Alg 6.4], lower part, which is G(x)
202 fhtv = 0.05751 * x - 4.343 * log(x);
203
204 // [Alg 6.4], middle part, but again XXX this doesn't match totally
205 if (x < 2000.0) {
206 w = 0.0134 * x * exp(-0.005 * x);
207 fhtv = (1.0 - w) * fhtv + w * (17.372 * log(x) - 117.0);
208 }
209 }
210
211 return fhtv;
212}
213
214
215// :25: Tropospheric scatter frequency gain, [Alg 6.10ff], page 12
216static
217double H_0(double r, double et)
218{
219 // constants from [Alg 6.13]
220 const double a[5] = {25.0, 80.0, 177.0, 395.0, 705.0};
221 const double b[5] = {24.0, 45.0, 68.0, 80.0, 105.0};
222 double q, x;
223 int it;
224 double h0fv;
225
226 it = (int)et;
227
228 if (it <= 0) {
229 it = 1;
230 q = 0.0;
231 } else
232 if (it >= 4) {
233 it = 4;
234 q = 0.0;
235 } else {
236 q = et - it;
237 }
238
239 x = (1.0 / r);
240 x *= x;
241 // [Alg 6.13], calculates something like H_01(r,j), but not really XXX
242 h0fv = 4.343 * log((1.0 + a[it-1] * x + b[it-1]) * x);
243
244 // XXX not sure what this means
245 if (q != 0.0)
246 h0fv = (1.0 - q) * h0fv + q * 4.343 * log((a[it] * x + b[it]) * x + 1.0);
247
248 return h0fv;
249}
250
251
252// :25: This is the F(\Theta d) function for scatter fields, page 12
253static
254double F_0(double td)
255{
256 // [Alg 6.9]
257 if (td <= 10e3) // below 10 km
258 return 133.4 + 104.6 * td + 71.8 * log(td);
259 else
260 if (td <= 70e3) // between 10 km and 70 km
261 return 0.332e-3 + 0.212e-3 * td + 0.157e-3 * log(td);
262 else // above 70 km
263 return-4.343 + -1.086 * td + 2.171 * log(td);
264}
265
266
267// :10: Diffraction attenuation, page 4
268static
269double adiff(double s, prop_type &prop)
270{
271 /*
272 * The function adiff finds the "Diffraction attenuation" at the
273 * distance s. It uses a convex combination of smooth earth
274 * diffraction and knife-edge diffraction.
275 */
276 static double wd1, xd1, A_fo, qk, aht, xht;
277 const double A = 151.03; // dimensionles constant from [Alg 4.20]
278 const double D = 50e3; // 50 km from [Alg 3.9], scale distance for \delta_h(s)
279 const double H = 16; // 16 m from [Alg 3.10]
280 const double ALPHA = 4.77e-4; // from [Alg 4.10]
281
282 if (s == 0.0) {
283 complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
284
285 // :11: Prepare initial diffraction constants, page 5
286 double q = prop.h_g[0] * prop.h_g[1];
287 qk = prop.h_e[0] * prop.h_e[1] - q;
288
289 if (prop.mdp < 0.0)
290 q += 10.0; // "C" from [Alg 4.9]
291
292 // wd1 and xd1 are parts of Q in [Alg 4.10], but I cannot find this there
293 wd1 = sqrt(1.0 + qk / q);
294 xd1 = prop.d_L + prop.theta_e / prop.gamma_e; // [Alg 4.9] upper right
295 // \delta_h(s), [Alg 3.9]
296 q = (1.0 - 0.8 * exp(-prop.d_Ls / D)) * prop.delta_h;
297 // \sigma_h(s), [Alg 3.10]
298 q *= 0.78 * exp(-pow(q / H, 0.25));
299
300 // A_fo is the "clutter factor"
301 A_fo = mymin(15.0, 2.171 * log(1.0 + ALPHA * prop.h_g[0] * prop.h_g[1] * prop.k * q)); // [Alg 4.10]
302 // qk is part of the K_j calculation from [Alg 4.17]
303 qk = 1.0 / abs(prop_zgnd);
304 aht = 20.0; // 20 dB approximation for C_1(K) from [Alg 6.7], see also [Alg 4.25]
305 xht = 0.0;
306
307 for (int j = 0; j < 2; ++j) {
308 double gamma_j_recip, alpha, K;
309 // [Alg 4.15], a is reciproke of gamma_j
310 gamma_j_recip = 0.5 * (prop.d_Lj[j] * prop.d_Lj[j]) / prop.h_e[j];
311 // [Alg 4.16]
312 alpha = pow(gamma_j_recip * prop.k, THIRD);
313 // [Alg 4.17]
314 K = qk / alpha;
315 // [Alg 4.18 and 6.2]
316 q = A * (1.607 - K) * alpha * prop.d_Lj[j] / gamma_j_recip;
317 // [Alg 4.19, high gain part]
318 xht += q;
319 // [Alg 4.20] ?, F(x, k) is in [Alg 6.4]
320 aht += F(q, K);
321 }
322 return 0.0;
323 }
324
325 double gamma_o_recip, q, K, ds, theta, alpha, A_r, w;
326
327 // :12: Compute diffraction attenuation, page 5
328 // [Alg 4.12]
329 theta = prop.theta_e + s * prop.gamma_e;
330 // XXX this is not [Alg 4.13]
331 ds = s - prop.d_L;
332 q = 0.0795775 * prop.k * ds * theta * theta;
333 // [Alg 4.14], double knife edge attenuation
334 // Note that the arguments to Fn() are not v, but v^2
335 double A_k = Fn(q * prop.d_Lj[0] / (ds + prop.d_Lj[0])) +
336 Fn(q * prop.d_Lj[1] / (ds + prop.d_Lj[1]));
337 // kinda [Alg 4.15], just so that gamma_o is 1/a
338 gamma_o_recip = ds / theta;
339 // [Alg 4.16]
340 alpha = pow(gamma_o_recip * prop.k, THIRD);
341 // [Alg 4.17], note that qk is "1.0 / abs(prop_zgnd)" from above
342 K = qk / alpha;
343 // [Alg 4.19], q is now X_o
344 q = A * (1.607 - K) * alpha * theta + xht;
345 // looks a bit like [Alg 4.20], rounded earth attenuation, or??
346 // note that G(x) should be "0.05751 * x - 10 * log(q)"
347 A_r = 0.05751 * q - 4.343 * log(q) - aht;
348 // I'm very unsure if this has anything to do with [Alg 4.9] or not
349 q = (wd1 + xd1 / s) * mymin(((1.0 - 0.8 * exp(-s / 50e3)) * prop.delta_h * prop.k), 6283.2);
350 // XXX this is NOT the same as the weighting factor from [Alg 4.9]
351 w = 25.1 / (25.1 + sqrt(q));
352 // [Alg 4.11]
353 return (1.0 - w) * A_k + w * A_r + A_fo;
354}
355
356
357/*
358 * :22: Scatter attenuation, page 9
359 *
360 * The function ascat finds the "scatter attenuation" at the distance d. It
361 * uses an approximation to the methods of NBS TN101 with check for
362 * inadmissable situations. For proper operation, the larger distance (d =
363 * d6) must be the first called. A call with d = 0 sets up initial
364 * constants.
365 *
366 * One needs to get TN101, especially chaper 9, to understand this function.
367 */
368static
369double A_scat(double s, prop_type &prop)
370{
371 static double ad, rr, etq, h0s;
372
373 if (s == 0.0) {
374 // :23: Prepare initial scatter constants, page 10
375 ad = prop.d_Lj[0] - prop.d_Lj[1];
376 rr = prop.h_e[1] / prop.h_e[0];
377
378 if (ad < 0.0) {
379 ad = -ad;
380 rr = 1.0 / rr;
381 }
382
383 etq = (5.67e-6 * prop.N_s - 2.32e-3) * prop.N_s + 0.031; // part of [Alg 4.67]
384 h0s = -15.0;
385 return 0.0;
386 }
387
388 double h0, r1, r2, z0, ss, et, ett, theta_tick, q, temp;
389
390 // :24: Compute scatter attenuation, page 11
391 if (h0s > 15.0)
392 h0 = h0s;
393 else {
394 // [Alg 4.61]
395 theta_tick = prop.theta_ej[0] + prop.theta_ej[1] + prop.gamma_e * s;
396 // [Alg 4.62]
397 r2 = 2.0 * prop.k * theta_tick;
398 r1 = r2 * prop.h_e[0];
399 r2 *= prop.h_e[1];
400
401 if (r1 < 0.2 && r2 < 0.2)
402 // The function is undefined
403 return 1001.0;
404
405 // XXX not like [Alg 4.65]
406 ss = (s - ad) / (s + ad);
407 q = rr / ss;
408 ss = mymax(0.1, ss);
409 q = mymin(mymax(0.1, q), 10.0);
410 // XXX not like [Alg 4.66]
411 z0 = (s - ad) * (s + ad) * theta_tick * 0.25 / s;
412 // [Alg 4.67]
413 temp = mymin(1.7, z0 / 8.0e3);
414 temp = temp * temp * temp * temp * temp * temp;
415 et = (etq * exp(-temp) + 1.0) * z0 / 1.7556e3;
416
417 ett = mymax(et, 1.0);
418 h0 = (H_0(r1, ett) + H_0(r2, ett)) * 0.5; // [Alg 6.12]
419 h0 += mymin(h0, (1.38 - log(ett)) * log(ss) * log(q) * 0.49); // [Alg 6.10 and 6.11]
420 h0 = FORTRAN_DIM(h0, 0.0);
421
422 if (et < 1.0)
423 // [Alg 6.14]
424 h0 = et * h0 + (1.0 - et) * 4.343 * log(pow((1.0 + 1.4142 / r1) * (1.0 + 1.4142 / r2), 2.0) * (r1 + r2) / (r1 + r2 + 2.8284));
425
426 if (h0 > 15.0 && h0s >= 0.0)
427 h0 = h0s;
428 }
429
430 h0s = h0;
431 double theta = prop.theta_e + s * prop.gamma_e; // [Alg 4.60]
432 // [Alg 4.63 and 6.8]
433 const double D_0 = 40e3; // 40 km from [Alg 6.8]
434 const double H = 47.7; // 47.7 m from [Alg 4.63]
435 return 4.343 * log(prop.k * H * theta * theta * theta * theta)
436 + F_0(theta * s)
437 - 0.1 * (prop.N_s - 301.0) * exp(-theta * s / D_0)
438 + h0;
439}
440
441
442static
443double abq_alos(complex<double> r)
444{
445 return r.real() * r.real() + r.imag() * r.imag();
446}
447
448
449/*
450 * :17: line-of-sight attenuation, page 8
451 *
452 * The function alos finds the "line-of-sight attenuation" at the distance
453 * d. It uses a convex combination of plane earth fields and diffracted
454 * fields. A call with d=0 sets up initial constants.
455 */
456static
457double A_los(double d, prop_type &prop)
458{
459 static double wls;
460
461 if (d == 0.0) {
462 // :18: prepare initial line-of-sight constants, page 8
463 const double D1 = 47.7; // 47.7 m from [Alg 4.43]
464 const double D2 = 10e3; // 10 km from [Alg 4.43]
465 const double D1R = 1 / D1;
466 // weighting factor w
467 wls = D1R / (D1R + prop.k * prop.delta_h / mymax(D2, prop.d_Ls)); // [Alg 4.43]
468 return 0;
469 }
470
471 complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
472 complex<double> r;
473 double s, sps, q;
474
475 // :19: compute line of sight attentuation, page 8
476 const double D = 50e3; // 50 km from [Alg 3.9]
477 const double H = 16.0; // 16 m from [Alg 3.10]
478 q = (1.0 - 0.8 * exp(-d / D)) * prop.delta_h; // \Delta h(d), [Alg 3.9]
479 s = 0.78 * q * exp(-pow(q / H, 0.25)); // \sigma_h(d), [Alg 3.10]
480 q = prop.h_e[0] + prop.h_e[1];
481 sps = q / sqrt(d * d + q * q); // sin(\psi), [Alg 4.46]
482 r = (sps - prop_zgnd) / (sps + prop_zgnd) * exp(-mymin(10.0, prop.k * s * sps)); // [Alg 4.47]
483 q = abq_alos(r);
484
485 if (q < 0.25 || q < sps) // [Alg 4.48]
486 r = r * sqrt(sps / q);
487
488 double alosv = prop.emd * d + prop.aed; // [Alg 4.45]
489 q = prop.k * prop.h_e[0] * prop.h_e[1] * 2.0 / d; // [Alg 4.49]
490
491 // M_PI is pi, M_PI_2 is pi/2
492 if (q > M_PI_2) // [Alg 4.50]
493 q = M_PI - (M_PI_2 * M_PI_2) / q;
494
495 // [Alg 4.51 and 4.44]
496 return (-4.343 * log(abq_alos(complex<double>(cos(q), -sin(q)) + r)) - alosv) * wls + alosv;
497}
498
499
500// :5: LRprop, page 2
501/*
502 * The value mdp controls some of the program flow. When it equals -1 we are
503 * in point-to-point mode, when 1 we are beginning the area mode, and when 0
504 * we are continuing the area mode. The assumption is that when one uses the
505 * area mode, one will want a sequence of results for varying distances.
506 */
507static
508void lrprop(double d, prop_type &prop)
509{
510 static bool wlos, wscat;
511 static double dmin, xae;
512 complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
513 double a0, a1, a2, a3, a4, a5, a6;
514 double d0, d1, d2, d3, d4, d5, d6;
515 bool wq;
516 double q;
517 int j;
518
519
520 if (prop.mdp != 0) {
521 // :6: Do secondary parameters, page 3
522 // [Alg 3.5]
523 for (j = 0; j < 2; j++)
524 prop.d_Lsj[j] = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
525
526 // [Alg 3.6]
527 prop.d_Ls = prop.d_Lsj[0] + prop.d_Lsj[1];
528 // [Alg 3.7]
529 prop.d_L = prop.d_Lj[0] + prop.d_Lj[1];
530 // [Alg 3.8]
531 prop.theta_e = mymax(prop.theta_ej[0] + prop.theta_ej[1], -prop.d_L * prop.gamma_e);
532 wlos = false;
533 wscat = false;
534
535 // :7: Check parameters range, page 3
536
537 // kwx is some kind of error indicator. Setting kwx to 1
538 // means "results are slightly bad", while setting it to 4
539 // means "my calculations will be bogus"
540
541 // Frequency must be between 40 MHz and 10 GHz
542 // Wave number (wn) = 2 * M_PI / lambda, or f/f0, where f0 = 47.7 MHz*m;
543 // 0.838 => 40 MHz, 210 => 10 GHz
544 if (prop.k < 0.838 || prop.k > 210.0) {
545 set_warn("Frequency not optimal", 1);
546 prop.kwx = mymax(prop.kwx, 1);
547 }
548
549 // Surface refractivity, see [Alg 1.2]
550 if (prop.N_s < 250.0 || prop.N_s > 400.0) {
551 set_warn("Surface refractivity out-of-bounds", 4);
552 prop.kwx = 4;
553 } else
554 // Earth's effective curvature, see [Alg 1.3]
555 if (prop.gamma_e < 75e-9 || prop.gamma_e > 250e-9) {
556 set_warn("Earth's curvature out-of-bounds", 4);
557 prop.kwx = 4;
558 } else
559 // Surface transfer impedance, see [Alg 1.4]
560 if (prop_zgnd.real() <= abs(prop_zgnd.imag())) {
561 set_warn("Surface transfer impedance out-of-bounds", 4);
562 prop.kwx = 4;
563 } else
564 // Calulating outside of 20 MHz to 40 GHz is really bad
565 if (prop.k < 0.419 || prop.k > 420.0) {
566 set_warn("Frequency out-of-bounds", 4);
567 prop.kwx = 4;
568 } else
569 for (j = 0; j < 2; j++) {
570 // Antenna structural height should be between 1 and 1000 m
571 if (prop.h_g[j] < 1.0 || prop.h_g[j] > 1000.0) {
572 set_warn("Antenna height not optimal", 1);
573 prop.kwx = mymax(prop.kwx, 1);
574 }
575
576 // Horizon elevation angle
577 if (abs(prop.theta_ej[j]) > 200e-3) {
578 set_warn("Horizon elevation weird", 3);
579 prop.kwx = mymax(prop.kwx, 3);
580 }
581
582 // Horizon distance dl,
583 // Smooth earth horizon distance dls
584 if (prop.d_Lj[j] < 0.1 * prop.d_Lsj[j] ||
585 prop.d_Lj[j] > 3.0 * prop.d_Lsj[j]) {
586 set_warn("Horizon distance weird", 3);
587 prop.kwx = mymax(prop.kwx, 3);
588 }
589 // Antenna structural height must between 0.5 and 3000 m
590 if (prop.h_g[j] < 0.5 || prop.h_g[j] > 3000.0) {
591 set_warn("Antenna heights out-of-bounds", 4);
592 prop.kwx = 4;
593 }
594 }
595
596 dmin = abs(prop.h_e[0] - prop.h_e[1]) / 200e-3;
597
598 // :9: Diffraction coefficients, page 4
599 /*
600 * This is the region beyond the smooth-earth horizon at
601 * D_Lsa and short of where isotropic scatter takes over. It
602 * is a key to central region and associated coefficients
603 * must always be computed.
604 */
605 q = adiff(0.0, prop);
606 xae = pow(prop.k * prop.gamma_e * prop.gamma_e, -THIRD); // [Alg 4.2]
607 d3 = mymax(prop.d_Ls, 1.3787 * xae + prop.d_L); // [Alg 4.3]
608 d4 = d3 + 2.7574 * xae; // [Alg 4.4]
609 a3 = adiff(d3, prop); // [Alg 4.5]
610 a4 = adiff(d4, prop); // [Alg 4.7]
611
612 prop.emd = (a4 - a3) / (d4 - d3); // [Alg 4.8]
613 prop.aed = a3 - prop.emd * d3;
614 }
615
616 if (prop.mdp >= 0) {
617 prop.mdp = 0;
618 prop.d = d;
619 }
620
621 if (prop.d > 0.0) {
622 // :8: Check distance, page 3
623
624 // Distance above 1000 km is guessing
625 if (prop.d > 1000e3) {
626 set_warn("Distance not optimal", 1);
627 prop.kwx = mymax(prop.kwx, 1);
628 }
629
630 // Distance too small, use some indoor algorithm :-)
631 if (prop.d < dmin) {
632 set_warn("Distance too small", 3);
633 prop.kwx = mymax(prop.kwx, 3);
634 }
635
636 // Distance above 2000 km is really bad, don't do that
637 if (prop.d < 1e3 || prop.d > 2000e3) {
638 set_warn("Distance out-of-bounds", 4);
639 prop.kwx = 4;
640 }
641 }
642
643 if (prop.d < prop.d_Ls) {
644 // :15: Line-of-sight calculations, page 7
645 if (!wlos) {
646 // :16: Line-of-sight coefficients, page 7
647 q = A_los(0.0, prop);
648 d2 = prop.d_Ls;
649 a2 = prop.aed + d2 * prop.emd;
650 d0 = 1.908 * prop.k * prop.h_e[0] * prop.h_e[1]; // [Alg 4.38]
651
652 if (prop.aed >= 0.0) {
653 d0 = mymin(d0, 0.5 * prop.d_L); // [Alg 4.28]
654 d1 = d0 + 0.25 * (prop.d_L - d0); // [Alg 4.29]
655 } else {
656 d1 = mymax(-prop.aed / prop.emd, 0.25 * prop.d_L); // [Alg 4.39]
657 }
658
659 a1 = A_los(d1, prop); // [Alg 4.31]
660 wq = false;
661
662 if (d0 < d1) {
663 a0 = A_los(d0, prop); // [Alg 4.30]
664 q = log(d2 / d0);
665 prop.ak2 = mymax(0.0, ((d2 - d0) * (a1 - a0) - (d1 - d0) * (a2 - a0)) / ((d2 - d0) * log(d1 / d0) - (d1 - d0) * q)); // [Alg 4.32]
666 wq = prop.aed >= 0.0 || prop.ak2 > 0.0;
667
668 if (wq) {
669 prop.ak1 = (a2 - a0 - prop.ak2 * q) / (d2 - d0); // [Alg 4.33]
670
671 if (prop.ak1 < 0.0) {
672 prop.ak1 = 0.0; // [Alg 4.36]
673 prop.ak2 = FORTRAN_DIM(a2, a0) / q; // [Alg 4.35]
674
675 if (prop.ak2 == 0.0) // [Alg 4.37]
676 prop.ak1 = prop.emd;
677 }
678 }
679 }
680
681 if (!wq) {
682 prop.ak1 = FORTRAN_DIM(a2, a1) / (d2 - d1); // [Alg 4.40]
683 prop.ak2 = 0.0; // [Alg 4.41]
684
685 if (prop.ak1 == 0.0) // [Alg 4.37]
686 prop.ak1 = prop.emd;
687 }
688
689 prop.ael = a2 - prop.ak1 * d2 - prop.ak2 * log(d2); // [Alg 4.42]
690 wlos = true;
691 }
692
693 if (prop.d > 0.0)
694 // [Alg 4.1]
695 /*
696 * The reference attenuation is determined as a function of the distance
697 * d from 3 piecewise formulatios. This is part one.
698 */
699 prop.A_ref = prop.ael + prop.ak1 * prop.d + prop.ak2 * log(prop.d);
700 }
701
702 if (prop.d <= 0.0 || prop.d >= prop.d_Ls) {
703 // :20: Troposcatter calculations, page 9
704 if (!wscat) {
705 // :21: Troposcatter coefficients
706 const double DS = 200e3; // 200 km from [Alg 4.53]
707 const double HS = 47.7; // 47.7 m from [Alg 4.59]
708 q = A_scat(0.0, prop);
709 d5 = prop.d_L + DS; // [Alg 4.52]
710 d6 = d5 + DS; // [Alg 4.53]
711 a6 = A_scat(d6, prop); // [Alg 4.54]
712 a5 = A_scat(d5, prop); // [Alg 4.55]
713
714 if (a5 < 1000.0) {
715 prop.ems = (a6 - a5) / DS; // [Alg 4.57]
716 prop.dx = mymax(prop.d_Ls, // [Alg 4.58]
717 mymax(prop.d_L + 0.3 * xae * log(HS * prop.k),
718 (a5 - prop.aed - prop.ems * d5) / (prop.emd - prop.ems)));
719 prop.aes = (prop.emd - prop.ems) * prop.dx + prop.aed; // [Alg 4.59]
720 } else {
721 prop.ems = prop.emd;
722 prop.aes = prop.aed;
723 prop.dx = 10.e6; // [Alg 4.56]
724 }
725 wscat = true;
726 }
727
728 // [Alg 4.1], part two and three.
729 if (prop.d > prop.dx)
730 prop.A_ref = prop.aes + prop.ems * prop.d; // scatter region
731 else
732 prop.A_ref = prop.aed + prop.emd * prop.d; // diffraction region
733 }
734
735 prop.A_ref = mymax(prop.A_ref, 0.0);
736}
737
738
739/*****************************************************************************/
740
741// :27: Variablility parameters, page 13
742
744 // Input:
745 int lvar; // control switch for initialisation and/or recalculation
746 // 0: no recalculations needed
747 // 1: distance changed
748 // 2: antenna heights changed
749 // 3: frequency changed
750 // 4: mdvar changed
751 // 5: climate changed, or initialize everything
752 int mdvar; // desired mode of variability
753 int klim; // climate indicator
754 // Output
755 double sgc; // standard deviation of situation variability (confidence)
756};
757
758
759#ifdef WITH_QRLA
760// :40: Prepare model for area prediction mode, page 21
761/*
762 * This is used to prepare the model in the area prediction mode. Normally,
763 * one first calls qlrps() and then qlra(). Before calling the latter, one
764 * should have defined in prop_type the antenna heights @hg, the terrain
765 * irregularity parameter @dh , and (probably through qlrps() the variables
766 * @wn, @ens, @gme, and @zgnd. The input @kst will define siting criteria
767 * for the terminals, @klimx the climate, and @mdvarx the mode of
768 * variability. If @klimx <= 0 or @mdvarx < 0 the associated parameters
769 * remain unchanged.
770 */
771static
772void qlra(int kst[], int klimx, int mdvarx, prop_type &prop, propv_type &propv)
773{
774 double q;
775
776 for (int j = 0; j < 2; ++j) {
777 if (kst[j] <= 0)
778 // [Alg 3.1]
779 prop.h_e[j] = prop.h_g[j];
780 else {
781 q = 4.0;
782
783 if (kst[j] != 1)
784 q = 9.0;
785
786 if (prop.h_g[j] < 5.0)
787 q *= sin(0.3141593 * prop.h_g[j]);
788
789 prop.h_e[j] = prop.h_g[j] + (1.0 + q) * exp(-mymin(20.0, 2.0 * prop.h_g[j] / mymax(1e-3, prop.delta_h)));
790 }
791
792 // [Alg 3.3], upper function. q is d_Ls_j
793 const double H_3 = 5; // 5m from [Alg 3.3]
794 q = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
795 prop.d_Lj[j] = q * exp(-0.07 * sqrt(prop.delta_h / mymax(prop.h_e[j], H_3)));
796 // [Alg 3.4]
797 prop.theta_ej[j] = (0.65 * prop.delta_h * (q / prop.d_Lj[j] - 1.0) - 2.0 * prop.h_e[j]) / q;
798 }
799
800 prop.mdp = 1;
801 propv.lvar = mymax(propv.lvar, 3);
802
803 if (mdvarx >= 0) {
804 propv.mdvar = mdvarx;
805 propv.lvar = mymax(propv.lvar, 4);
806 }
807
808 if (klimx > 0) {
809 propv.klim = klimx;
810 propv.lvar = 5;
811 }
812}
813#endif
814
815
816// :51: Inverse of standard normal complementary probability
817/*
818 * The approximation is due to C. Hastings, Jr. ("Approximations for digital
819 * computers," Princeton Univ. Press, 1955) and the maximum error should be
820 * 4.5e-4.
821 */
822#if 1
823static
824double qerfi(double q)
825{
826 double x, t, v;
827 const double c0 = 2.515516698;
828 const double c1 = 0.802853;
829 const double c2 = 0.010328;
830 const double d1 = 1.432788;
831 const double d2 = 0.189269;
832 const double d3 = 0.001308;
833
834 x = 0.5 - q;
835 t = mymax(0.5 - fabs(x), 0.000001);
836 t = sqrt(-2.0 * log(t));
837 v = t - ((c2 * t + c1) * t + c0) / (((d3 * t + d2) * t + d1) * t + 1.0);
838
839 if (x < 0.0)
840 v = -v;
841
842 return v;
843}
844#endif
845
846
847// :41: preparatory routine, page 20
848/*
849 * This subroutine converts
850 * frequency @fmhz
851 * surface refractivity reduced to sea level @en0
852 * general system elevation @zsys
853 * polarization and ground constants @eps, @sgm
854 * to
855 * wave number @wn,
856 * surface refractivity @ens
857 * effective earth curvature @gme
858 * surface impedance @zgnd
859 * It may be used with either the area prediction or the point-to-point mode.
860 */
861#if 1
862static
863void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, double sgm, prop_type &prop)
864
865{
866 const double Z_1 = 9.46e3; // 9.46 km from [Alg 1.2]
867 const double gma = 157e-9; // 157e-9 1/m from [Alg 1.3]
868 const double N_1 = 179.3; // 179.3 N-units from [Alg 1.3]
869 const double Z_0 = 376.62; // 376.62 Ohm from [Alg 1.5]
870
871 // Frequecy -> Wave number
872 prop.k = fmhz / f_0; // [Alg 1.1]
873
874 // Surface refractivity
875 prop.N_s = en0;
876 if (zsys != 0.0)
877 prop.N_s *= exp(-zsys / Z_1); // [Alg 1.2]
878
879 // Earths effective curvature
880 prop.gamma_e = gma * (1.0 - 0.04665 * exp(prop.N_s / N_1)); // [Alg 1.3]
881
882 // Surface transfer impedance
883 complex<double> zq, prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
884 zq = complex<double> (eps, Z_0 * sgm / prop.k); // [Alg 1.5]
885 prop_zgnd = sqrt(zq - 1.0);
886
887 // adjust surface transfer impedance for Polarization
888 if (ipol != 0.0)
889 prop_zgnd = prop_zgnd / zq; // [Alg 1.4]
890
891 prop.Z_g_real = prop_zgnd.real();
892 prop.Z_g_imag = prop_zgnd.imag();
893}
894#endif
895
896
897// :30: Function curv, page 15
898static
899double curve(double const &c1, double const &c2, double const &x1, double const &x2, double const &x3, double const &de)
900{
901 double temp1, temp2;
902
903 temp1 = (de - x2) / x3;
904 temp2 = de / x1;
905
906 temp1 *= temp1;
907 temp2 *= temp2;
908
909 return (c1 + c2 / (1.0 + temp1)) * temp2 / (1.0 + temp2);
910}
911
912
913// :28: Area variablity, page 14
914#if 1
915static
916double avar(double zzt, double zzl, double zzc, prop_type &prop, propv_type &propv)
917{
918 static int kdv;
919 static double dexa, de, vmd, vs0, sgl, sgtm, sgtp, sgtd, tgtd, gm, gp, cv1, cv2, yv1, yv2, yv3, csm1, csm2, ysm1, ysm2, ysm3, csp1, csp2, ysp1, ysp2, ysp3, csd1, zd, cfm1, cfm2, cfm3, cfp1, cfp2, cfp3;
920
921 // :29: Climatic constants, page 15
922 // Indexes are:
923 // 0: equatorial
924 // 1: continental suptropical
925 // 2: maritime subtropical
926 // 3: desert
927 // 4: continental temperature
928 // 5: maritime over land
929 // 6: maritime over sea
930 // equator contsup maritsup desert conttemp mariland marisea
931 const double bv1[7] = { -9.67, -0.62, 1.26, -9.21, -0.62, -0.39, 3.15};
932 const double bv2[7] = { 12.7, 9.19, 15.5, 9.05, 9.19, 2.86, 857.9};
933 const double xv1[7] = {144.9e3, 228.9e3, 262.6e3, 84.1e3, 228.9e3, 141.7e3, 2222.e3};
934 const double xv2[7] = {190.3e3, 205.2e3, 185.2e3, 101.1e3, 205.2e3, 315.9e3, 164.8e3};
935 const double xv3[7] = {133.8e3, 143.6e3, 99.8e3, 98.6e3, 143.6e3, 167.4e3, 116.3e3};
936 const double bsm1[7] = { 2.13, 2.66, 6.11, 1.98, 2.68, 6.86, 8.51};
937 const double bsm2[7] = { 159.5, 7.67, 6.65, 13.11, 7.16, 10.38, 169.8};
938 const double xsm1[7] = {762.2e3, 100.4e3, 138.2e3, 139.1e3, 93.7e3, 187.8e3, 609.8e3};
939 const double xsm2[7] = {123.6e3, 172.5e3, 242.2e3, 132.7e3, 186.8e3, 169.6e3, 119.9e3};
940 const double xsm3[7] = { 94.5e3, 136.4e3, 178.6e3, 193.5e3, 133.5e3, 108.9e3, 106.6e3};
941 const double bsp1[7] = { 2.11, 6.87, 10.08, 3.68, 4.75, 8.58, 8.43};
942 const double bsp2[7] = { 102.3, 15.53, 9.60, 159.3, 8.12, 13.97, 8.19};
943 const double xsp1[7] = {636.9e3, 138.7e3, 165.3e3, 464.4e3, 93.2e3, 216.0e3, 136.2e3};
944 const double xsp2[7] = {134.8e3, 143.7e3, 225.7e3, 93.1e3, 135.9e3, 152.0e3, 188.5e3};
945 const double xsp3[7] = { 95.6e3, 98.6e3, 129.7e3, 94.2e3, 113.4e3, 122.7e3, 122.9e3};
946 // bds1 -> is similar to C_D from table 5.1 at [Alg 5.8]
947 // bzd1 -> is similar to z_D from table 5.1 at [Alg 5.8]
948 const double bsd1[7] = { 1.224, 0.801, 1.380, 1.000, 1.224, 1.518, 1.518};
949 const double bzd1[7] = { 1.282, 2.161, 1.282, 20.0, 1.282, 1.282, 1.282};
950 const double bfm1[7] = { 1.0, 1.0, 1.0, 1.0, 0.92, 1.0, 1.0};
951 const double bfm2[7] = { 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0};
952 const double bfm3[7] = { 0.0, 0.0, 0.0, 0.0, 1.77, 0.0, 0.0};
953 const double bfp1[7] = { 1.0, 0.93, 1.0, 0.93, 0.93, 1.0, 1.0};
954 const double bfp2[7] = { 0.0, 0.31, 0.0, 0.19, 0.31, 0.0, 0.0};
955 const double bfp3[7] = { 0.0, 2.00, 0.0, 1.79, 2.00, 0.0, 0.0};
956 const double rt = 7.8, rl = 24.0;
957 static bool no_location_variability, no_situation_variability;
958 double avarv, q, vs, zt, zl, zc;
959 double sgt, yr;
960 int temp_klim;
961
962 if (propv.lvar > 0) {
963 // :31: Setup variablity constants, page 16
964 switch (propv.lvar) {
965 default:
966 // Initialization or climate change
967
968 // if climate is wrong, use some "continental temperature" as default
969 // and set error indicator
970 if (propv.klim <= 0 || propv.klim > 7) {
971 propv.klim = 5;
972 prop.kwx = mymax(prop.kwx, 2);
973 set_warn("Climate index set to continental", 2);
974 }
975
976 // convert climate number into index into the climate tables
977 temp_klim = propv.klim - 1;
978
979 // :32: Climatic coefficients, page 17
980 cv1 = bv1[temp_klim];
981 cv2 = bv2[temp_klim];
982 yv1 = xv1[temp_klim];
983 yv2 = xv2[temp_klim];
984 yv3 = xv3[temp_klim];
985 csm1 = bsm1[temp_klim];
986 csm2 = bsm2[temp_klim];
987 ysm1 = xsm1[temp_klim];
988 ysm2 = xsm2[temp_klim];
989 ysm3 = xsm3[temp_klim];
990 csp1 = bsp1[temp_klim];
991 csp2 = bsp2[temp_klim];
992 ysp1 = xsp1[temp_klim];
993 ysp2 = xsp2[temp_klim];
994 ysp3 = xsp3[temp_klim];
995 csd1 = bsd1[temp_klim];
996 zd = bzd1[temp_klim];
997 cfm1 = bfm1[temp_klim];
998 cfm2 = bfm2[temp_klim];
999 cfm3 = bfm3[temp_klim];
1000 cfp1 = bfp1[temp_klim];
1001 cfp2 = bfp2[temp_klim];
1002 cfp3 = bfp3[temp_klim];
1003 [[fallthrough]];
1004
1005 case 4:
1006 // :33: Mode of variablity coefficients, page 17
1007
1008 // This code means that propv.mdvar can be
1009 // 0 .. 3
1010 // 10 .. 13, then no_location_variability is set (no location variability)
1011 // 20 .. 23, then no_situation_variability is set (no situatian variability)
1012 // 30 .. 33, then no_location_variability and no_situation_variability is set
1013 kdv = propv.mdvar;
1014 no_situation_variability = kdv >= 20;
1015 if (no_situation_variability)
1016 kdv -= 20;
1017
1018 no_location_variability = kdv >= 10;
1019 if (no_location_variability)
1020 kdv -= 10;
1021
1022 if (kdv < 0 || kdv > 3) {
1023 kdv = 0;
1024 set_warn("kdv set to 0", 2);
1025 prop.kwx = mymax(prop.kwx, 2);
1026 }
1027 [[fallthrough]];
1028
1029 case 3:
1030 // :34: Frequency coefficients, page 18
1031 q = log(0.133 * prop.k);
1032 gm = cfm1 + cfm2 / ((cfm3 * q * cfm3 * q) + 1.0);
1033 gp = cfp1 + cfp2 / ((cfp3 * q * cfp3 * q) + 1.0);
1034 [[fallthrough]];
1035
1036 case 2:
1037 // :35: System coefficients, page 18
1038 // [Alg 5.3], effective distance
1039 {
1040 const double a_1 = 9000e3; // 9000 km from [[Alg 5.3]
1041 //XXX I don't have any idea how they made up the third summand,
1042 //XXX text says a_1 * pow(k * D_1, -THIRD)
1043 //XXX with const double D_1 = 1266; // 1266 km
1044 dexa = sqrt(2*a_1 * prop.h_e[0]) +
1045 sqrt(2*a_1 * prop.h_e[1]) +
1046 pow((575.7e12 / prop.k), THIRD);
1047 }
1048 [[fallthrough]];
1049
1050 case 1:
1051 // :36: Distance coefficients, page 18
1052 {
1053 // [Alg 5.4]
1054 const double D_0 = 130e3; // 130 km from [Alg 5.4]
1055 if (prop.d < dexa)
1056 de = D_0 * prop.d / dexa;
1057 else
1058 de = D_0 + prop.d - dexa;
1059 }
1060 }
1061 /*
1062 * Quantiles of time variability are computed using a
1063 * variation of the methods described in Section 10 and
1064 * Annex III.7 of NBS~TN101, and also in CCIR
1065 * Report {238-3}. Those methods speak of eight or nine
1066 * discrete radio climates, of which seven have been
1067 * documented with corresponding empirical curves. It is
1068 * these empirical curves to which we refer below. They are
1069 * all curves of quantiles of deviations versus the
1070 * effective distance @de.
1071 */
1072 vmd = curve(cv1, cv2, yv1, yv2, yv3, de); // [Alg 5.5]
1073 // [Alg 5.7], the slopes or "pseudo-standard deviations":
1074 // sgtm -> \sigma T-
1075 // sgtp -> \sigma T+
1076 sgtm = curve(csm1, csm2, ysm1, ysm2, ysm3, de) * gm;
1077 sgtp = curve(csp1, csp2, ysp1, ysp2, ysp3, de) * gp;
1078 // [Alg 5.8], ducting, "sgtd" -> \sigma TD
1079 sgtd = sgtp * csd1;
1080 tgtd = (sgtp - sgtd) * zd;
1081
1082 // Location variability
1083 if (no_location_variability) {
1084 sgl = 0.0;
1085 } else {
1086 // Alg [3.9]
1087 q = (1.0 - 0.8 * exp(-prop.d / 50e3)) * prop.delta_h * prop.k;
1088 // [Alg 5.9]
1089 sgl = 10.0 * q / (q + 13.0);
1090 }
1091
1092 // Situation variability
1093 if (no_situation_variability) {
1094 vs0 = 0.0;
1095 } else {
1096 const double D = 100e3; // 100 km
1097 vs0 = (5.0 + 3.0 * exp(-de / D)); // [Alg 5.10]
1098 vs0 *= vs0;
1099 }
1100
1101 // Mark all constants as initialized
1102 propv.lvar = 0;
1103 }
1104
1105
1106 // :37: Correct normal deviates, page 19
1107 zt = zzt;
1108 zl = zzl;
1109 zc = zzc;
1110 // kdv is derived from prop.mdvar
1111 switch (kdv) {
1112 case 0:
1113 // Single message mode. Time, location and situation variability
1114 // are combined to form a confidence level.
1115 zt = zc;
1116 zl = zc;
1117 break;
1118
1119 case 1:
1120 // Individual mode. Reliability is given by time
1121 // variability. Confidence. is a combination of location
1122 // and situation variability.
1123 zl = zc;
1124 break;
1125
1126 case 2:
1127 // Mobile modes. Reliability is a combination of time and
1128 // location variability. Confidence. is given by the
1129 // situation variability.
1130 zl = zt;
1131 // case 3: Broadcast mode. like avar(zt, zl, zc).
1132 // Reliability is given by the two-fold statement of at
1133 // least qT of the time in qL of the locations. Confidence.
1134 // is given by the situation variability.
1135 }
1136 if (fabs(zt) > 3.1 || fabs(zl) > 3.1 || fabs(zc) > 3.1) {
1137 set_warn("Situations variables not optimal", 1);
1138 prop.kwx = mymax(prop.kwx, 1);
1139 }
1140
1141
1142 // :38: Resolve standard deviations, page 19
1143 if (zt < 0.0)
1144 sgt = sgtm;
1145 else
1146 if (zt <= zd)
1147 sgt = sgtp;
1148 else
1149 sgt = sgtd + tgtd / zt;
1150 // [Alg 5.11], situation variability
1151 vs = vs0 + (sgt * zt * sgt * zt) / (rt + zc * zc) + (sgl * zl * sgl * zl) / (rl + zc * zc);
1152
1153
1154 // :39: Resolve deviations, page 19
1155 if (kdv == 0) {
1156 yr = 0.0;
1157 propv.sgc = sqrt(sgt * sgt + sgl * sgl + vs);
1158 } else
1159 if (kdv == 1) {
1160 yr = sgt * zt;
1161 propv.sgc = sqrt(sgl * sgl + vs);
1162 } else
1163 if (kdv == 2) {
1164 yr = sqrt(sgt * sgt + sgl * sgl) * zt;
1165 propv.sgc = sqrt(vs);
1166 } else {
1167 yr = sgt * zt + sgl * zl;
1168 propv.sgc = sqrt(vs);
1169 }
1170
1171 // [Alg 5.1], area variability
1172 avarv = prop.A_ref - vmd - yr - propv.sgc * zc;
1173
1174 // [Alg 5.2]
1175 if (avarv < 0.0)
1176 avarv = avarv * (29.0 - avarv) / (29.0 - 10.0 * avarv);
1177
1178 return avarv;
1179}
1180#endif
1181
1182// :45: Find to horizons, page 24
1183/*
1184 * Here we use the terrain profile @pfl to find the two horizons. Output
1185 * consists of the horizon distances @dl and the horizon take-off angles
1186 * @the. If the path is line-of-sight, the routine sets both horizon
1187 * distances equal to @dist.
1188 */
1189static
1190void hzns(double pfl[], prop_type &prop)
1191{
1192 bool wq;
1193 int np;
1194 double xi, za, zb, qc, q, sb, sa;
1195
1196 np = (int)pfl[0];
1197 xi = pfl[1];
1198 za = pfl[2] + prop.h_g[0];
1199 zb = pfl[np+2] + prop.h_g[1];
1200 qc = 0.5 * prop.gamma_e;
1201 q = qc * prop.d;
1202 prop.theta_ej[1] = (zb - za) / prop.d;
1203 prop.theta_ej[0] = prop.theta_ej[1] - q;
1204 prop.theta_ej[1] = -prop.theta_ej[1] - q;
1205 prop.d_Lj[0] = prop.d;
1206 prop.d_Lj[1] = prop.d;
1207
1208 if (np >= 2) {
1209 sa = 0.0;
1210 sb = prop.d;
1211 wq = true;
1212
1213 for (int i = 1; i < np; i++) {
1214 sa += xi;
1215 sb -= xi;
1216 q = pfl[i+2] - (qc * sa + prop.theta_ej[0]) * sa - za;
1217
1218 if (q > 0.0) {
1219 prop.theta_ej[0] += q / sa;
1220 prop.d_Lj[0] = sa;
1221 wq = false;
1222 }
1223
1224 if (!wq) {
1225 q = pfl[i+2] - (qc * sb + prop.theta_ej[1]) * sb - zb;
1226
1227 if (q > 0.0) {
1228 prop.theta_ej[1] += q / sb;
1229 prop.d_Lj[1] = sb;
1230 }
1231 }
1232 }
1233 }
1234}
1235
1236
1237// :53: Linear least square fit, page 28
1238static
1239void zlsq1(double z[], const double &x1, const double &x2, double& z0, double& zn)
1240
1241{
1242 double xn, xa, xb, x, a, b;
1243 int n, ja, jb;
1244
1245 xn = z[0];
1246 xa = int(FORTRAN_DIM(x1 / z[1], 0.0));
1247 xb = xn - int(FORTRAN_DIM(xn, x2 / z[1]));
1248
1249 if (xb <= xa) {
1250 xa = FORTRAN_DIM(xa, 1.0);
1251 xb = xn - FORTRAN_DIM(xn, xb + 1.0);
1252 }
1253
1254 ja = (int)xa;
1255 jb = (int)xb;
1256 n = jb - ja;
1257 xa = xb - xa;
1258 x = -0.5 * xa;
1259 xb += x;
1260 a = 0.5 * (z[ja+2] + z[jb+2]);
1261 b = 0.5 * (z[ja+2] - z[jb+2]) * x;
1262
1263 for (int i = 2; i <= n; ++i) {
1264 ++ja;
1265 x += 1.0;
1266 a += z[ja+2];
1267 b += z[ja+2] * x;
1268 }
1269
1270 a /= xa;
1271 b = b * 12.0 / ((xa * xa + 2.0) * xa);
1272 z0 = a - b * xb;
1273 zn = a + b * (xn - xb);
1274}
1275
1276
1277// :52: Provide a quantile and reorders array @a, page 27
1278static
1279double qtile(const int &nn, double a[], const int &ir)
1280{
1281 double q = 0.0, r; /* Initializations by KD2BD */
1282 int m, n, i, j, j1 = 0, i0 = 0, k; /* Initializations by KD2BD */
1283 bool done = false;
1284 bool goto10 = true;
1285
1286 m = 0;
1287 n = nn;
1288 k = mymin(mymax(0, ir), n);
1289
1290 while (!done) {
1291 if (goto10) {
1292 q = a[k];
1293 i0 = m;
1294 j1 = n;
1295 }
1296
1297 i = i0;
1298
1299 while (i <= n && a[i] >= q)
1300 i++;
1301
1302 if (i > n)
1303 i = n;
1304 j = j1;
1305
1306 while (j >= m && a[j] <= q)
1307 j--;
1308
1309 if (j < m)
1310 j = m;
1311
1312 if (i < j) {
1313 r = a[i];
1314 a[i] = a[j];
1315 a[j] = r;
1316 i0 = i + 1;
1317 j1 = j - 1;
1318 goto10 = false;
1319 } else
1320 if (i < k) {
1321 a[k] = a[i];
1322 a[i] = q;
1323 m = i + 1;
1324 goto10 = true;
1325 } else
1326 if (j > k) {
1327 a[k] = a[j];
1328 a[j] = q;
1329 n = j - 1;
1330 goto10 = true;
1331 } else {
1332 done = true;
1333 }
1334 }
1335
1336 return q;
1337}
1338
1339
1340#ifdef WITH_QERF
1341// :50: Standard normal complementary probability, page 26
1342static
1343double qerf(const double &z)
1344{
1345 const double b1 = 0.319381530, b2 = -0.356563782, b3 = 1.781477937;
1346 const double b4 = -1.821255987, b5 = 1.330274429;
1347 const double rp = 4.317008, rrt2pi = 0.398942280;
1348 double t, x, qerfv;
1349
1350 x = z;
1351 t = fabs(x);
1352
1353 if (t >= 10.0)
1354 qerfv = 0.0;
1355 else {
1356 t = rp / (t + rp);
1357 qerfv = exp(-0.5 * x * x) * rrt2pi * ((((b5 * t + b4) * t + b3) * t + b2) * t + b1) * t;
1358 }
1359
1360 if (x < 0.0)
1361 qerfv = 1.0 - qerfv;
1362
1363 return qerfv;
1364}
1365#endif
1366
1367
1368// :48: Find interdecile range of elevations, page 25
1369/*
1370 * Using the terrain profile @pfl we find \Delta h, the interdecile range of
1371 * elevations between the two points @x1 and @x2.
1372 */
1373static
1374double dlthx(double pfl[], const double &x1, const double &x2)
1375{
1376 int np, ka, kb, n, k, j;
1377 double dlthxv, sn, xa, xb;
1378 double *s;
1379
1380 np = (int)pfl[0];
1381 xa = x1 / pfl[1];
1382 xb = x2 / pfl[1];
1383 dlthxv = 0.0;
1384
1385 if (xb - xa < 2.0) // exit out
1386 return dlthxv;
1387
1388 ka = (int)(0.1 * (xb - xa + 8.0));
1389 ka = mymin(mymax(4, ka), 25);
1390 n = 10 * ka - 5;
1391 kb = n - ka + 1;
1392 sn = n - 1;
1393 s = new double[n+2];
1394 s[0] = sn;
1395 s[1] = 1.0;
1396 xb = (xb - xa) / sn;
1397 k = (int)(xa + 1.0);
1398 xa -= (double)k;
1399
1400 for (j = 0; j < n; j++) {
1401 // Reduce
1402 while (xa > 0.0 && k < np) {
1403 xa -= 1.0;
1404 ++k;
1405 }
1406
1407 s[j+2] = pfl[k+2] + (pfl[k+2] - pfl[k+1]) * xa;
1408 xa = xa + xb;
1409 }
1410
1411 zlsq1(s, 0.0, sn, xa, xb);
1412 xb = (xb - xa) / sn;
1413
1414 for (j = 0; j < n; j++) {
1415 s[j+2] -= xa;
1416 xa = xa + xb;
1417 }
1418
1419 dlthxv = qtile(n - 1, s + 2, ka - 1) - qtile(n - 1, s + 2, kb - 1);
1420 dlthxv /= 1.0 - 0.8 * exp(-(x2 - x1) / 50.0e3);
1421 delete[] s;
1422
1423 return dlthxv;
1424}
1425
1426
1427// :41: Prepare model for point-to-point operation, page 22
1428/*
1429 * This mode requires the terrain profile lying between the terminals. This
1430 * should be a sequence of surface elevations at points along the great
1431 * circle path joining the two points. It should start at the ground beneath
1432 * the first terminal and end at the ground beneath the second. In the
1433 * present routine it is assumed that the elevations are equispaced along
1434 * the path. They are stored in the array @pfl along with two defining
1435 * parameters.
1436 *
1437 * We will have
1438 * pfl[0] = np, the number of points in the path
1439 * pfl[1] = xi, the length of each increment
1440*/
1441#if 1
1442static
1443void qlrpfl(double pfl[], int klimx, int mdvarx, prop_type &prop, propv_type &propv)
1444{
1445 int np, j;
1446 double xl[2], q, za, zb;
1447
1448 prop.d = pfl[0] * pfl[1];
1449 np = (int)pfl[0];
1450
1451 // :44: determine horizons and dh from pfl, page 23
1452 hzns(pfl, prop);
1453 for (j = 0; j < 2; j++)
1454 xl[j] = mymin(15.0 * prop.h_g[j], 0.1 * prop.d_Lj[j]);
1455
1456 xl[1] = prop.d - xl[1];
1457 prop.delta_h = dlthx(pfl, xl[0], xl[1]);
1458
1459 if (prop.d_Lj[0] + prop.d_Lj[1] > 1.5 * prop.d) {
1460 // :45: Redo line-of-sight horizons, page 23
1461
1462 /*
1463 * If the path is line-of-sight, we still need to know where
1464 * the horizons might have been, and so we turn to
1465 * techniques used in area prediction mode.
1466 */
1467 zlsq1(pfl, xl[0], xl[1], za, zb);
1468 prop.h_e[0] = prop.h_g[0] + FORTRAN_DIM(pfl[2], za);
1469 prop.h_e[1] = prop.h_g[1] + FORTRAN_DIM(pfl[np+2], zb);
1470
1471 for (j = 0; j < 2; j++)
1472 prop.d_Lj[j] = sqrt(2.0 * prop.h_e[j] / prop.gamma_e) * exp(-0.07 * sqrt(prop.delta_h / mymax(prop.h_e[j], 5.0)));
1473
1474 q = prop.d_Lj[0] + prop.d_Lj[1];
1475
1476 if (q <= prop.d) {
1477 q = ((prop.d / q) * (prop.d / q));
1478
1479 for (j = 0; j < 2; j++) {
1480 prop.h_e[j] *= q;
1481 prop.d_Lj[j] = sqrt(2.0 * prop.h_e[j] / prop.gamma_e) * exp(-0.07 * sqrt(prop.delta_h / mymax(prop.h_e[j], 5.0)));
1482 }
1483 }
1484
1485 for (j = 0; j < 2; j++) {
1486 q = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
1487 prop.theta_ej[j] = (0.65 * prop.delta_h * (q / prop.d_Lj[j] - 1.0) - 2.0 * prop.h_e[j]) / q;
1488 }
1489 } else {
1490 // :46: Transhorizon effective heights, page 23
1491 zlsq1(pfl, xl[0], 0.9 * prop.d_Lj[0], za, q);
1492 zlsq1(pfl, prop.d - 0.9 * prop.d_Lj[1], xl[1], q, zb);
1493 prop.h_e[0] = prop.h_g[0] + FORTRAN_DIM(pfl[2], za);
1494 prop.h_e[1] = prop.h_g[1] + FORTRAN_DIM(pfl[np+2], zb);
1495 }
1496
1497 prop.mdp = -1;
1498 propv.lvar = mymax(propv.lvar, 3);
1499
1500 if (mdvarx >= 0) {
1501 propv.mdvar = mdvarx;
1502 propv.lvar = mymax(propv.lvar, 4);
1503 }
1504
1505 if (klimx > 0) {
1506 propv.klim = klimx;
1507 propv.lvar = 5;
1508 }
1509
1510 lrprop(0.0, prop);
1511}
1512#endif
1513
1514
1515//********************************************************
1516//* Point-To-Point Mode Calculations *
1517//********************************************************
1518
1519
1520#ifdef WITH_POINT_TO_POINT
1521#include <string.h>
1522static
1523void point_to_point(double elev[],
1524 double tht_m, // Transceiver above ground level
1525 double rht_m, // Receiver above groud level
1526 double eps_dielect, // Earth dielectric constant (rel. permittivity)
1527 double sgm_conductivity, // Earth conductivity (Siemens/m)
1528 double eno, // Atmospheric bending const, n-Units
1529 double frq_mhz,
1530 int radio_climate, // 1..7
1531 int pol, // 0 horizontal, 1 vertical
1532 double conf, // 0.01 .. .99, Fractions of situations
1533 double rel, // 0.01 .. .99, Fractions of time
1534 double &dbloss,
1535 char *strmode,
1536 int &p_mode, // propagation mode selector
1537 double (&horizons)[2], // horizon distances
1538 int &errnum)
1539{
1540 // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
1541 // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
1542 // 7-Maritime Temperate, Over Sea
1543 // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
1544 // errnum: 0- No Error.
1545 // 1- Warning: Some parameters are nearly out of range.
1546 // Results should be used with caution.
1547 // 2- Note: Default parameters have been substituted for impossible ones.
1548 // 3- Warning: A combination of parameters is out of range.
1549 // Results are probably invalid.
1550 // Other- Warning: Some parameters are out of range.
1551 // Results are probably invalid.
1552
1553 prop_type prop;
1554 propv_type propv;
1555
1556 double zsys = 0;
1557 double zc, zr;
1558 double q = eno;
1559 long ja, jb, i, np;
1560 double fs;
1561
1562 prop.h_g[0] = tht_m; // Tx height above ground level
1563 prop.h_g[1] = rht_m; // Rx height above ground level
1564 propv.klim = radio_climate;
1565 prop.kwx = 0; // no error yet
1566 propv.lvar = 5; // initialize all constants
1567 prop.mdp = -1; // point-to-point
1568 zc = qerfi(conf);
1569 zr = qerfi(rel);
1570 np = (long)elev[0];
1571#if 0
1572 double dkm = (elev[1] * elev[0]) / 1000.0;
1573 double xkm = elev[1] / 1000.0;
1574#endif
1575
1576 ja = (long)(3.0 + 0.1 * elev[0]);
1577 jb = np - ja + 6;
1578 for (i = ja - 1; i < jb; ++i)
1579 zsys += elev[i];
1580 zsys /= (jb - ja + 1);
1581
1582 propv.mdvar = 12;
1583 qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
1584 qlrpfl(elev, propv.klim, propv.mdvar, prop, propv);
1585 fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
1586 q = prop.d - prop.d_L;
1587
1588 horizons[0] = 0.0;
1589 horizons[1] = 0.0;
1590 if (int(q) < 0.0) {
1591 strcpy(strmode, "Line-Of-Sight Mode");
1592 p_mode = 0;
1593 } else {
1594 if (int(q) == 0.0) {
1595 strcpy(strmode, "Single Horizon");
1596 horizons[0] = prop.d_Lj[0];
1597 p_mode = 1;
1598 }
1599
1600 else {
1601 if (int(q) > 0.0) {
1602 strcpy(strmode, "Double Horizon");
1603 horizons[0] = prop.d_Lj[0];
1604 horizons[1] = prop.d_Lj[1];
1605 p_mode = 1;
1606 }
1607 }
1608
1609 if (prop.d <= prop.d_Ls || prop.d <= prop.dx) {
1610 strcat(strmode, ", Diffraction Dominant");
1611 p_mode = 1;
1612 }
1613
1614 else {
1615 if (prop.d > prop.dx) {
1616 strcat(strmode, ", Troposcatter Dominant");
1617 p_mode = 2;
1618 }
1619 }
1620 }
1621
1622 dbloss = avar(zr, 0.0, zc, prop, propv) + fs;
1623 errnum = prop.kwx;
1624}
1625#endif
1626
1627
1628#ifdef WITH_POINT_TO_POINT_MDH
1629static
1630void point_to_pointMDH(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno, double frq_mhz, int radio_climate, int pol, double timepct, double locpct, double confpct, double &dbloss, int &propmode, double &deltaH, int &errnum)
1631{
1632 // pol: 0-Horizontal, 1-Vertical
1633 // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
1634 // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
1635 // 7-Maritime Temperate, Over Sea
1636 // timepct, locpct, confpct: .01 to .99
1637 // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
1638 // propmode: Value Mode
1639 // -1 mode is undefined
1640 // 0 Line of Sight
1641 // 5 Single Horizon, Diffraction
1642 // 6 Single Horizon, Troposcatter
1643 // 9 Double Horizon, Diffraction
1644 // 10 Double Horizon, Troposcatter
1645 // errnum: 0- No Error.
1646 // 1- Warning: Some parameters are nearly out of range.
1647 // Results should be used with caution.
1648 // 2- Note: Default parameters have been substituted for impossible ones.
1649 // 3- Warning: A combination of parameters is out of range.
1650 // Results are probably invalid.
1651 // Other- Warning: Some parameters are out of range.
1652 // Results are probably invalid.
1653
1654 prop_type prop;
1655 propv_type propv;
1656 propa_type propa;
1657 double zsys = 0;
1658 double ztime, zloc, zconf;
1659 double q = eno;
1660 long ja, jb, i, np;
1661 double dkm, xkm;
1662 double fs;
1663
1664 propmode = -1; // mode is undefined
1665 prop.h_g[0] = tht_m;
1666 prop.h_g[1] = rht_m;
1667 propv.klim = radio_climate;
1668 prop.kwx = 0;
1669 propv.lvar = 5;
1670 prop.mdp = -1;
1671 ztime = qerfi(timepct);
1672 zloc = qerfi(locpct);
1673 zconf = qerfi(confpct);
1674
1675 np = (long)elev[0];
1676 dkm = (elev[1] * elev[0]) / 1000.0;
1677 xkm = elev[1] / 1000.0;
1678
1679 ja = (long)(3.0 + 0.1 * elev[0]);
1680 jb = np - ja + 6;
1681 for (i = ja - 1; i < jb; ++i)
1682 zsys += elev[i];
1683 zsys /= (jb - ja + 1);
1684
1685 propv.mdvar = 12;
1686 qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
1687 qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv);
1688 fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
1689 deltaH = prop.delta_h;
1690 q = prop.d - prop.d_L;
1691
1692 if (int(q) < 0.0) {
1693 propmode = 0; // Line-Of-Sight Mode
1694 } else {
1695 if (int(q) == 0.0)
1696 propmode = 4; // Single Horizon
1697 else
1698 if (int(q) > 0.0)
1699 propmode = 8; // Double Horizon
1700
1701 if (prop.d <= prop.d_Ls || prop.d <= prop.dx)
1702 propmode += 1; // Diffraction Dominant
1703
1704 else
1705 if (prop.d > prop.dx)
1706 propmode += 2; // Troposcatter Dominant
1707 }
1708
1709 dbloss = avar(ztime, zloc, zconf, prop, propv) + fs; //avar(time,location,confidence)
1710 errnum = prop.kwx;
1711}
1712#endif
1713
1714
1715#ifdef WITH_POINT_TO_POINT_DH
1716static
1717void point_to_pointDH(double elev[], double tht_m, double rht_m, double eps_dielect, double sgm_conductivity, double eno, double frq_mhz, int radio_climate, int pol, double conf, double rel, double &dbloss, double &deltaH, int &errnum)
1718{
1719 // pol: 0-Horizontal, 1-Vertical
1720 // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
1721 // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
1722 // 7-Maritime Temperate, Over Sea
1723 // conf, rel: .01 to .99
1724 // elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
1725 // errnum: 0- No Error.
1726 // 1- Warning: Some parameters are nearly out of range.
1727 // Results should be used with caution.
1728 // 2- Note: Default parameters have been substituted for impossible ones.
1729 // 3- Warning: A combination of parameters is out of range.
1730 // Results are probably invalid.
1731 // Other- Warning: Some parameters are out of range.
1732 // Results are probably invalid.
1733
1734 char strmode[100];
1735 prop_type prop;
1736 propv_type propv;
1737 propa_type propa;
1738 double zsys = 0;
1739 double zc, zr;
1740 double q = eno;
1741 long ja, jb, i, np;
1742 double dkm, xkm;
1743 double fs;
1744
1745 prop.h_g[0] = tht_m;
1746 prop.h_g[1] = rht_m;
1747 propv.klim = radio_climate;
1748 prop.kwx = 0;
1749 propv.lvar = 5;
1750 prop.mdp = -1;
1751 zc = qerfi(conf);
1752 zr = qerfi(rel);
1753 np = (long)elev[0];
1754 dkm = (elev[1] * elev[0]) / 1000.0;
1755 xkm = elev[1] / 1000.0;
1756
1757 ja = (long)(3.0 + 0.1 * elev[0]);
1758 jb = np - ja + 6;
1759 for (i = ja - 1; i < jb; ++i)
1760 zsys += elev[i];
1761 zsys /= (jb - ja + 1);
1762
1763 propv.mdvar = 12;
1764 qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
1765 qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv);
1766 fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
1767 deltaH = prop.delta_h;
1768 q = prop.d - prop.d_L;
1769
1770 if (int(q) < 0.0)
1771 strcpy(strmode, "Line-Of-Sight Mode");
1772 else {
1773 if (int(q) == 0.0)
1774 strcpy(strmode, "Single Horizon");
1775
1776 else
1777 if (int(q) > 0.0)
1778 strcpy(strmode, "Double Horizon");
1779
1780 if (prop.d <= prop.d_Ls || prop.d <= prop.dx)
1781 strcat(strmode, ", Diffraction Dominant");
1782
1783 else
1784 if (prop.d > prop.dx)
1785 strcat(strmode, ", Troposcatter Dominant");
1786 }
1787
1788 dbloss = avar(zr, 0.0, zc, prop, propv) + fs; //avar(time,location,confidence)
1789 errnum = prop.kwx;
1790}
1791#endif
1792
1793
1794
1795//********************************************************
1796//* Area Mode Calculations *
1797//********************************************************
1798
1799
1800#ifdef WITH_AREA
1801static
1802void area(long ModVar, double deltaH, double tht_m, double rht_m, double dist_km, int TSiteCriteria, int RSiteCriteria, double eps_dielect, double sgm_conductivity, double eno, double frq_mhz, int radio_climate, int pol, double pctTime, double pctLoc, double pctConf, double &dbloss, int &errnum)
1803{
1804 // pol: 0-Horizontal, 1-Vertical
1805 // TSiteCriteria, RSiteCriteria:
1806 // 0 - random, 1 - careful, 2 - very careful
1807 // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
1808 // 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
1809 // 7-Maritime Temperate, Over Sea
1810 // ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used
1811 // 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used
1812 // 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used
1813 // 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence"
1814 // pctTime, pctLoc, pctConf: .01 to .99
1815 // errnum: 0- No Error.
1816 // 1- Warning: Some parameters are nearly out of range.
1817 // Results should be used with caution.
1818 // 2- Note: Default parameters have been substituted for impossible ones.
1819 // 3- Warning: A combination of parameters is out of range.
1820 // Results are probably invalid.
1821 // Other- Warning: Some parameters are out of range.
1822 // Results are probably invalid.
1823 // NOTE: strmode is not used at this time.
1824
1825 prop_type prop;
1826 propv_type propv;
1827 propa_type propa;
1828 double zt, zl, zc, xlb;
1829 double fs;
1830 long ivar;
1831 double eps, sgm;
1832 long ipol;
1833 int kst[2];
1834
1835 kst[0] = (int)TSiteCriteria;
1836 kst[1] = (int)RSiteCriteria;
1837 zt = qerfi(pctTime);
1838 zl = qerfi(pctLoc);
1839 zc = qerfi(pctConf);
1840 eps = eps_dielect;
1841 sgm = sgm_conductivity;
1842 prop.delta_h = deltaH;
1843 prop.h_g[0] = tht_m;
1844 prop.h_g[1] = rht_m;
1845 propv.klim = (long)radio_climate;
1846 prop.N_s = eno;
1847 prop.kwx = 0;
1848 ivar = (long)ModVar;
1849 ipol = (long)pol;
1850 qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop);
1851 qlra(kst, propv.klim, ivar, prop, propv);
1852
1853 if (propv.lvar < 1)
1854 propv.lvar = 1;
1855
1856 lrprop(dist_km * 1000.0, prop, propa);
1857 fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
1858 xlb = fs + avar(zt, zl, zc, prop, propv);
1859 dbloss = xlb;
1860
1861 if (prop.kwx == 0)
1862 errnum = 0;
1863 else
1864 errnum = prop.kwx;
1865}
1866#endif
1867
1868}
#define M_PI
Definition FGJSBBase.h:50
#define i(x)
#define set_warn(txt, err)
Definition itm.cpp:144
Definition itm.cpp:51
static void lrprop(double d, prop_type &prop)
Definition itm.cpp:508
static int mymax(const int &i, const int &j)
Definition itm.cpp:102
const double THIRD
Definition itm.cpp:53
static double qerfi(double q)
Definition itm.cpp:824
static double Fn(const double &v_square)
Definition itm.cpp:161
static double A_los(double d, prop_type &prop)
Definition itm.cpp:457
static double curve(double const &c1, double const &c2, double const &x1, double const &x2, double const &x3, double const &de)
Definition itm.cpp:899
static double dlthx(double pfl[], const double &x1, const double &x2)
Definition itm.cpp:1374
static void hzns(double pfl[], prop_type &prop)
Definition itm.cpp:1190
static double F_0(double td)
Definition itm.cpp:254
static double avar(double zzt, double zzl, double zzc, prop_type &prop, propv_type &propv)
Definition itm.cpp:916
static double A_scat(double s, prop_type &prop)
Definition itm.cpp:369
static double F(const double &x, const double &K)
Definition itm.cpp:181
static double qtile(const int &nn, double a[], const int &ir)
Definition itm.cpp:1279
static double FORTRAN_DIM(const double &x, const double &y)
Definition itm.cpp:132
static double abq_alos(complex< double > r)
Definition itm.cpp:443
static void zlsq1(double z[], const double &x1, const double &x2, double &z0, double &zn)
Definition itm.cpp:1239
static void qlrpfl(double pfl[], int klimx, int mdvarx, prop_type &prop, propv_type &propv)
Definition itm.cpp:1443
static int mymin(const int &i, const int &j)
Definition itm.cpp:92
static void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, double sgm, prop_type &prop)
Definition itm.cpp:863
static double adiff(double s, prop_type &prop)
Definition itm.cpp:269
const double f_0
Definition itm.cpp:54
static double H_0(double r, double et)
Definition itm.cpp:217
double Z_g_real
Definition itm.cpp:65
double ael
Definition itm.cpp:77
double emd
Definition itm.cpp:81
double d_Ls
Definition itm.cpp:85
double d_Lj[2]
Definition itm.cpp:69
double dx
Definition itm.cpp:76
double d
Definition itm.cpp:59
double delta_h
Definition itm.cpp:62
double ems
Definition itm.cpp:83
double aed
Definition itm.cpp:80
double k
Definition itm.cpp:61
double Z_g_imag
Definition itm.cpp:66
double N_s
Definition itm.cpp:63
double h_g[2]
Definition itm.cpp:60
double h_e[2]
Definition itm.cpp:68
double ak1
Definition itm.cpp:78
double gamma_e
Definition itm.cpp:64
double aes
Definition itm.cpp:82
double d_L
Definition itm.cpp:86
double A_ref
Definition itm.cpp:74
double theta_ej[2]
Definition itm.cpp:70
double theta_e
Definition itm.cpp:87
double ak2
Definition itm.cpp:79
double d_Lsj[2]
Definition itm.cpp:84
double sgc
Definition itm.cpp:755