2011-09-04 09:18:13 +03:00
|
|
|
/*****************************************************************************\
|
|
|
|
* *
|
|
|
|
* The following code was derived from public domain ITM code available *
|
|
|
|
* at ftp://flattop.its.bldrdoc.gov/itm/ITMDLL.cpp that was released on *
|
|
|
|
* June 26, 2007. It was modified to remove Microsoft Windows "dll-isms", *
|
|
|
|
* redundant and unnecessary #includes, redundant and unnecessary { }'s, *
|
|
|
|
* to initialize uninitialized variables, type cast some variables, *
|
|
|
|
* re-format the code for easier reading, and to replace pow() function *
|
|
|
|
* calls with explicit multiplications wherever possible to increase *
|
|
|
|
* execution speed and improve computational accuracy. *
|
|
|
|
* *
|
|
|
|
*****************************************************************************
|
|
|
|
* *
|
|
|
|
* Added comments that refer to itm.pdf and itmalg.pdf in a way to easy *
|
|
|
|
* searching. *
|
|
|
|
* *
|
|
|
|
* // :0: Blah, page 0 This is the implementation of the code from *
|
|
|
|
* itm.pdf, section "0". The description is *
|
|
|
|
* found on page 0. *
|
|
|
|
* [Alg 0.0] please refer to algorithm 0.0 from itm_alg.pdf *
|
|
|
|
* *
|
|
|
|
* Holger Schurig, DH3HS *
|
|
|
|
\*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
// *************************************
|
|
|
|
// C++ routines for this program are taken from
|
|
|
|
// a translation of the FORTRAN code written by
|
|
|
|
// U.S. Department of Commerce NTIA/ITS
|
|
|
|
// Institute for Telecommunication Sciences
|
|
|
|
// *****************
|
|
|
|
// Irregular Terrain Model (ITM) (Longley-Rice)
|
|
|
|
// *************************************
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <math.h>
|
|
|
|
#include <complex>
|
|
|
|
#include <assert.h>
|
2011-11-24 06:39:54 +02:00
|
|
|
#include <stdio.h>
|
2011-09-04 09:18:13 +03:00
|
|
|
|
2011-12-04 22:08:56 +02:00
|
|
|
const double THIRD = (1.0/3.0);
|
|
|
|
const double f_0 = 47.7; // 47.7 MHz from [Alg 1.1], to convert frequency into wavenumber and vica versa
|
2011-09-04 09:18:13 +03:00
|
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
|
|
|
|
struct prop_type {
|
|
|
|
// General input
|
|
|
|
double d; // distance between the two terminals
|
|
|
|
double h_g[2]; // antenna structural heights (above ground)
|
|
|
|
double k; // wave number (= radio frequency)
|
|
|
|
double delta_h; // terrain irregularity parameter
|
|
|
|
double N_s; // minimum monthly surface refractivity, n-Units
|
|
|
|
double gamma_e; // earth's effective curvature
|
|
|
|
double Z_g_real; // surface transfer impedance of the ground
|
|
|
|
double Z_g_imag;
|
|
|
|
// Additional input for point-to-point mode
|
|
|
|
double h_e[2]; // antenna effective heights
|
|
|
|
double d_Lj[2]; // horizon distances
|
|
|
|
double theta_ej[2];// horizontal elevation angles
|
|
|
|
int mdp; // controlling mode: -1: point to point, 1 start of area, 0 area continuation
|
|
|
|
// Output
|
|
|
|
int kwx; // error indicator
|
|
|
|
double A_ref; // reference attenuation
|
|
|
|
// used to be propa_type, computed in lrprop()
|
|
|
|
double dx; // scatter distance
|
|
|
|
double ael; // line-of-sight coefficients
|
|
|
|
double ak1; // dito
|
|
|
|
double ak2; // dito
|
|
|
|
double aed; // diffraction coefficients
|
|
|
|
double emd; // dito
|
|
|
|
double aes; // scatter coefficients
|
|
|
|
double ems; // dito
|
|
|
|
double d_Lsj[2]; // smooth earth horizon distances
|
|
|
|
double d_Ls; // d_Lsj[] accumulated
|
|
|
|
double d_L; // d_Lj[] accumulated
|
|
|
|
double theta_e; // theta_ej[] accumulated, total bending angle
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
int mymin(const int &i, const int &j)
|
|
|
|
{
|
|
|
|
if (i < j)
|
|
|
|
return i;
|
|
|
|
else
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
int mymax(const int &i, const int &j)
|
|
|
|
{
|
|
|
|
if (i > j)
|
|
|
|
return i;
|
|
|
|
else
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
double mymin(const double &a, const double &b)
|
|
|
|
{
|
|
|
|
if (a < b)
|
|
|
|
return a;
|
|
|
|
else
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
double mymax(const double &a, const double &b)
|
|
|
|
{
|
|
|
|
if (a > b)
|
|
|
|
return a;
|
|
|
|
else
|
|
|
|
return b;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
double FORTRAN_DIM(const double &x, const double &y)
|
|
|
|
{
|
|
|
|
// This performs the FORTRAN DIM function.
|
|
|
|
// result is x-y if x is greater than y; otherwise result is 0.0
|
|
|
|
|
|
|
|
if (x > y)
|
|
|
|
return x - y;
|
|
|
|
else
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
2011-12-03 20:08:50 +02:00
|
|
|
//#define set_warn(txt, err) printf("%s:%d %s\n", __func__, __LINE__, txt);
|
|
|
|
#define set_warn(txt, err) 1;
|
2011-09-04 09:18:13 +03:00
|
|
|
|
|
|
|
// :13: single-knife attenuation, page 6
|
|
|
|
/*
|
|
|
|
* The attenuation due to a sigle knife edge -- this is an approximation of
|
|
|
|
* a Fresnel integral as a function of v^2. The non-approximated integral
|
|
|
|
* is documented as [Alg 4.21]
|
|
|
|
*
|
|
|
|
* Now, what is "single knife attenuation" ? Googling found some paper
|
|
|
|
* http://www.its.bldrdoc.gov/pub/ntia-rpt/81-86/81-86.pdf, which actually
|
|
|
|
* talks about multi-knife attenuation calculation. However, it says there
|
|
|
|
* that single-knife attenuation models attenuation over the edge of one
|
|
|
|
* isolated hill.
|
|
|
|
*
|
|
|
|
* Note that the arguments to this function aren't v, but v^2
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double Fn(const double &v_square)
|
|
|
|
{
|
|
|
|
double a;
|
|
|
|
|
|
|
|
// [Alg 6.1]
|
|
|
|
if (v_square <= 5.76) // this is the 2.40 from the text, but squared
|
|
|
|
a = 6.02 + 9.11 * sqrt(v_square) - 1.27 * v_square;
|
|
|
|
else
|
|
|
|
a = 12.953 + 4.343 * log(v_square);
|
|
|
|
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :14: page 6
|
|
|
|
/*
|
|
|
|
* The heigh-gain over a smooth spherical earth -- to be used in the "three
|
|
|
|
* radii" mode. The approximation is that given in in [Alg 6.4ff].
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double F(const double& x, const double& K)
|
|
|
|
{
|
|
|
|
double w, fhtv;
|
|
|
|
if (x <= 200.0) {
|
|
|
|
// F = F_2(x, L), which is defined in [Alg 6.6]
|
|
|
|
|
|
|
|
w = -log(K);
|
|
|
|
|
|
|
|
// XXX the text says "or x * w^3 > 450"
|
|
|
|
if (K < 1e-5 || (x * w * w * w) > 5495.0) {
|
|
|
|
// F_2(x, k) = F_1(x), which is defined in [Alg 6.5]
|
|
|
|
// XXX but this isn't the same as in itm_alg.pdf
|
|
|
|
fhtv = -117.0;
|
|
|
|
if (x > 1.0)
|
|
|
|
fhtv = 17.372 * log(x) + fhtv;
|
|
|
|
} else {
|
|
|
|
// [Alg 6.6], lower part
|
|
|
|
fhtv = 2.5e-5 * x * x / K - 8.686 * w - 15.0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// [Alg 6.3] and [Alg 6.4], lower part, which is G(x)
|
|
|
|
fhtv = 0.05751 * x - 4.343 * log(x);
|
|
|
|
|
|
|
|
// [Alg 6.4], middle part, but again XXX this doesn't match totally
|
|
|
|
if (x < 2000.0) {
|
|
|
|
w = 0.0134 * x * exp(-0.005 * x);
|
|
|
|
fhtv = (1.0 - w) * fhtv + w * (17.372 * log(x) - 117.0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fhtv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :25: Tropospheric scatter frequency gain, [Alg 6.10ff], page 12
|
|
|
|
static
|
|
|
|
double H_0(double r, double et)
|
|
|
|
{
|
|
|
|
// constants from [Alg 6.13]
|
|
|
|
const double a[5] = {25.0, 80.0, 177.0, 395.0, 705.0};
|
|
|
|
const double b[5] = {24.0, 45.0, 68.0, 80.0, 105.0};
|
|
|
|
double q, x;
|
|
|
|
int it;
|
|
|
|
double h0fv;
|
|
|
|
|
|
|
|
it = (int)et;
|
|
|
|
|
|
|
|
if (it <= 0) {
|
|
|
|
it = 1;
|
|
|
|
q = 0.0;
|
|
|
|
} else
|
|
|
|
if (it >= 4) {
|
|
|
|
it = 4;
|
|
|
|
q = 0.0;
|
|
|
|
} else {
|
|
|
|
q = et - it;
|
|
|
|
}
|
|
|
|
|
|
|
|
x = (1.0 / r);
|
|
|
|
x *= x;
|
|
|
|
// [Alg 6.13], calculates something like H_01(r,j), but not really XXX
|
|
|
|
h0fv = 4.343 * log((1.0 + a[it-1] * x + b[it-1]) * x);
|
|
|
|
|
|
|
|
// XXX not sure what this means
|
|
|
|
if (q != 0.0)
|
|
|
|
h0fv = (1.0 - q) * h0fv + q * 4.343 * log((a[it] * x + b[it]) * x + 1.0);
|
|
|
|
|
|
|
|
return h0fv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :25: This is the F(\Theta d) function for scatter fields, page 12
|
|
|
|
static
|
|
|
|
double F_0(double td)
|
|
|
|
{
|
|
|
|
// [Alg 6.9]
|
|
|
|
if (td <= 10e3) // below 10 km
|
|
|
|
return 133.4 + 104.6 * td + 71.8 * log(td);
|
|
|
|
else
|
|
|
|
if (td <= 70e3) // between 10 km and 70 km
|
|
|
|
return 0.332e-3 + 0.212e-3 * td + 0.157e-3 * log(td);
|
|
|
|
else // above 70 km
|
|
|
|
return-4.343 + -1.086 * td + 2.171 * log(td);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :10: Diffraction attenuation, page 4
|
|
|
|
static
|
|
|
|
double adiff(double s, prop_type &prop)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* The function adiff finds the "Diffraction attenuation" at the
|
|
|
|
* distance s. It uses a convex combination of smooth earth
|
|
|
|
* diffraction and knife-edge diffraction.
|
|
|
|
*/
|
|
|
|
static double wd1, xd1, A_fo, qk, aht, xht;
|
|
|
|
const double A = 151.03; // dimensionles constant from [Alg 4.20]
|
|
|
|
const double D = 50e3; // 50 km from [Alg 3.9], scale distance for \delta_h(s)
|
|
|
|
const double H = 16; // 16 m from [Alg 3.10]
|
|
|
|
const double ALPHA = 4.77e-4; // from [Alg 4.10]
|
|
|
|
|
|
|
|
if (s == 0.0) {
|
|
|
|
complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
|
|
|
|
|
|
|
|
// :11: Prepare initial diffraction constants, page 5
|
|
|
|
double q = prop.h_g[0] * prop.h_g[1];
|
|
|
|
double qk = prop.h_e[0] * prop.h_e[1] - q;
|
|
|
|
|
|
|
|
if (prop.mdp < 0.0)
|
|
|
|
q += 10.0; // "C" from [Alg 4.9]
|
|
|
|
|
|
|
|
// wd1 and xd1 are parts of Q in [Alg 4.10], but I cannot find this there
|
|
|
|
wd1 = sqrt(1.0 + qk / q);
|
|
|
|
xd1 = prop.d_L + prop.theta_e / prop.gamma_e; // [Alg 4.9] upper right
|
|
|
|
// \delta_h(s), [Alg 3.9]
|
|
|
|
q = (1.0 - 0.8 * exp(-prop.d_Ls / D)) * prop.delta_h;
|
|
|
|
// \sigma_h(s), [Alg 3.10]
|
|
|
|
q *= 0.78 * exp(-pow(q / H, 0.25));
|
|
|
|
|
|
|
|
// A_fo is the "clutter factor"
|
|
|
|
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]
|
|
|
|
// qk is part of the K_j calculation from [Alg 4.17]
|
|
|
|
qk = 1.0 / abs(prop_zgnd);
|
|
|
|
aht = 20.0; // 20 dB approximation for C_1(K) from [Alg 6.7], see also [Alg 4.25]
|
|
|
|
xht = 0.0;
|
|
|
|
|
|
|
|
for (int j = 0; j < 2; ++j) {
|
|
|
|
double gamma_j_recip, alpha, K;
|
|
|
|
// [Alg 4.15], a is reciproke of gamma_j
|
|
|
|
gamma_j_recip = 0.5 * (prop.d_Lj[j] * prop.d_Lj[j]) / prop.h_e[j];
|
|
|
|
// [Alg 4.16]
|
|
|
|
alpha = pow(gamma_j_recip * prop.k, THIRD);
|
|
|
|
// [Alg 4.17]
|
|
|
|
K = qk / alpha;
|
|
|
|
// [Alg 4.18 and 6.2]
|
|
|
|
q = A * (1.607 - K) * alpha * prop.d_Lj[j] / gamma_j_recip;
|
|
|
|
// [Alg 4.19, high gain part]
|
|
|
|
xht += q;
|
|
|
|
// [Alg 4.20] ?, F(x, k) is in [Alg 6.4]
|
|
|
|
aht += F(q, K);
|
|
|
|
}
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
double gamma_o_recip, q, K, ds, theta, alpha, A_r, w;
|
|
|
|
|
|
|
|
// :12: Compute diffraction attenuation, page 5
|
|
|
|
// [Alg 4.12]
|
|
|
|
theta = prop.theta_e + s * prop.gamma_e;
|
|
|
|
// XXX this is not [Alg 4.13]
|
|
|
|
ds = s - prop.d_L;
|
|
|
|
q = 0.0795775 * prop.k * ds * theta * theta;
|
|
|
|
// [Alg 4.14], double knife edge attenuation
|
|
|
|
// Note that the arguments to Fn() are not v, but v^2
|
|
|
|
double A_k = Fn(q * prop.d_Lj[0] / (ds + prop.d_Lj[0])) +
|
|
|
|
Fn(q * prop.d_Lj[1] / (ds + prop.d_Lj[1]));
|
|
|
|
// kinda [Alg 4.15], just so that gamma_o is 1/a
|
|
|
|
gamma_o_recip = ds / theta;
|
|
|
|
// [Alg 4.16]
|
|
|
|
alpha = pow(gamma_o_recip * prop.k, THIRD);
|
|
|
|
// [Alg 4.17], note that qk is "1.0 / abs(prop_zgnd)" from above
|
|
|
|
K = qk / alpha;
|
|
|
|
// [Alg 4.19], q is now X_o
|
|
|
|
q = A * (1.607 - K) * alpha * theta + xht;
|
|
|
|
// looks a bit like [Alg 4.20], rounded earth attenuation, or??
|
|
|
|
// note that G(x) should be "0.05751 * x - 10 * log(q)"
|
|
|
|
A_r = 0.05751 * q - 4.343 * log(q) - aht;
|
|
|
|
// I'm very unsure if this has anything to do with [Alg 4.9] or not
|
|
|
|
q = (wd1 + xd1 / s) * mymin(((1.0 - 0.8 * exp(-s / 50e3)) * prop.delta_h * prop.k), 6283.2);
|
|
|
|
// XXX this is NOT the same as the weighting factor from [Alg 4.9]
|
|
|
|
w = 25.1 / (25.1 + sqrt(q));
|
|
|
|
// [Alg 4.11]
|
|
|
|
return (1.0 - w) * A_k + w * A_r + A_fo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* :22: Scatter attenuation, page 9
|
|
|
|
*
|
|
|
|
* The function ascat finds the "scatter attenuation" at the distance d. It
|
|
|
|
* uses an approximation to the methods of NBS TN101 with check for
|
|
|
|
* inadmissable situations. For proper operation, the larger distance (d =
|
|
|
|
* d6) must be the first called. A call with d = 0 sets up initial
|
|
|
|
* constants.
|
|
|
|
*
|
|
|
|
* One needs to get TN101, especially chaper 9, to understand this function.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double A_scat(double s, prop_type &prop)
|
|
|
|
{
|
|
|
|
static double ad, rr, etq, h0s;
|
|
|
|
|
|
|
|
if (s == 0.0) {
|
|
|
|
// :23: Prepare initial scatter constants, page 10
|
|
|
|
ad = prop.d_Lj[0] - prop.d_Lj[1];
|
|
|
|
rr = prop.h_e[1] / prop.h_e[0];
|
|
|
|
|
|
|
|
if (ad < 0.0) {
|
|
|
|
ad = -ad;
|
|
|
|
rr = 1.0 / rr;
|
|
|
|
}
|
|
|
|
|
|
|
|
etq = (5.67e-6 * prop.N_s - 2.32e-3) * prop.N_s + 0.031; // part of [Alg 4.67]
|
|
|
|
h0s = -15.0;
|
|
|
|
return 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
double h0, r1, r2, z0, ss, et, ett, theta_tick, q, temp;
|
|
|
|
|
|
|
|
// :24: Compute scatter attenuation, page 11
|
|
|
|
if (h0s > 15.0)
|
|
|
|
h0 = h0s;
|
|
|
|
else {
|
|
|
|
// [Alg 4.61]
|
|
|
|
theta_tick = prop.theta_ej[0] + prop.theta_ej[1] + prop.gamma_e * s;
|
|
|
|
// [Alg 4.62]
|
|
|
|
r2 = 2.0 * prop.k * theta_tick;
|
|
|
|
r1 = r2 * prop.h_e[0];
|
|
|
|
r2 *= prop.h_e[1];
|
|
|
|
|
|
|
|
if (r1 < 0.2 && r2 < 0.2)
|
|
|
|
// The function is undefined
|
|
|
|
return 1001.0;
|
|
|
|
|
|
|
|
// XXX not like [Alg 4.65]
|
|
|
|
ss = (s - ad) / (s + ad);
|
|
|
|
q = rr / ss;
|
|
|
|
ss = mymax(0.1, ss);
|
|
|
|
q = mymin(mymax(0.1, q), 10.0);
|
|
|
|
// XXX not like [Alg 4.66]
|
|
|
|
z0 = (s - ad) * (s + ad) * theta_tick * 0.25 / s;
|
|
|
|
// [Alg 4.67]
|
|
|
|
temp = mymin(1.7, z0 / 8.0e3);
|
|
|
|
temp = temp * temp * temp * temp * temp * temp;
|
|
|
|
et = (etq * exp(-temp) + 1.0) * z0 / 1.7556e3;
|
|
|
|
|
|
|
|
ett = mymax(et, 1.0);
|
|
|
|
h0 = (H_0(r1, ett) + H_0(r2, ett)) * 0.5; // [Alg 6.12]
|
|
|
|
h0 += mymin(h0, (1.38 - log(ett)) * log(ss) * log(q) * 0.49); // [Alg 6.10 and 6.11]
|
|
|
|
h0 = FORTRAN_DIM(h0, 0.0);
|
|
|
|
|
|
|
|
if (et < 1.0)
|
|
|
|
// [Alg 6.14]
|
|
|
|
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));
|
|
|
|
|
|
|
|
if (h0 > 15.0 && h0s >= 0.0)
|
|
|
|
h0 = h0s;
|
|
|
|
}
|
|
|
|
|
|
|
|
h0s = h0;
|
|
|
|
double theta = prop.theta_e + s * prop.gamma_e; // [Alg 4.60]
|
|
|
|
// [Alg 4.63 and 6.8]
|
|
|
|
const double D_0 = 40e3; // 40 km from [Alg 6.8]
|
|
|
|
const double H = 47.7; // 47.7 m from [Alg 4.63]
|
|
|
|
return 4.343 * log(prop.k * H * theta * theta * theta * theta)
|
|
|
|
+ F_0(theta * s)
|
|
|
|
- 0.1 * (prop.N_s - 301.0) * exp(-theta * s / D_0)
|
|
|
|
+ h0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static
|
|
|
|
double abq_alos(complex<double> r)
|
|
|
|
{
|
|
|
|
return r.real() * r.real() + r.imag() * r.imag();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* :17: line-of-sight attenuation, page 8
|
|
|
|
*
|
|
|
|
* The function alos finds the "line-of-sight attenuation" at the distance
|
|
|
|
* d. It uses a convex combination of plane earth fields and diffracted
|
|
|
|
* fields. A call with d=0 sets up initial constants.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double A_los(double d, prop_type &prop)
|
|
|
|
{
|
|
|
|
static double wls;
|
|
|
|
|
|
|
|
if (d == 0.0) {
|
|
|
|
// :18: prepare initial line-of-sight constants, page 8
|
|
|
|
const double D1 = 47.7; // 47.7 m from [Alg 4.43]
|
|
|
|
const double D2 = 10e3; // 10 km from [Alg 4.43]
|
|
|
|
const double D1R = 1 / D1;
|
|
|
|
// weighting factor w
|
|
|
|
wls = D1R / (D1R + prop.k * prop.delta_h / mymax(D2, prop.d_Ls)); // [Alg 4.43]
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
|
|
|
|
complex<double> r;
|
|
|
|
double s, sps, q;
|
|
|
|
|
|
|
|
// :19: compute line of sight attentuation, page 8
|
|
|
|
const double D = 50e3; // 50 km from [Alg 3.9]
|
|
|
|
const double H = 16.0; // 16 m from [Alg 3.10]
|
|
|
|
q = (1.0 - 0.8 * exp(-d / D)) * prop.delta_h; // \Delta h(d), [Alg 3.9]
|
|
|
|
s = 0.78 * q * exp(-pow(q / H, 0.25)); // \sigma_h(d), [Alg 3.10]
|
|
|
|
q = prop.h_e[0] + prop.h_e[1];
|
|
|
|
sps = q / sqrt(d * d + q * q); // sin(\psi), [Alg 4.46]
|
|
|
|
r = (sps - prop_zgnd) / (sps + prop_zgnd) * exp(-mymin(10.0, prop.k * s * sps)); // [Alg 4.47]
|
|
|
|
q = abq_alos(r);
|
|
|
|
|
|
|
|
if (q < 0.25 || q < sps) // [Alg 4.48]
|
|
|
|
r = r * sqrt(sps / q);
|
|
|
|
|
|
|
|
double alosv = prop.emd * d + prop.aed; // [Alg 4.45]
|
|
|
|
q = prop.k * prop.h_e[0] * prop.h_e[1] * 2.0 / d; // [Alg 4.49]
|
|
|
|
|
|
|
|
// M_PI is pi, M_PI_2 is pi/2
|
|
|
|
if (q > M_PI_2) // [Alg 4.50]
|
|
|
|
q = M_PI - (M_PI_2 * M_PI_2) / q;
|
|
|
|
|
|
|
|
// [Alg 4.51 and 4.44]
|
|
|
|
return (-4.343 * log(abq_alos(complex<double>(cos(q), -sin(q)) + r)) - alosv) * wls + alosv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :5: LRprop, page 2
|
|
|
|
/*
|
|
|
|
* The value mdp controls some of the program flow. When it equals -1 we are
|
|
|
|
* in point-to-point mode, when 1 we are beginning the area mode, and when 0
|
|
|
|
* we are continuing the area mode. The assumption is that when one uses the
|
|
|
|
* area mode, one will want a sequence of results for varying distances.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void lrprop(double d, prop_type &prop)
|
|
|
|
{
|
|
|
|
static bool wlos, wscat;
|
|
|
|
static double dmin, xae;
|
|
|
|
complex<double> prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
|
|
|
|
double a0, a1, a2, a3, a4, a5, a6;
|
|
|
|
double d0, d1, d2, d3, d4, d5, d6;
|
|
|
|
bool wq;
|
|
|
|
double q;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
|
|
|
|
if (prop.mdp != 0) {
|
|
|
|
// :6: Do secondary parameters, page 3
|
|
|
|
// [Alg 3.5]
|
|
|
|
for (j = 0; j < 2; j++)
|
|
|
|
prop.d_Lsj[j] = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
|
|
|
|
|
|
|
|
// [Alg 3.6]
|
|
|
|
prop.d_Ls = prop.d_Lsj[0] + prop.d_Lsj[1];
|
|
|
|
// [Alg 3.7]
|
|
|
|
prop.d_L = prop.d_Lj[0] + prop.d_Lj[1];
|
|
|
|
// [Alg 3.8]
|
|
|
|
prop.theta_e = mymax(prop.theta_ej[0] + prop.theta_ej[1], -prop.d_L * prop.gamma_e);
|
|
|
|
wlos = false;
|
|
|
|
wscat = false;
|
|
|
|
|
|
|
|
// :7: Check parameters range, page 3
|
|
|
|
|
|
|
|
// kwx is some kind of error indicator. Setting kwx to 1
|
|
|
|
// means "results are slightly bad", while setting it to 4
|
|
|
|
// means "my calculations will be bogus"
|
|
|
|
|
|
|
|
// Frequency must be between 40 MHz and 10 GHz
|
|
|
|
// Wave number (wn) = 2 * M_PI / lambda, or f/f0, where f0 = 47.7 MHz*m;
|
|
|
|
// 0.838 => 40 MHz, 210 => 10 GHz
|
|
|
|
if (prop.k < 0.838 || prop.k > 210.0) {
|
|
|
|
set_warn("Frequency not optimal", 1);
|
|
|
|
prop.kwx = mymax(prop.kwx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Surface refractivity, see [Alg 1.2]
|
|
|
|
if (prop.N_s < 250.0 || prop.N_s > 400.0) {
|
|
|
|
set_warn("Surface refractivity out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
} else
|
|
|
|
// Earth's effective curvature, see [Alg 1.3]
|
|
|
|
if (prop.gamma_e < 75e-9 || prop.gamma_e > 250e-9) {
|
|
|
|
set_warn("Earth's curvature out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
} else
|
|
|
|
// Surface transfer impedance, see [Alg 1.4]
|
|
|
|
if (prop_zgnd.real() <= abs(prop_zgnd.imag())) {
|
|
|
|
set_warn("Surface transfer impedance out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
} else
|
|
|
|
// Calulating outside of 20 MHz to 40 GHz is really bad
|
|
|
|
if (prop.k < 0.419 || prop.k > 420.0) {
|
|
|
|
set_warn("Frequency out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
} else
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
// Antenna structural height should be between 1 and 1000 m
|
|
|
|
if (prop.h_g[j] < 1.0 || prop.h_g[j] > 1000.0) {
|
|
|
|
set_warn("Antenna height not optimal", 1);
|
|
|
|
prop.kwx = mymax(prop.kwx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Horizon elevation angle
|
|
|
|
if (abs(prop.theta_ej[j]) > 200e-3) {
|
|
|
|
set_warn("Horizon elevation weird", 3);
|
|
|
|
prop.kwx = mymax(prop.kwx, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Horizon distance dl,
|
|
|
|
// Smooth earth horizon distance dls
|
|
|
|
if (prop.d_Lj[j] < 0.1 * prop.d_Lsj[j] ||
|
|
|
|
prop.d_Lj[j] > 3.0 * prop.d_Lsj[j]) {
|
|
|
|
set_warn("Horizon distance weird", 3);
|
|
|
|
prop.kwx = mymax(prop.kwx, 3);
|
|
|
|
}
|
|
|
|
// Antenna structural height must between 0.5 and 3000 m
|
|
|
|
if (prop.h_g[j] < 0.5 || prop.h_g[j] > 3000.0) {
|
|
|
|
set_warn("Antenna heights out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dmin = abs(prop.h_e[0] - prop.h_e[1]) / 200e-3;
|
|
|
|
|
|
|
|
// :9: Diffraction coefficients, page 4
|
|
|
|
/*
|
|
|
|
* This is the region beyond the smooth-earth horizon at
|
|
|
|
* D_Lsa and short of where isotropic scatter takes over. It
|
|
|
|
* is a key to central region and associated coefficients
|
|
|
|
* must always be computed.
|
|
|
|
*/
|
|
|
|
q = adiff(0.0, prop);
|
|
|
|
xae = pow(prop.k * prop.gamma_e * prop.gamma_e, -THIRD); // [Alg 4.2]
|
|
|
|
d3 = mymax(prop.d_Ls, 1.3787 * xae + prop.d_L); // [Alg 4.3]
|
|
|
|
d4 = d3 + 2.7574 * xae; // [Alg 4.4]
|
|
|
|
a3 = adiff(d3, prop); // [Alg 4.5]
|
|
|
|
a4 = adiff(d4, prop); // [Alg 4.7]
|
|
|
|
|
|
|
|
prop.emd = (a4 - a3) / (d4 - d3); // [Alg 4.8]
|
|
|
|
prop.aed = a3 - prop.emd * d3;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop.mdp >= 0) {
|
|
|
|
prop.mdp = 0;
|
|
|
|
prop.d = d;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop.d > 0.0) {
|
|
|
|
// :8: Check distance, page 3
|
|
|
|
|
|
|
|
// Distance above 1000 km is guessing
|
|
|
|
if (prop.d > 1000e3) {
|
|
|
|
set_warn("Distance not optimal", 1);
|
|
|
|
prop.kwx = mymax(prop.kwx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Distance too small, use some indoor algorithm :-)
|
|
|
|
if (prop.d < dmin) {
|
|
|
|
set_warn("Distance too small", 3);
|
|
|
|
prop.kwx = mymax(prop.kwx, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Distance above 2000 km is really bad, don't do that
|
|
|
|
if (prop.d < 1e3 || prop.d > 2000e3) {
|
|
|
|
set_warn("Distance out-of-bounds", 4);
|
|
|
|
prop.kwx = 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop.d < prop.d_Ls) {
|
|
|
|
// :15: Line-of-sight calculations, page 7
|
|
|
|
if (!wlos) {
|
|
|
|
// :16: Line-of-sight coefficients, page 7
|
|
|
|
q = A_los(0.0, prop);
|
|
|
|
d2 = prop.d_Ls;
|
|
|
|
a2 = prop.aed + d2 * prop.emd;
|
|
|
|
d0 = 1.908 * prop.k * prop.h_e[0] * prop.h_e[1]; // [Alg 4.38]
|
|
|
|
|
|
|
|
if (prop.aed >= 0.0) {
|
|
|
|
d0 = mymin(d0, 0.5 * prop.d_L); // [Alg 4.28]
|
|
|
|
d1 = d0 + 0.25 * (prop.d_L - d0); // [Alg 4.29]
|
|
|
|
} else {
|
|
|
|
d1 = mymax(-prop.aed / prop.emd, 0.25 * prop.d_L); // [Alg 4.39]
|
|
|
|
}
|
|
|
|
|
|
|
|
a1 = A_los(d1, prop); // [Alg 4.31]
|
|
|
|
wq = false;
|
|
|
|
|
|
|
|
if (d0 < d1) {
|
|
|
|
a0 = A_los(d0, prop); // [Alg 4.30]
|
|
|
|
q = log(d2 / d0);
|
|
|
|
prop.ak2 = mymax(0.0, ((d2 - d0) * (a1 - a0) - (d1 - d0) * (a2 - a0)) / ((d2 - d0) * log(d1 / d0) - (d1 - d0) * q)); // [Alg 4.32]
|
|
|
|
wq = prop.aed >= 0.0 || prop.ak2 > 0.0;
|
|
|
|
|
|
|
|
if (wq) {
|
|
|
|
prop.ak1 = (a2 - a0 - prop.ak2 * q) / (d2 - d0); // [Alg 4.33]
|
|
|
|
|
|
|
|
if (prop.ak1 < 0.0) {
|
|
|
|
prop.ak1 = 0.0; // [Alg 4.36]
|
|
|
|
prop.ak2 = FORTRAN_DIM(a2, a0) / q; // [Alg 4.35]
|
|
|
|
|
|
|
|
if (prop.ak2 == 0.0) // [Alg 4.37]
|
|
|
|
prop.ak1 = prop.emd;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wq) {
|
|
|
|
prop.ak1 = FORTRAN_DIM(a2, a1) / (d2 - d1); // [Alg 4.40]
|
|
|
|
prop.ak2 = 0.0; // [Alg 4.41]
|
|
|
|
|
|
|
|
if (prop.ak1 == 0.0) // [Alg 4.37]
|
|
|
|
prop.ak1 = prop.emd;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.ael = a2 - prop.ak1 * d2 - prop.ak2 * log(d2); // [Alg 4.42]
|
|
|
|
wlos = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop.d > 0.0)
|
|
|
|
// [Alg 4.1]
|
|
|
|
/*
|
|
|
|
* The reference attenuation is determined as a function of the distance
|
|
|
|
* d from 3 piecewise formulatios. This is part one.
|
|
|
|
*/
|
|
|
|
prop.A_ref = prop.ael + prop.ak1 * prop.d + prop.ak2 * log(prop.d);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prop.d <= 0.0 || prop.d >= prop.d_Ls) {
|
|
|
|
// :20: Troposcatter calculations, page 9
|
|
|
|
if (!wscat) {
|
|
|
|
// :21: Troposcatter coefficients
|
|
|
|
const double DS = 200e3; // 200 km from [Alg 4.53]
|
|
|
|
const double HS = 47.7; // 47.7 m from [Alg 4.59]
|
|
|
|
q = A_scat(0.0, prop);
|
|
|
|
d5 = prop.d_L + DS; // [Alg 4.52]
|
|
|
|
d6 = d5 + DS; // [Alg 4.53]
|
|
|
|
a6 = A_scat(d6, prop); // [Alg 4.54]
|
|
|
|
a5 = A_scat(d5, prop); // [Alg 4.55]
|
|
|
|
|
|
|
|
if (a5 < 1000.0) {
|
|
|
|
prop.ems = (a6 - a5) / DS; // [Alg 4.57]
|
|
|
|
prop.dx = mymax(prop.d_Ls, // [Alg 4.58]
|
|
|
|
mymax(prop.d_L + 0.3 * xae * log(HS * prop.k),
|
|
|
|
(a5 - prop.aed - prop.ems * d5) / (prop.emd - prop.ems)));
|
|
|
|
prop.aes = (prop.emd - prop.ems) * prop.dx + prop.aed; // [Alg 4.59]
|
|
|
|
} else {
|
|
|
|
prop.ems = prop.emd;
|
|
|
|
prop.aes = prop.aed;
|
|
|
|
prop.dx = 10.e6; // [Alg 4.56]
|
|
|
|
}
|
|
|
|
wscat = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// [Alg 4.1], part two and three.
|
|
|
|
if (prop.d > prop.dx)
|
|
|
|
prop.A_ref = prop.aes + prop.ems * prop.d; // scatter region
|
|
|
|
else
|
|
|
|
prop.A_ref = prop.aed + prop.emd * prop.d; // diffraction region
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.A_ref = mymax(prop.A_ref, 0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
// :27: Variablility parameters, page 13
|
|
|
|
|
|
|
|
struct propv_type {
|
|
|
|
// Input:
|
|
|
|
int lvar; // control switch for initialisation and/or recalculation
|
|
|
|
// 0: no recalculations needed
|
|
|
|
// 1: distance changed
|
|
|
|
// 2: antenna heights changed
|
|
|
|
// 3: frequency changed
|
|
|
|
// 4: mdvar changed
|
|
|
|
// 5: climate changed, or initialize everything
|
|
|
|
int mdvar; // desired mode of variability
|
|
|
|
int klim; // climate indicator
|
|
|
|
// Output
|
|
|
|
double sgc; // standard deviation of situation variability (confidence)
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_QRLA
|
|
|
|
// :40: Prepare model for area prediction mode, page 21
|
|
|
|
/*
|
|
|
|
* This is used to prepare the model in the area prediction mode. Normally,
|
|
|
|
* one first calls qlrps() and then qlra(). Before calling the latter, one
|
|
|
|
* should have defined in prop_type the antenna heights @hg, the terrain
|
|
|
|
* irregularity parameter @dh , and (probably through qlrps() the variables
|
|
|
|
* @wn, @ens, @gme, and @zgnd. The input @kst will define siting criteria
|
|
|
|
* for the terminals, @klimx the climate, and @mdvarx the mode of
|
|
|
|
* variability. If @klimx <= 0 or @mdvarx < 0 the associated parameters
|
|
|
|
* remain unchanged.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void qlra(int kst[], int klimx, int mdvarx, prop_type &prop, propv_type &propv)
|
|
|
|
{
|
|
|
|
double q;
|
|
|
|
|
|
|
|
for (int j = 0; j < 2; ++j) {
|
|
|
|
if (kst[j] <= 0)
|
|
|
|
// [Alg 3.1]
|
|
|
|
prop.h_e[j] = prop.h_g[j];
|
|
|
|
else {
|
|
|
|
q = 4.0;
|
|
|
|
|
|
|
|
if (kst[j] != 1)
|
|
|
|
q = 9.0;
|
|
|
|
|
|
|
|
if (prop.h_g[j] < 5.0)
|
|
|
|
q *= sin(0.3141593 * prop.h_g[j]);
|
|
|
|
|
|
|
|
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)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// [Alg 3.3], upper function. q is d_Ls_j
|
|
|
|
const double H_3 = 5; // 5m from [Alg 3.3]
|
|
|
|
q = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
|
|
|
|
prop.d_Lj[j] = q * exp(-0.07 * sqrt(prop.delta_h / mymax(prop.h_e[j], H_3)));
|
|
|
|
// [Alg 3.4]
|
|
|
|
prop.theta_ej[j] = (0.65 * prop.delta_h * (q / prop.d_Lj[j] - 1.0) - 2.0 * prop.h_e[j]) / q;
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.mdp = 1;
|
|
|
|
propv.lvar = mymax(propv.lvar, 3);
|
|
|
|
|
|
|
|
if (mdvarx >= 0) {
|
|
|
|
propv.mdvar = mdvarx;
|
|
|
|
propv.lvar = mymax(propv.lvar, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (klimx > 0) {
|
|
|
|
propv.klim = klimx;
|
|
|
|
propv.lvar = 5;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// :51: Inverse of standard normal complementary probability
|
|
|
|
/*
|
|
|
|
* The approximation is due to C. Hastings, Jr. ("Approximations for digital
|
|
|
|
* computers," Princeton Univ. Press, 1955) and the maximum error should be
|
|
|
|
* 4.5e-4.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double qerfi(double q)
|
|
|
|
{
|
|
|
|
double x, t, v;
|
|
|
|
const double c0 = 2.515516698;
|
|
|
|
const double c1 = 0.802853;
|
|
|
|
const double c2 = 0.010328;
|
|
|
|
const double d1 = 1.432788;
|
|
|
|
const double d2 = 0.189269;
|
|
|
|
const double d3 = 0.001308;
|
|
|
|
|
|
|
|
x = 0.5 - q;
|
|
|
|
t = mymax(0.5 - fabs(x), 0.000001);
|
|
|
|
t = sqrt(-2.0 * log(t));
|
|
|
|
v = t - ((c2 * t + c1) * t + c0) / (((d3 * t + d2) * t + d1) * t + 1.0);
|
|
|
|
|
|
|
|
if (x < 0.0)
|
|
|
|
v = -v;
|
|
|
|
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :41: preparatory routine, page 20
|
|
|
|
/*
|
|
|
|
* This subroutine converts
|
|
|
|
* frequency @fmhz
|
|
|
|
* surface refractivity reduced to sea level @en0
|
|
|
|
* general system elevation @zsys
|
|
|
|
* polarization and ground constants @eps, @sgm
|
|
|
|
* to
|
|
|
|
* wave number @wn,
|
|
|
|
* surface refractivity @ens
|
|
|
|
* effective earth curvature @gme
|
|
|
|
* surface impedance @zgnd
|
|
|
|
* It may be used with either the area prediction or the point-to-point mode.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void qlrps(double fmhz, double zsys, double en0, int ipol, double eps, double sgm, prop_type &prop)
|
|
|
|
|
|
|
|
{
|
|
|
|
const double Z_1 = 9.46e3; // 9.46 km from [Alg 1.2]
|
|
|
|
const double gma = 157e-9; // 157e-9 1/m from [Alg 1.3]
|
|
|
|
const double N_1 = 179.3; // 179.3 N-units from [Alg 1.3]
|
|
|
|
const double Z_0 = 376.62; // 376.62 Ohm from [Alg 1.5]
|
|
|
|
|
|
|
|
// Frequecy -> Wave number
|
|
|
|
prop.k = fmhz / f_0; // [Alg 1.1]
|
|
|
|
|
|
|
|
// Surface refractivity
|
|
|
|
prop.N_s = en0;
|
|
|
|
if (zsys != 0.0)
|
|
|
|
prop.N_s *= exp(-zsys / Z_1); // [Alg 1.2]
|
|
|
|
|
|
|
|
// Earths effective curvature
|
|
|
|
prop.gamma_e = gma * (1.0 - 0.04665 * exp(prop.N_s / N_1)); // [Alg 1.3]
|
|
|
|
|
|
|
|
// Surface transfer impedance
|
|
|
|
complex<double> zq, prop_zgnd(prop.Z_g_real, prop.Z_g_imag);
|
|
|
|
zq = complex<double> (eps, Z_0 * sgm / prop.k); // [Alg 1.5]
|
|
|
|
prop_zgnd = sqrt(zq - 1.0);
|
|
|
|
|
|
|
|
// adjust surface transfer impedance for Polarization
|
|
|
|
if (ipol != 0.0)
|
|
|
|
prop_zgnd = prop_zgnd / zq; // [Alg 1.4]
|
|
|
|
|
|
|
|
prop.Z_g_real = prop_zgnd.real();
|
|
|
|
prop.Z_g_imag = prop_zgnd.imag();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :30: Function curv, page 15
|
|
|
|
static
|
|
|
|
double curve(double const &c1, double const &c2, double const &x1, double const &x2, double const &x3, double const &de)
|
|
|
|
{
|
|
|
|
double temp1, temp2;
|
|
|
|
|
|
|
|
temp1 = (de - x2) / x3;
|
|
|
|
temp2 = de / x1;
|
|
|
|
|
|
|
|
temp1 *= temp1;
|
|
|
|
temp2 *= temp2;
|
|
|
|
|
|
|
|
return (c1 + c2 / (1.0 + temp1)) * temp2 / (1.0 + temp2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :28: Area variablity, page 14
|
|
|
|
static
|
|
|
|
double avar(double zzt, double zzl, double zzc, prop_type &prop, propv_type &propv)
|
|
|
|
{
|
|
|
|
static int kdv;
|
|
|
|
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;
|
|
|
|
|
|
|
|
// :29: Climatic constants, page 15
|
|
|
|
// Indexes are:
|
|
|
|
// 0: equatorial
|
|
|
|
// 1: continental suptropical
|
|
|
|
// 2: maritime subtropical
|
|
|
|
// 3: desert
|
|
|
|
// 4: continental temperature
|
|
|
|
// 5: maritime over land
|
|
|
|
// 6: maritime over sea
|
|
|
|
// equator contsup maritsup desert conttemp mariland marisea
|
|
|
|
const double bv1[7] = { -9.67, -0.62, 1.26, -9.21, -0.62, -0.39, 3.15};
|
|
|
|
const double bv2[7] = { 12.7, 9.19, 15.5, 9.05, 9.19, 2.86, 857.9};
|
|
|
|
const double xv1[7] = {144.9e3, 228.9e3, 262.6e3, 84.1e3, 228.9e3, 141.7e3, 2222.e3};
|
|
|
|
const double xv2[7] = {190.3e3, 205.2e3, 185.2e3, 101.1e3, 205.2e3, 315.9e3, 164.8e3};
|
|
|
|
const double xv3[7] = {133.8e3, 143.6e3, 99.8e3, 98.6e3, 143.6e3, 167.4e3, 116.3e3};
|
|
|
|
const double bsm1[7] = { 2.13, 2.66, 6.11, 1.98, 2.68, 6.86, 8.51};
|
|
|
|
const double bsm2[7] = { 159.5, 7.67, 6.65, 13.11, 7.16, 10.38, 169.8};
|
|
|
|
const double xsm1[7] = {762.2e3, 100.4e3, 138.2e3, 139.1e3, 93.7e3, 187.8e3, 609.8e3};
|
|
|
|
const double xsm2[7] = {123.6e3, 172.5e3, 242.2e3, 132.7e3, 186.8e3, 169.6e3, 119.9e3};
|
|
|
|
const double xsm3[7] = { 94.5e3, 136.4e3, 178.6e3, 193.5e3, 133.5e3, 108.9e3, 106.6e3};
|
|
|
|
const double bsp1[7] = { 2.11, 6.87, 10.08, 3.68, 4.75, 8.58, 8.43};
|
|
|
|
const double bsp2[7] = { 102.3, 15.53, 9.60, 159.3, 8.12, 13.97, 8.19};
|
|
|
|
const double xsp1[7] = {636.9e3, 138.7e3, 165.3e3, 464.4e3, 93.2e3, 216.0e3, 136.2e3};
|
|
|
|
const double xsp2[7] = {134.8e3, 143.7e3, 225.7e3, 93.1e3, 135.9e3, 152.0e3, 188.5e3};
|
|
|
|
const double xsp3[7] = { 95.6e3, 98.6e3, 129.7e3, 94.2e3, 113.4e3, 122.7e3, 122.9e3};
|
|
|
|
// bds1 -> is similar to C_D from table 5.1 at [Alg 5.8]
|
|
|
|
// bzd1 -> is similar to z_D from table 5.1 at [Alg 5.8]
|
|
|
|
const double bsd1[7] = { 1.224, 0.801, 1.380, 1.000, 1.224, 1.518, 1.518};
|
|
|
|
const double bzd1[7] = { 1.282, 2.161, 1.282, 20.0, 1.282, 1.282, 1.282};
|
|
|
|
const double bfm1[7] = { 1.0, 1.0, 1.0, 1.0, 0.92, 1.0, 1.0};
|
|
|
|
const double bfm2[7] = { 0.0, 0.0, 0.0, 0.0, 0.25, 0.0, 0.0};
|
|
|
|
const double bfm3[7] = { 0.0, 0.0, 0.0, 0.0, 1.77, 0.0, 0.0};
|
|
|
|
const double bfp1[7] = { 1.0, 0.93, 1.0, 0.93, 0.93, 1.0, 1.0};
|
|
|
|
const double bfp2[7] = { 0.0, 0.31, 0.0, 0.19, 0.31, 0.0, 0.0};
|
|
|
|
const double bfp3[7] = { 0.0, 2.00, 0.0, 1.79, 2.00, 0.0, 0.0};
|
|
|
|
const double rt = 7.8, rl = 24.0;
|
|
|
|
static bool no_location_variability, no_situation_variability;
|
|
|
|
double avarv, q, vs, zt, zl, zc;
|
|
|
|
double sgt, yr;
|
|
|
|
int temp_klim;
|
|
|
|
|
|
|
|
if (propv.lvar > 0) {
|
|
|
|
// :31: Setup variablity constants, page 16
|
|
|
|
switch (propv.lvar) {
|
|
|
|
default:
|
|
|
|
// Initialization or climate change
|
|
|
|
|
|
|
|
// if climate is wrong, use some "continental temperature" as default
|
|
|
|
// and set error indicator
|
|
|
|
if (propv.klim <= 0 || propv.klim > 7) {
|
|
|
|
propv.klim = 5;
|
|
|
|
prop.kwx = mymax(prop.kwx, 2);
|
|
|
|
set_warn("Climate index set to continental", 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// convert climate number into index into the climate tables
|
|
|
|
temp_klim = propv.klim - 1;
|
|
|
|
|
|
|
|
// :32: Climatic coefficients, page 17
|
|
|
|
cv1 = bv1[temp_klim];
|
|
|
|
cv2 = bv2[temp_klim];
|
|
|
|
yv1 = xv1[temp_klim];
|
|
|
|
yv2 = xv2[temp_klim];
|
|
|
|
yv3 = xv3[temp_klim];
|
|
|
|
csm1 = bsm1[temp_klim];
|
|
|
|
csm2 = bsm2[temp_klim];
|
|
|
|
ysm1 = xsm1[temp_klim];
|
|
|
|
ysm2 = xsm2[temp_klim];
|
|
|
|
ysm3 = xsm3[temp_klim];
|
|
|
|
csp1 = bsp1[temp_klim];
|
|
|
|
csp2 = bsp2[temp_klim];
|
|
|
|
ysp1 = xsp1[temp_klim];
|
|
|
|
ysp2 = xsp2[temp_klim];
|
|
|
|
ysp3 = xsp3[temp_klim];
|
|
|
|
csd1 = bsd1[temp_klim];
|
|
|
|
zd = bzd1[temp_klim];
|
|
|
|
cfm1 = bfm1[temp_klim];
|
|
|
|
cfm2 = bfm2[temp_klim];
|
|
|
|
cfm3 = bfm3[temp_klim];
|
|
|
|
cfp1 = bfp1[temp_klim];
|
|
|
|
cfp2 = bfp2[temp_klim];
|
|
|
|
cfp3 = bfp3[temp_klim];
|
|
|
|
// fall throught
|
|
|
|
|
|
|
|
case 4:
|
|
|
|
// :33: Mode of variablity coefficients, page 17
|
|
|
|
|
|
|
|
// This code means that propv.mdvar can be
|
|
|
|
// 0 .. 3
|
|
|
|
// 10 .. 13, then no_location_variability is set (no location variability)
|
|
|
|
// 20 .. 23, then no_situation_variability is set (no situatian variability)
|
|
|
|
// 30 .. 33, then no_location_variability and no_situation_variability is set
|
|
|
|
kdv = propv.mdvar;
|
|
|
|
no_situation_variability = kdv >= 20;
|
|
|
|
if (no_situation_variability)
|
|
|
|
no_situation_variability -= 20;
|
|
|
|
|
|
|
|
no_location_variability = kdv >= 10;
|
|
|
|
if (no_location_variability)
|
|
|
|
kdv -= 10;
|
|
|
|
|
|
|
|
if (kdv < 0 || kdv > 3) {
|
|
|
|
kdv = 0;
|
|
|
|
set_warn("kdv set to 0", 2);
|
|
|
|
prop.kwx = mymax(prop.kwx, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
// fall throught
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
// :34: Frequency coefficients, page 18
|
|
|
|
q = log(0.133 * prop.k);
|
|
|
|
gm = cfm1 + cfm2 / ((cfm3 * q * cfm3 * q) + 1.0);
|
|
|
|
gp = cfp1 + cfp2 / ((cfp3 * q * cfp3 * q) + 1.0);
|
|
|
|
// fall throught
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// :35: System coefficients, page 18
|
|
|
|
// [Alg 5.3], effective distance
|
|
|
|
{
|
|
|
|
const double a_1 = 9000e3; // 9000 km from [[Alg 5.3]
|
|
|
|
//XXX I don't have any idea how they made up the third summand,
|
|
|
|
//XXX text says a_1 * pow(k * D_1, -THIRD)
|
|
|
|
//XXX with const double D_1 = 1266; // 1266 km
|
|
|
|
dexa = sqrt(2*a_1 * prop.h_e[0]) +
|
|
|
|
sqrt(2*a_1 * prop.h_e[1]) +
|
|
|
|
pow((575.7e12 / prop.k), THIRD);
|
|
|
|
}
|
|
|
|
// fall throught
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// :36: Distance coefficients, page 18
|
|
|
|
{
|
|
|
|
// [Alg 5.4]
|
|
|
|
const double D_0 = 130e3; // 130 km from [Alg 5.4]
|
|
|
|
if (prop.d < dexa)
|
|
|
|
de = D_0 * prop.d / dexa;
|
|
|
|
else
|
|
|
|
de = D_0 + prop.d - dexa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Quantiles of time variability are computed using a
|
|
|
|
* variation of the methods described in Section 10 and
|
|
|
|
* Annex III.7 of NBS~TN101, and also in CCIR
|
|
|
|
* Report {238-3}. Those methods speak of eight or nine
|
|
|
|
* discrete radio climates, of which seven have been
|
|
|
|
* documented with corresponding empirical curves. It is
|
|
|
|
* these empirical curves to which we refer below. They are
|
|
|
|
* all curves of quantiles of deviations versus the
|
|
|
|
* effective distance @de.
|
|
|
|
*/
|
|
|
|
vmd = curve(cv1, cv2, yv1, yv2, yv3, de); // [Alg 5.5]
|
|
|
|
// [Alg 5.7], the slopes or "pseudo-standard deviations":
|
|
|
|
// sgtm -> \sigma T-
|
|
|
|
// sgtp -> \sigma T+
|
|
|
|
sgtm = curve(csm1, csm2, ysm1, ysm2, ysm3, de) * gm;
|
|
|
|
sgtp = curve(csp1, csp2, ysp1, ysp2, ysp3, de) * gp;
|
|
|
|
// [Alg 5.8], ducting, "sgtd" -> \sigma TD
|
|
|
|
sgtd = sgtp * csd1;
|
|
|
|
tgtd = (sgtp - sgtd) * zd;
|
|
|
|
|
|
|
|
// Location variability
|
|
|
|
if (no_location_variability) {
|
|
|
|
sgl = 0.0;
|
|
|
|
} else {
|
|
|
|
// Alg [3.9]
|
|
|
|
q = (1.0 - 0.8 * exp(-prop.d / 50e3)) * prop.delta_h * prop.k;
|
|
|
|
// [Alg 5.9]
|
|
|
|
sgl = 10.0 * q / (q + 13.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Situation variability
|
|
|
|
if (no_situation_variability) {
|
|
|
|
vs0 = 0.0;
|
|
|
|
} else {
|
|
|
|
const double D = 100e3; // 100 km
|
|
|
|
vs0 = (5.0 + 3.0 * exp(-de / D)); // [Alg 5.10]
|
|
|
|
vs0 *= vs0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Mark all constants as initialized
|
|
|
|
propv.lvar = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :37: Correct normal deviates, page 19
|
|
|
|
zt = zzt;
|
|
|
|
zl = zzl;
|
|
|
|
zc = zzc;
|
|
|
|
// kdv is derived from prop.mdvar
|
|
|
|
switch (kdv) {
|
|
|
|
case 0:
|
|
|
|
// Single message mode. Time, location and situation variability
|
|
|
|
// are combined to form a confidence level.
|
|
|
|
zt = zc;
|
|
|
|
zl = zc;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 1:
|
|
|
|
// Individual mode. Reliability is given by time
|
|
|
|
// variability. Confidence. is a combination of location
|
|
|
|
// and situation variability.
|
|
|
|
zl = zc;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
// Mobile modes. Reliability is a combination of time and
|
|
|
|
// location variability. Confidence. is given by the
|
|
|
|
// situation variability.
|
|
|
|
zl = zt;
|
|
|
|
// case 3: Broadcast mode. like avar(zt, zl, zc).
|
|
|
|
// Reliability is given by the two-fold statement of at
|
|
|
|
// least qT of the time in qL of the locations. Confidence.
|
|
|
|
// is given by the situation variability.
|
|
|
|
}
|
|
|
|
if (fabs(zt) > 3.1 || fabs(zl) > 3.1 || fabs(zc) > 3.1) {
|
|
|
|
set_warn("Situations variables not optimal", 1);
|
|
|
|
prop.kwx = mymax(prop.kwx, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :38: Resolve standard deviations, page 19
|
|
|
|
if (zt < 0.0)
|
|
|
|
sgt = sgtm;
|
|
|
|
else
|
|
|
|
if (zt <= zd)
|
|
|
|
sgt = sgtp;
|
|
|
|
else
|
|
|
|
sgt = sgtd + tgtd / zt;
|
|
|
|
// [Alg 5.11], situation variability
|
|
|
|
vs = vs0 + (sgt * zt * sgt * zt) / (rt + zc * zc) + (sgl * zl * sgl * zl) / (rl + zc * zc);
|
|
|
|
|
|
|
|
|
|
|
|
// :39: Resolve deviations, page 19
|
|
|
|
if (kdv == 0) {
|
|
|
|
yr = 0.0;
|
|
|
|
propv.sgc = sqrt(sgt * sgt + sgl * sgl + vs);
|
|
|
|
} else
|
|
|
|
if (kdv == 1) {
|
|
|
|
yr = sgt * zt;
|
|
|
|
propv.sgc = sqrt(sgl * sgl + vs);
|
|
|
|
} else
|
|
|
|
if (kdv == 2) {
|
|
|
|
yr = sqrt(sgt * sgt + sgl * sgl) * zt;
|
|
|
|
propv.sgc = sqrt(vs);
|
|
|
|
} else {
|
|
|
|
yr = sgt * zt + sgl * zl;
|
|
|
|
propv.sgc = sqrt(vs);
|
|
|
|
}
|
|
|
|
|
|
|
|
// [Alg 5.1], area variability
|
|
|
|
avarv = prop.A_ref - vmd - yr - propv.sgc * zc;
|
|
|
|
|
|
|
|
// [Alg 5.2]
|
|
|
|
if (avarv < 0.0)
|
|
|
|
avarv = avarv * (29.0 - avarv) / (29.0 - 10.0 * avarv);
|
|
|
|
|
|
|
|
return avarv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :45: Find to horizons, page 24
|
|
|
|
/*
|
|
|
|
* Here we use the terrain profile @pfl to find the two horizons. Output
|
|
|
|
* consists of the horizon distances @dl and the horizon take-off angles
|
|
|
|
* @the. If the path is line-of-sight, the routine sets both horizon
|
|
|
|
* distances equal to @dist.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void hzns(double pfl[], prop_type &prop)
|
|
|
|
{
|
|
|
|
bool wq;
|
|
|
|
int np;
|
|
|
|
double xi, za, zb, qc, q, sb, sa;
|
|
|
|
|
|
|
|
np = (int)pfl[0];
|
|
|
|
xi = pfl[1];
|
|
|
|
za = pfl[2] + prop.h_g[0];
|
|
|
|
zb = pfl[np+2] + prop.h_g[1];
|
|
|
|
qc = 0.5 * prop.gamma_e;
|
|
|
|
q = qc * prop.d;
|
|
|
|
prop.theta_ej[1] = (zb - za) / prop.d;
|
|
|
|
prop.theta_ej[0] = prop.theta_ej[1] - q;
|
|
|
|
prop.theta_ej[1] = -prop.theta_ej[1] - q;
|
|
|
|
prop.d_Lj[0] = prop.d;
|
|
|
|
prop.d_Lj[1] = prop.d;
|
|
|
|
|
|
|
|
if (np >= 2) {
|
|
|
|
sa = 0.0;
|
|
|
|
sb = prop.d;
|
|
|
|
wq = true;
|
|
|
|
|
|
|
|
for (int i = 1; i < np; i++) {
|
|
|
|
sa += xi;
|
|
|
|
sb -= xi;
|
|
|
|
q = pfl[i+2] - (qc * sa + prop.theta_ej[0]) * sa - za;
|
|
|
|
|
|
|
|
if (q > 0.0) {
|
|
|
|
prop.theta_ej[0] += q / sa;
|
|
|
|
prop.d_Lj[0] = sa;
|
|
|
|
wq = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!wq) {
|
|
|
|
q = pfl[i+2] - (qc * sb + prop.theta_ej[1]) * sb - zb;
|
|
|
|
|
|
|
|
if (q > 0.0) {
|
|
|
|
prop.theta_ej[1] += q / sb;
|
|
|
|
prop.d_Lj[1] = sb;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :53: Linear least square fit, page 28
|
|
|
|
static
|
|
|
|
void zlsq1(double z[], const double &x1, const double &x2, double& z0, double& zn)
|
|
|
|
|
|
|
|
{
|
|
|
|
double xn, xa, xb, x, a, b;
|
|
|
|
int n, ja, jb;
|
|
|
|
|
|
|
|
xn = z[0];
|
|
|
|
xa = int(FORTRAN_DIM(x1 / z[1], 0.0));
|
|
|
|
xb = xn - int(FORTRAN_DIM(xn, x2 / z[1]));
|
|
|
|
|
|
|
|
if (xb <= xa) {
|
|
|
|
xa = FORTRAN_DIM(xa, 1.0);
|
|
|
|
xb = xn - FORTRAN_DIM(xn, xb + 1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
ja = (int)xa;
|
|
|
|
jb = (int)xb;
|
|
|
|
n = jb - ja;
|
|
|
|
xa = xb - xa;
|
|
|
|
x = -0.5 * xa;
|
|
|
|
xb += x;
|
|
|
|
a = 0.5 * (z[ja+2] + z[jb+2]);
|
|
|
|
b = 0.5 * (z[ja+2] - z[jb+2]) * x;
|
|
|
|
|
|
|
|
for (int i = 2; i <= n; ++i) {
|
|
|
|
++ja;
|
|
|
|
x += 1.0;
|
|
|
|
a += z[ja+2];
|
|
|
|
b += z[ja+2] * x;
|
|
|
|
}
|
|
|
|
|
|
|
|
a /= xa;
|
|
|
|
b = b * 12.0 / ((xa * xa + 2.0) * xa);
|
|
|
|
z0 = a - b * xb;
|
|
|
|
zn = a + b * (xn - xb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :52: Provide a quantile and reorders array @a, page 27
|
|
|
|
static
|
|
|
|
double qtile(const int &nn, double a[], const int &ir)
|
|
|
|
{
|
|
|
|
double q = 0.0, r; /* Initializations by KD2BD */
|
|
|
|
int m, n, i, j, j1 = 0, i0 = 0, k; /* Initializations by KD2BD */
|
|
|
|
bool done = false;
|
|
|
|
bool goto10 = true;
|
|
|
|
|
|
|
|
m = 0;
|
|
|
|
n = nn;
|
|
|
|
k = mymin(mymax(0, ir), n);
|
|
|
|
|
|
|
|
while (!done) {
|
|
|
|
if (goto10) {
|
|
|
|
q = a[k];
|
|
|
|
i0 = m;
|
|
|
|
j1 = n;
|
|
|
|
}
|
|
|
|
|
|
|
|
i = i0;
|
|
|
|
|
|
|
|
while (i <= n && a[i] >= q)
|
|
|
|
i++;
|
|
|
|
|
|
|
|
if (i > n)
|
|
|
|
i = n;
|
|
|
|
j = j1;
|
|
|
|
|
|
|
|
while (j >= m && a[j] <= q)
|
|
|
|
j--;
|
|
|
|
|
|
|
|
if (j < m)
|
|
|
|
j = m;
|
|
|
|
|
|
|
|
if (i < j) {
|
|
|
|
r = a[i];
|
|
|
|
a[i] = a[j];
|
|
|
|
a[j] = r;
|
|
|
|
i0 = i + 1;
|
|
|
|
j1 = j - 1;
|
|
|
|
goto10 = false;
|
|
|
|
} else
|
|
|
|
if (i < k) {
|
|
|
|
a[k] = a[i];
|
|
|
|
a[i] = q;
|
|
|
|
m = i + 1;
|
|
|
|
goto10 = true;
|
|
|
|
} else
|
|
|
|
if (j > k) {
|
|
|
|
a[k] = a[j];
|
|
|
|
a[j] = q;
|
|
|
|
n = j - 1;
|
|
|
|
goto10 = true;
|
|
|
|
} else {
|
|
|
|
done = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return q;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_QERF
|
|
|
|
// :50: Standard normal complementary probability, page 26
|
|
|
|
static
|
|
|
|
double qerf(const double &z)
|
|
|
|
{
|
|
|
|
const double b1 = 0.319381530, b2 = -0.356563782, b3 = 1.781477937;
|
|
|
|
const double b4 = -1.821255987, b5 = 1.330274429;
|
|
|
|
const double rp = 4.317008, rrt2pi = 0.398942280;
|
|
|
|
double t, x, qerfv;
|
|
|
|
|
|
|
|
x = z;
|
|
|
|
t = fabs(x);
|
|
|
|
|
|
|
|
if (t >= 10.0)
|
|
|
|
qerfv = 0.0;
|
|
|
|
else {
|
|
|
|
t = rp / (t + rp);
|
|
|
|
qerfv = exp(-0.5 * x * x) * rrt2pi * ((((b5 * t + b4) * t + b3) * t + b2) * t + b1) * t;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (x < 0.0)
|
|
|
|
qerfv = 1.0 - qerfv;
|
|
|
|
|
|
|
|
return qerfv;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// :48: Find interdecile range of elevations, page 25
|
|
|
|
/*
|
|
|
|
* Using the terrain profile @pfl we find \Delta h, the interdecile range of
|
|
|
|
* elevations between the two points @x1 and @x2.
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
double dlthx(double pfl[], const double &x1, const double &x2)
|
|
|
|
{
|
|
|
|
int np, ka, kb, n, k, j;
|
|
|
|
double dlthxv, sn, xa, xb;
|
|
|
|
double *s;
|
|
|
|
|
|
|
|
np = (int)pfl[0];
|
|
|
|
xa = x1 / pfl[1];
|
|
|
|
xb = x2 / pfl[1];
|
|
|
|
dlthxv = 0.0;
|
|
|
|
|
|
|
|
if (xb - xa < 2.0) // exit out
|
|
|
|
return dlthxv;
|
|
|
|
|
|
|
|
ka = (int)(0.1 * (xb - xa + 8.0));
|
|
|
|
ka = mymin(mymax(4, ka), 25);
|
|
|
|
n = 10 * ka - 5;
|
|
|
|
kb = n - ka + 1;
|
|
|
|
sn = n - 1;
|
|
|
|
assert((s = new double[n+2]) != 0);
|
|
|
|
s[0] = sn;
|
|
|
|
s[1] = 1.0;
|
|
|
|
xb = (xb - xa) / sn;
|
|
|
|
k = (int)(xa + 1.0);
|
|
|
|
xa -= (double)k;
|
|
|
|
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
// Reduce
|
|
|
|
while (xa > 0.0 && k < np) {
|
|
|
|
xa -= 1.0;
|
|
|
|
++k;
|
|
|
|
}
|
|
|
|
|
|
|
|
s[j+2] = pfl[k+2] + (pfl[k+2] - pfl[k+1]) * xa;
|
|
|
|
xa = xa + xb;
|
|
|
|
}
|
|
|
|
|
|
|
|
zlsq1(s, 0.0, sn, xa, xb);
|
|
|
|
xb = (xb - xa) / sn;
|
|
|
|
|
|
|
|
for (j = 0; j < n; j++) {
|
|
|
|
s[j+2] -= xa;
|
|
|
|
xa = xa + xb;
|
|
|
|
}
|
|
|
|
|
|
|
|
dlthxv = qtile(n - 1, s + 2, ka - 1) - qtile(n - 1, s + 2, kb - 1);
|
|
|
|
dlthxv /= 1.0 - 0.8 * exp(-(x2 - x1) / 50.0e3);
|
|
|
|
delete[] s;
|
|
|
|
|
|
|
|
return dlthxv;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// :41: Prepare model for point-to-point operation, page 22
|
|
|
|
/*
|
|
|
|
* This mode requires the terrain profile lying between the terminals. This
|
|
|
|
* should be a sequence of surface elevations at points along the great
|
|
|
|
* circle path joining the two points. It should start at the ground beneath
|
|
|
|
* the first terminal and end at the ground beneath the second. In the
|
|
|
|
* present routine it is assumed that the elevations are equispaced along
|
|
|
|
* the path. They are stored in the array @pfl along with two defining
|
|
|
|
* parameters.
|
|
|
|
*
|
|
|
|
* We will have
|
|
|
|
* pfl[0] = np, the number of points in the path
|
|
|
|
* pfl[1] = xi, the length of each increment
|
|
|
|
*/
|
|
|
|
static
|
|
|
|
void qlrpfl(double pfl[], int klimx, int mdvarx, prop_type &prop, propv_type &propv)
|
|
|
|
{
|
|
|
|
int np, j;
|
|
|
|
double xl[2], q, za, zb;
|
|
|
|
|
|
|
|
prop.d = pfl[0] * pfl[1];
|
|
|
|
np = (int)pfl[0];
|
|
|
|
|
|
|
|
// :44: determine horizons and dh from pfl, page 23
|
|
|
|
hzns(pfl, prop);
|
|
|
|
for (j = 0; j < 2; j++)
|
|
|
|
xl[j] = mymin(15.0 * prop.h_g[j], 0.1 * prop.d_Lj[j]);
|
|
|
|
|
|
|
|
xl[1] = prop.d - xl[1];
|
|
|
|
prop.delta_h = dlthx(pfl, xl[0], xl[1]);
|
|
|
|
|
|
|
|
if (prop.d_Lj[0] + prop.d_Lj[1] > 1.5 * prop.d) {
|
|
|
|
// :45: Redo line-of-sight horizons, page 23
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the path is line-of-sight, we still need to know where
|
|
|
|
* the horizons might have been, and so we turn to
|
|
|
|
* techniques used in area prediction mode.
|
|
|
|
*/
|
|
|
|
zlsq1(pfl, xl[0], xl[1], za, zb);
|
|
|
|
prop.h_e[0] = prop.h_g[0] + FORTRAN_DIM(pfl[2], za);
|
|
|
|
prop.h_e[1] = prop.h_g[1] + FORTRAN_DIM(pfl[np+2], zb);
|
|
|
|
|
|
|
|
for (j = 0; j < 2; j++)
|
|
|
|
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)));
|
|
|
|
|
|
|
|
q = prop.d_Lj[0] + prop.d_Lj[1];
|
|
|
|
|
|
|
|
if (q <= prop.d) {
|
|
|
|
q = ((prop.d / q) * (prop.d / q));
|
|
|
|
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
prop.h_e[j] *= q;
|
|
|
|
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)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (j = 0; j < 2; j++) {
|
|
|
|
q = sqrt(2.0 * prop.h_e[j] / prop.gamma_e);
|
|
|
|
prop.theta_ej[j] = (0.65 * prop.delta_h * (q / prop.d_Lj[j] - 1.0) - 2.0 * prop.h_e[j]) / q;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// :46: Transhorizon effective heights, page 23
|
|
|
|
zlsq1(pfl, xl[0], 0.9 * prop.d_Lj[0], za, q);
|
|
|
|
zlsq1(pfl, prop.d - 0.9 * prop.d_Lj[1], xl[1], q, zb);
|
|
|
|
prop.h_e[0] = prop.h_g[0] + FORTRAN_DIM(pfl[2], za);
|
|
|
|
prop.h_e[1] = prop.h_g[1] + FORTRAN_DIM(pfl[np+2], zb);
|
|
|
|
}
|
|
|
|
|
|
|
|
prop.mdp = -1;
|
|
|
|
propv.lvar = mymax(propv.lvar, 3);
|
|
|
|
|
|
|
|
if (mdvarx >= 0) {
|
|
|
|
propv.mdvar = mdvarx;
|
|
|
|
propv.lvar = mymax(propv.lvar, 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (klimx > 0) {
|
|
|
|
propv.klim = klimx;
|
|
|
|
propv.lvar = 5;
|
|
|
|
}
|
|
|
|
|
|
|
|
lrprop(0.0, prop);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//********************************************************
|
|
|
|
//* Point-To-Point Mode Calculations *
|
|
|
|
//********************************************************
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_POINT_TO_POINT
|
|
|
|
#include <string.h>
|
|
|
|
static
|
|
|
|
void point_to_point(double elev[],
|
|
|
|
double tht_m, // Transceiver above ground level
|
|
|
|
double rht_m, // Receiver above groud level
|
|
|
|
double eps_dielect, // Earth dielectric constant (rel. permittivity)
|
|
|
|
double sgm_conductivity, // Earth conductivity (Siemens/m)
|
|
|
|
double eno, // Atmospheric bending const, n-Units
|
|
|
|
double frq_mhz,
|
|
|
|
int radio_climate, // 1..7
|
|
|
|
int pol, // 0 horizontal, 1 vertical
|
|
|
|
double conf, // 0.01 .. .99, Fractions of situations
|
|
|
|
double rel, // 0.01 .. .99, Fractions of time
|
|
|
|
double &dbloss,
|
|
|
|
char *strmode,
|
2011-11-26 18:51:33 +02:00
|
|
|
int &p_mode, // propagation mode selector
|
|
|
|
double (&horizons)[2], // horizon distances
|
2011-09-04 09:18:13 +03:00
|
|
|
int &errnum)
|
|
|
|
{
|
|
|
|
// radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
|
|
|
|
// 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
|
|
|
|
// 7-Maritime Temperate, Over Sea
|
|
|
|
// elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
|
|
|
|
// errnum: 0- No Error.
|
|
|
|
// 1- Warning: Some parameters are nearly out of range.
|
|
|
|
// Results should be used with caution.
|
|
|
|
// 2- Note: Default parameters have been substituted for impossible ones.
|
|
|
|
// 3- Warning: A combination of parameters is out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
// Other- Warning: Some parameters are out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
|
|
|
|
prop_type prop;
|
|
|
|
propv_type propv;
|
|
|
|
|
|
|
|
double zsys = 0;
|
|
|
|
double zc, zr;
|
|
|
|
double q = eno;
|
|
|
|
long ja, jb, i, np;
|
|
|
|
double dkm, xkm;
|
|
|
|
double fs;
|
|
|
|
|
|
|
|
prop.h_g[0] = tht_m; // Tx height above ground level
|
|
|
|
prop.h_g[1] = rht_m; // Rx height above ground level
|
|
|
|
propv.klim = radio_climate;
|
|
|
|
prop.kwx = 0; // no error yet
|
|
|
|
propv.lvar = 5; // initialize all constants
|
|
|
|
prop.mdp = -1; // point-to-point
|
|
|
|
zc = qerfi(conf);
|
|
|
|
zr = qerfi(rel);
|
|
|
|
np = (long)elev[0];
|
|
|
|
dkm = (elev[1] * elev[0]) / 1000.0;
|
|
|
|
xkm = elev[1] / 1000.0;
|
|
|
|
|
|
|
|
ja = (long)(3.0 + 0.1 * elev[0]);
|
|
|
|
jb = np - ja + 6;
|
|
|
|
for (i = ja - 1; i < jb; ++i)
|
|
|
|
zsys += elev[i];
|
|
|
|
zsys /= (jb - ja + 1);
|
|
|
|
|
|
|
|
propv.mdvar = 12;
|
|
|
|
qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
|
|
|
|
qlrpfl(elev, propv.klim, propv.mdvar, prop, propv);
|
|
|
|
fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
|
|
|
|
q = prop.d - prop.d_L;
|
|
|
|
|
2011-11-26 18:51:33 +02:00
|
|
|
horizons[0] = 0.0;
|
|
|
|
horizons[1] = 0.0;
|
2011-09-04 09:18:13 +03:00
|
|
|
if (int(q) < 0.0) {
|
|
|
|
strcpy(strmode, "Line-Of-Sight Mode");
|
2011-11-26 18:51:33 +02:00
|
|
|
p_mode = 0;
|
2011-09-04 09:18:13 +03:00
|
|
|
} else {
|
2011-11-26 18:51:33 +02:00
|
|
|
if (int(q) == 0.0) {
|
2011-09-04 09:18:13 +03:00
|
|
|
strcpy(strmode, "Single Horizon");
|
2011-11-26 18:51:33 +02:00
|
|
|
horizons[0] = prop.d_Lj[0];
|
|
|
|
p_mode = 1;
|
|
|
|
}
|
2011-09-04 09:18:13 +03:00
|
|
|
|
2011-11-26 18:51:33 +02:00
|
|
|
else {
|
|
|
|
if (int(q) > 0.0) {
|
2011-09-04 09:18:13 +03:00
|
|
|
strcpy(strmode, "Double Horizon");
|
2011-11-26 18:51:33 +02:00
|
|
|
horizons[0] = prop.d_Lj[0];
|
|
|
|
horizons[1] = prop.d_Lj[1];
|
|
|
|
p_mode = 1;
|
|
|
|
}
|
|
|
|
}
|
2011-09-04 09:18:13 +03:00
|
|
|
|
2011-11-26 18:51:33 +02:00
|
|
|
if (prop.d <= prop.d_Ls || prop.d <= prop.dx) {
|
2011-09-04 09:18:13 +03:00
|
|
|
strcat(strmode, ", Diffraction Dominant");
|
2011-11-26 18:51:33 +02:00
|
|
|
p_mode = 1;
|
|
|
|
}
|
2011-09-04 09:18:13 +03:00
|
|
|
|
2011-11-26 18:51:33 +02:00
|
|
|
else {
|
|
|
|
if (prop.d > prop.dx) {
|
|
|
|
strcat(strmode, ", Troposcatter Dominant");
|
|
|
|
p_mode = 2;
|
|
|
|
}
|
|
|
|
}
|
2011-09-04 09:18:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
dbloss = avar(zr, 0.0, zc, prop, propv) + fs;
|
|
|
|
errnum = prop.kwx;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_POINT_TO_POINT_MDH
|
|
|
|
static
|
|
|
|
void 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)
|
|
|
|
{
|
|
|
|
// pol: 0-Horizontal, 1-Vertical
|
|
|
|
// radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
|
|
|
|
// 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
|
|
|
|
// 7-Maritime Temperate, Over Sea
|
|
|
|
// timepct, locpct, confpct: .01 to .99
|
|
|
|
// elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
|
|
|
|
// propmode: Value Mode
|
|
|
|
// -1 mode is undefined
|
|
|
|
// 0 Line of Sight
|
|
|
|
// 5 Single Horizon, Diffraction
|
|
|
|
// 6 Single Horizon, Troposcatter
|
|
|
|
// 9 Double Horizon, Diffraction
|
|
|
|
// 10 Double Horizon, Troposcatter
|
|
|
|
// errnum: 0- No Error.
|
|
|
|
// 1- Warning: Some parameters are nearly out of range.
|
|
|
|
// Results should be used with caution.
|
|
|
|
// 2- Note: Default parameters have been substituted for impossible ones.
|
|
|
|
// 3- Warning: A combination of parameters is out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
// Other- Warning: Some parameters are out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
|
|
|
|
prop_type prop;
|
|
|
|
propv_type propv;
|
|
|
|
propa_type propa;
|
|
|
|
double zsys = 0;
|
|
|
|
double ztime, zloc, zconf;
|
|
|
|
double q = eno;
|
|
|
|
long ja, jb, i, np;
|
|
|
|
double dkm, xkm;
|
|
|
|
double fs;
|
|
|
|
|
|
|
|
propmode = -1; // mode is undefined
|
|
|
|
prop.h_g[0] = tht_m;
|
|
|
|
prop.h_g[1] = rht_m;
|
|
|
|
propv.klim = radio_climate;
|
|
|
|
prop.kwx = 0;
|
|
|
|
propv.lvar = 5;
|
|
|
|
prop.mdp = -1;
|
|
|
|
ztime = qerfi(timepct);
|
|
|
|
zloc = qerfi(locpct);
|
|
|
|
zconf = qerfi(confpct);
|
|
|
|
|
|
|
|
np = (long)elev[0];
|
|
|
|
dkm = (elev[1] * elev[0]) / 1000.0;
|
|
|
|
xkm = elev[1] / 1000.0;
|
|
|
|
|
|
|
|
ja = (long)(3.0 + 0.1 * elev[0]);
|
|
|
|
jb = np - ja + 6;
|
|
|
|
for (i = ja - 1; i < jb; ++i)
|
|
|
|
zsys += elev[i];
|
|
|
|
zsys /= (jb - ja + 1);
|
|
|
|
|
|
|
|
propv.mdvar = 12;
|
|
|
|
qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
|
|
|
|
qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv);
|
|
|
|
fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
|
|
|
|
deltaH = prop.delta_h;
|
|
|
|
q = prop.d - prop.d_L;
|
|
|
|
|
|
|
|
if (int(q) < 0.0) {
|
|
|
|
propmode = 0; // Line-Of-Sight Mode
|
|
|
|
} else {
|
|
|
|
if (int(q) == 0.0)
|
|
|
|
propmode = 4; // Single Horizon
|
|
|
|
else
|
|
|
|
if (int(q) > 0.0)
|
|
|
|
propmode = 8; // Double Horizon
|
|
|
|
|
|
|
|
if (prop.d <= prop.d_Ls || prop.d <= prop.dx)
|
|
|
|
propmode += 1; // Diffraction Dominant
|
|
|
|
|
|
|
|
else
|
|
|
|
if (prop.d > prop.dx)
|
|
|
|
propmode += 2; // Troposcatter Dominant
|
|
|
|
}
|
|
|
|
|
|
|
|
dbloss = avar(ztime, zloc, zconf, prop, propv) + fs; //avar(time,location,confidence)
|
|
|
|
errnum = prop.kwx;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_POINT_TO_POINT_DH
|
|
|
|
static
|
|
|
|
void 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)
|
|
|
|
{
|
|
|
|
// pol: 0-Horizontal, 1-Vertical
|
|
|
|
// radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
|
|
|
|
// 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
|
|
|
|
// 7-Maritime Temperate, Over Sea
|
|
|
|
// conf, rel: .01 to .99
|
|
|
|
// elev[]: [num points - 1], [delta dist(meters)], [height(meters) point 1], ..., [height(meters) point n]
|
|
|
|
// errnum: 0- No Error.
|
|
|
|
// 1- Warning: Some parameters are nearly out of range.
|
|
|
|
// Results should be used with caution.
|
|
|
|
// 2- Note: Default parameters have been substituted for impossible ones.
|
|
|
|
// 3- Warning: A combination of parameters is out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
// Other- Warning: Some parameters are out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
|
|
|
|
char strmode[100];
|
|
|
|
prop_type prop;
|
|
|
|
propv_type propv;
|
|
|
|
propa_type propa;
|
|
|
|
double zsys = 0;
|
|
|
|
double zc, zr;
|
|
|
|
double q = eno;
|
|
|
|
long ja, jb, i, np;
|
|
|
|
double dkm, xkm;
|
|
|
|
double fs;
|
|
|
|
|
|
|
|
prop.h_g[0] = tht_m;
|
|
|
|
prop.h_g[1] = rht_m;
|
|
|
|
propv.klim = radio_climate;
|
|
|
|
prop.kwx = 0;
|
|
|
|
propv.lvar = 5;
|
|
|
|
prop.mdp = -1;
|
|
|
|
zc = qerfi(conf);
|
|
|
|
zr = qerfi(rel);
|
|
|
|
np = (long)elev[0];
|
|
|
|
dkm = (elev[1] * elev[0]) / 1000.0;
|
|
|
|
xkm = elev[1] / 1000.0;
|
|
|
|
|
|
|
|
ja = (long)(3.0 + 0.1 * elev[0]);
|
|
|
|
jb = np - ja + 6;
|
|
|
|
for (i = ja - 1; i < jb; ++i)
|
|
|
|
zsys += elev[i];
|
|
|
|
zsys /= (jb - ja + 1);
|
|
|
|
|
|
|
|
propv.mdvar = 12;
|
|
|
|
qlrps(frq_mhz, zsys, q, pol, eps_dielect, sgm_conductivity, prop);
|
|
|
|
qlrpfl(elev, propv.klim, propv.mdvar, prop, propa, propv);
|
|
|
|
fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
|
|
|
|
deltaH = prop.delta_h;
|
|
|
|
q = prop.d - prop.d_L;
|
|
|
|
|
|
|
|
if (int(q) < 0.0)
|
|
|
|
strcpy(strmode, "Line-Of-Sight Mode");
|
|
|
|
else {
|
|
|
|
if (int(q) == 0.0)
|
|
|
|
strcpy(strmode, "Single Horizon");
|
|
|
|
|
|
|
|
else
|
|
|
|
if (int(q) > 0.0)
|
|
|
|
strcpy(strmode, "Double Horizon");
|
|
|
|
|
|
|
|
if (prop.d <= prop.d_Ls || prop.d <= prop.dx)
|
|
|
|
strcat(strmode, ", Diffraction Dominant");
|
|
|
|
|
|
|
|
else
|
|
|
|
if (prop.d > prop.dx)
|
|
|
|
strcat(strmode, ", Troposcatter Dominant");
|
|
|
|
}
|
|
|
|
|
|
|
|
dbloss = avar(zr, 0.0, zc, prop, propv) + fs; //avar(time,location,confidence)
|
|
|
|
errnum = prop.kwx;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//********************************************************
|
|
|
|
//* Area Mode Calculations *
|
|
|
|
//********************************************************
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef WITH_AREA
|
|
|
|
static
|
|
|
|
void 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)
|
|
|
|
{
|
|
|
|
// pol: 0-Horizontal, 1-Vertical
|
|
|
|
// TSiteCriteria, RSiteCriteria:
|
|
|
|
// 0 - random, 1 - careful, 2 - very careful
|
|
|
|
// radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical,
|
|
|
|
// 4-Desert, 5-Continental Temperate, 6-Maritime Temperate, Over Land,
|
|
|
|
// 7-Maritime Temperate, Over Sea
|
|
|
|
// ModVar: 0 - Single: pctConf is "Time/Situation/Location", pctTime, pctLoc not used
|
|
|
|
// 1 - Individual: pctTime is "Situation/Location", pctConf is "Confidence", pctLoc not used
|
|
|
|
// 2 - Mobile: pctTime is "Time/Locations (Reliability)", pctConf is "Confidence", pctLoc not used
|
|
|
|
// 3 - Broadcast: pctTime is "Time", pctLoc is "Location", pctConf is "Confidence"
|
|
|
|
// pctTime, pctLoc, pctConf: .01 to .99
|
|
|
|
// errnum: 0- No Error.
|
|
|
|
// 1- Warning: Some parameters are nearly out of range.
|
|
|
|
// Results should be used with caution.
|
|
|
|
// 2- Note: Default parameters have been substituted for impossible ones.
|
|
|
|
// 3- Warning: A combination of parameters is out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
// Other- Warning: Some parameters are out of range.
|
|
|
|
// Results are probably invalid.
|
|
|
|
// NOTE: strmode is not used at this time.
|
|
|
|
|
|
|
|
prop_type prop;
|
|
|
|
propv_type propv;
|
|
|
|
propa_type propa;
|
|
|
|
double zt, zl, zc, xlb;
|
|
|
|
double fs;
|
|
|
|
long ivar;
|
|
|
|
double eps, sgm;
|
|
|
|
long ipol;
|
|
|
|
int kst[2];
|
|
|
|
|
|
|
|
kst[0] = (int)TSiteCriteria;
|
|
|
|
kst[1] = (int)RSiteCriteria;
|
|
|
|
zt = qerfi(pctTime);
|
|
|
|
zl = qerfi(pctLoc);
|
|
|
|
zc = qerfi(pctConf);
|
|
|
|
eps = eps_dielect;
|
|
|
|
sgm = sgm_conductivity;
|
|
|
|
prop.delta_h = deltaH;
|
|
|
|
prop.h_g[0] = tht_m;
|
|
|
|
prop.h_g[1] = rht_m;
|
|
|
|
propv.klim = (long)radio_climate;
|
|
|
|
prop.N_s = eno;
|
|
|
|
prop.kwx = 0;
|
|
|
|
ivar = (long)ModVar;
|
|
|
|
ipol = (long)pol;
|
|
|
|
qlrps(frq_mhz, 0.0, eno, ipol, eps, sgm, prop);
|
|
|
|
qlra(kst, propv.klim, ivar, prop, propv);
|
|
|
|
|
|
|
|
if (propv.lvar < 1)
|
|
|
|
propv.lvar = 1;
|
|
|
|
|
|
|
|
lrprop(dist_km * 1000.0, prop, propa);
|
|
|
|
fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0);
|
|
|
|
xlb = fs + avar(zt, zl, zc, prop, propv);
|
|
|
|
dbloss = xlb;
|
|
|
|
|
|
|
|
if (prop.kwx == 0)
|
|
|
|
errnum = 0;
|
|
|
|
else
|
|
|
|
errnum = prop.kwx;
|
|
|
|
}
|
|
|
|
#endif
|