From c9ef773d98ff784a2d169f13236ee5a4beebb552 Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 4 Sep 2011 09:18:13 +0300 Subject: [PATCH 01/47] compute radio transmission attenuation according to the Longley-Rice Irregular Terrain Model propagation mode. --- src/ATC/CMakeLists.txt | 1 + src/ATC/itm.cpp | 1833 ++++++++++++++++++++++++++++++++++++ src/ATC/trafficcontrol.cxx | 135 ++- src/ATC/trafficcontrol.hxx | 2 +- 4 files changed, 1968 insertions(+), 3 deletions(-) create mode 100644 src/ATC/itm.cpp diff --git a/src/ATC/CMakeLists.txt b/src/ATC/CMakeLists.txt index e055ec6dd..26ccaadbf 100644 --- a/src/ATC/CMakeLists.txt +++ b/src/ATC/CMakeLists.txt @@ -5,6 +5,7 @@ set(SOURCES atcdialog.cxx trafficcontrol.cxx CommStation.cxx + itm.cpp ) flightgear_component(ATC "${SOURCES}") diff --git a/src/ATC/itm.cpp b/src/ATC/itm.cpp new file mode 100644 index 000000000..20862355b --- /dev/null +++ b/src/ATC/itm.cpp @@ -0,0 +1,1833 @@ +/*****************************************************************************\ + * * + * 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 +#include +#include + + +const float THIRD = (1.0/3.0); +const float f_0 = 47.7; // 47.7 MHz from [Alg 1.1], to convert frequency into wavenumber and vica versa + + +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; +} + +#define set_warn(txt, err) printf("%s:%d %s\n", __func__, __LINE__, txt); + + +// :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 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 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 prop_zgnd(prop.Z_g_real, prop.Z_g_imag); + complex 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(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 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 zq, prop_zgnd(prop.Z_g_real, prop.Z_g_imag); + zq = complex (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 +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, + 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; + + 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; + 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 diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 47f5acc8a..02d5a5c81 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -25,6 +25,8 @@ #endif #include +#include +#include #include #include @@ -48,6 +50,8 @@ #include #include +#include "itm.cpp" + using std::sort; /*************************************************************************** @@ -496,7 +500,7 @@ bool FGATCController::isUserAircraft(FGAIAircraft* ac) return (ac->getCallSign() == fgGetString("/sim/multiplay/callsign")) ? true : false; }; -void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId, +void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible) { string sender, receiver; @@ -724,8 +728,18 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId, // Display ATC message only when one of the radios is tuned // the relevant frequency. // Note that distance attenuation is currently not yet implemented + if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) { + double snr = calculate_attenuation(rec, parent, msgDir); + if (snr <= 0) + return; + if (snr > 0 && snr < 10) { + //for low snr values implement a way to make the conversation + //hard to understand (perhaps eliminate letters from words or such + return; + } + if (rec->allowTransmissions()) { fgSetString("/sim/messages/atc", text.c_str()); } @@ -737,6 +751,123 @@ void FGATCController::transmit(FGTrafficRecord * rec, AtcMsgId msgId, } } +int calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, + AtcMsgDir msgDir) { + ///////////////////////////////////////////////// + /// Implement radio attenuation + /// based on the Longley-Rice propagation model + ///////////////////////////////////////////////// + + FGScenery * scenery = globals->get_scenery(); + // player aircraft position + double own_lat = fgGetDouble("/position/latitude-deg"); + double own_lon = fgGetDouble("/position/longitude-deg"); + double own_alt_ft = fgGetDouble("/position/altitude-ft"); + double own_alt= own_alt_ft * SG_FEET_TO_METER; + + SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); + SGGeod max_own_pos = SGGeod::fromDegM( own_lon, own_lat, SG_MAX_ELEVATION_M ); + SGGeoc center = SGGeoc::fromGeod( max_own_pos ); + + // position of sender + // sender can be aircraft or ground station + double sender_alt_ft,sender_alt; + SGGeod sender_pos; + if(msgDir == ATC_GROUND_TO_AIR) { + sender_alt_ft = parent->getElevation(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_pos= SGGeod::fromDegM( parent->getLongitude(), parent->getLatitude(), sender_alt ); + } + else { + sender_alt_ft = rec->getAltitude(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_pos= SGGeod::fromDegM( rec->getLongitude(), rec->getLatitude(), sender_alt ); + } + double point_distance= 100.0; // regular SRTM is 90 meters + double course = SGGeodesy::courseDeg(own_pos, sender_pos); + double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); + double max_points = distance_m / point_distance; + deque _elevations; + + // If distance larger than this value (400 km), assume reception imposssible + // technically 400 km is no problem if LOS conditions exist, + // but we do this to spare resources + if (distance_m > 400000) + return -1; + + while (_elevations.size() < (deque::size_type)max_points) { + SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, point_distance )); + + double elevation_m = 0.0; + + if (scenery->get_elevation_m( probe, elevation_m, NULL )) { + _elevations.push_front(elevation_m); + } + } + + /* + double max_alt_between=0.0; + for( deque::size_type i = 0; i < _elevations.size(); i++ ) { + if (_elevations[i] > max_alt_between) { + max_alt_between = _elevations[i]; + } + } + */ + + double num_points= (int)_elevations.size(); + _elevations.push_front(distance_m); + _elevations.push_front(num_points -1); + int size= _elevations.size(); + double itm_elev[]; + for(int i=0;igetATCDialog()->removeEntry(1); } else { //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl; - transmit(&(*i), msgId, msgDir, false); + transmit(&(*i), parent, msgId, msgDir, false); return false; } } diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 6aae0ab9e..d915fa264 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -300,7 +300,7 @@ public: double getDt() { return dt_count; }; void setDt(double dt) { dt_count = dt;}; - void transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); + void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); string getGateName(FGAIAircraft *aircraft); virtual void render(bool) = 0; virtual string getName() = 0; From 4801b28c425908292f6f1eda2d23cf71f0e56780 Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 4 Sep 2011 10:00:36 +0300 Subject: [PATCH 02/47] fix + define WITH_POINT_TO_POINT --- src/ATC/trafficcontrol.cxx | 32 ++++++++++++++++++-------------- src/ATC/trafficcontrol.hxx | 2 +- src/Airports/groundnetwork.cxx | 8 ++++---- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 02d5a5c81..90eba53a9 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -49,7 +49,7 @@ #include #include #include - +#define WITH_POINT_TO_POINT #include "itm.cpp" using std::sort; @@ -521,6 +521,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, FGAIFlightPlan *fp; string fltRules; string instructionText; + int ground_to_air=0; //double commFreqD; sender = rec->getAircraft()->getTrafficRef()->getCallSign(); @@ -561,6 +562,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, string tmp = sender; sender = receiver; receiver = tmp; + ground_to_air=1; } switch (msgId) { case MSG_ANNOUNCE_ENGINE_START: @@ -731,7 +733,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) { - double snr = calculate_attenuation(rec, parent, msgDir); + double snr = calculate_attenuation(rec, parent, ground_to_air); if (snr <= 0) return; if (snr > 0 && snr < 10) { @@ -751,8 +753,8 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, } } -int calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, - AtcMsgDir msgDir) { +int FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, + int ground_to_air) { ///////////////////////////////////////////////// /// Implement radio attenuation /// based on the Longley-Rice propagation model @@ -773,15 +775,17 @@ int calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, // sender can be aircraft or ground station double sender_alt_ft,sender_alt; SGGeod sender_pos; - if(msgDir == ATC_GROUND_TO_AIR) { + if(ground_to_air) { sender_alt_ft = parent->getElevation(); sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( parent->getLongitude(), parent->getLatitude(), sender_alt ); + sender_pos= SGGeod::fromDegM( parent->getLongitude(), + parent->getLatitude(), sender_alt ); } else { sender_alt_ft = rec->getAltitude(); sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( rec->getLongitude(), rec->getLatitude(), sender_alt ); + sender_pos= SGGeod::fromDegM( rec->getLongitude(), + rec->getLatitude(), sender_alt ); } double point_distance= 100.0; // regular SRTM is 90 meters double course = SGGeodesy::courseDeg(own_pos, sender_pos); @@ -818,7 +822,7 @@ int calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, _elevations.push_front(distance_m); _elevations.push_front(num_points -1); int size= _elevations.size(); - double itm_elev[]; + double itm_elev[size]; for(int i=0;igetATCDialog()->removeEntry(1); } else { //cerr << "creading message for " << i->getAircraft()->getCallSign() << endl; - transmit(&(*i), parent, msgId, msgDir, false); + transmit(&(*i), &(*parent), msgId, msgDir, false); return false; } } if (now > startTime) { //cerr << "Transmitting startup msg" << endl; - transmit(&(*i), msgId, msgDir, true); + transmit(&(*i), &(*parent), msgId, msgDir, true); i->updateState(); lastTransmission = now; available = false; @@ -1361,11 +1365,11 @@ void FGStartupController::updateAircraftInformation(int id, double lat, double l if (now > startTime + 200) { if (i->pushBackAllowed()) { i->allowRepeatedTransmissions(); - transmit(&(*i), MSG_PERMIT_PUSHBACK_CLEARANCE, + transmit(&(*i), &(*parent), MSG_PERMIT_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR, true); i->updateState(); } else { - transmit(&(*i), MSG_HOLD_PUSHBACK_CLEARANCE, + transmit(&(*i), &(*parent), MSG_HOLD_PUSHBACK_CLEARANCE, ATC_GROUND_TO_AIR, true); i->suppressRepeatedTransmissions(); } diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index d915fa264..d6a59384c 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -304,7 +304,7 @@ public: string getGateName(FGAIAircraft *aircraft); virtual void render(bool) = 0; virtual string getName() = 0; - + int calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, int ground_to_air); private: diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 86beec118..7bf1eac2e 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -674,11 +674,11 @@ bool FGGroundNetwork::checkTransmissionState(int minState, int maxState, Traffic atc->getATCDialog()->removeEntry(1); } else { //cerr << "creating message for " << i->getAircraft()->getCallSign() << endl; - transmit(&(*i), msgId, msgDir, false); + transmit(&(*i), &(*parent->getDynamics()), msgId, msgDir, false); return false; } } - transmit(&(*i), msgId, msgDir, true); + transmit(&(*i), &(*parent->getDynamics()), msgId, msgDir, true); i->updateState(); lastTransmission = now; available = false; @@ -1033,11 +1033,11 @@ void FGGroundNetwork::checkHoldPosition(int id, double lat, if ((origStatus != currStatus) && available) { //cerr << "Issueing hold short instrudtion " << currStatus << " " << available << endl; if (currStatus == true) { // No has a hold short instruction - transmit(&(*current), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true); + transmit(&(*current), &(*parent->getDynamics()), MSG_HOLD_POSITION, ATC_GROUND_TO_AIR, true); //cerr << "Transmittin hold short instrudtion " << currStatus << " " << available << endl; current->setState(1); } else { - transmit(&(*current), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true); + transmit(&(*current), &(*parent->getDynamics()), MSG_RESUME_TAXI, ATC_GROUND_TO_AIR, true); //cerr << "Transmittig resume instrudtion " << currStatus << " " << available << endl; current->setState(2); } From 3a82ce76961d3dcee66d9c168c076d1c2f9a5141 Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 4 Sep 2011 13:56:03 +0300 Subject: [PATCH 03/47] functional radio signal attenuation --- src/ATC/trafficcontrol.cxx | 106 +++++++++++++++++++++++++++---------- src/ATC/trafficcontrol.hxx | 2 +- 2 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 90eba53a9..15520295a 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -26,6 +26,7 @@ #include #include +#include #include #include @@ -736,10 +737,17 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, double snr = calculate_attenuation(rec, parent, ground_to_air); if (snr <= 0) return; - if (snr > 0 && snr < 10) { - //for low snr values implement a way to make the conversation - //hard to understand (perhaps eliminate letters from words or such - return; + if (snr > 0 && snr < 12) { + //for low SNR values implement a way to make the conversation + //hard to understand but audible + string hash_noise = " "; + int reps = fabs((int)snr - 11); + int t_size = text.size(); + for (int n=1;n<=reps * 2;n++) { + int pos = rand() % t_size -1; + text.replace(pos,1, hash_noise); + } + } if (rec->allowTransmissions()) { @@ -753,12 +761,12 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, } } -int FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, +double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, int ground_to_air) { - ///////////////////////////////////////////////// - /// Implement radio attenuation - /// based on the Longley-Rice propagation model - ///////////////////////////////////////////////// + ////////////////////////////////////////////////// + /// Implement radio attenuation // + /// based on the Longley-Rice propagation model// + ////////////////////////////////////////////////// FGScenery * scenery = globals->get_scenery(); // player aircraft position @@ -767,64 +775,96 @@ int FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynam double own_alt_ft = fgGetDouble("/position/altitude-ft"); double own_alt= own_alt_ft * SG_FEET_TO_METER; + cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; + SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); SGGeod max_own_pos = SGGeod::fromDegM( own_lon, own_lat, SG_MAX_ELEVATION_M ); SGGeoc center = SGGeoc::fromGeod( max_own_pos ); + SGGeoc own_pos_c = SGGeoc::fromGeod( own_pos ); - // position of sender + // position of sender radio antenna (HAAT) // sender can be aircraft or ground station + double ATC_HAAT = 30.0; + double Aircraft_HAAT = 7.0; double sender_alt_ft,sender_alt; + double transceiver_height=0.0; + double receiver_height=0.0; SGGeod sender_pos; if(ground_to_air) { sender_alt_ft = parent->getElevation(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_alt = sender_alt_ft * SG_FEET_TO_METER + ATC_HAAT; sender_pos= SGGeod::fromDegM( parent->getLongitude(), parent->getLatitude(), sender_alt ); } else { sender_alt_ft = rec->getAltitude(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_alt = sender_alt_ft * SG_FEET_TO_METER + Aircraft_HAAT; sender_pos= SGGeod::fromDegM( rec->getLongitude(), rec->getLatitude(), sender_alt ); } - double point_distance= 100.0; // regular SRTM is 90 meters - double course = SGGeodesy::courseDeg(own_pos, sender_pos); + SGGeoc sender_pos_c = SGGeoc::fromGeod( sender_pos ); + cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; + + double point_distance= 90.0; // regular SRTM is 90 meters + double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); + double probe_distance = 0.0; + + cerr << "ITM:: Distance: " << distance_m << endl; + double max_points = distance_m / point_distance; deque _elevations; + SGGeod probe_pilot = SGGeod::fromGeoc(center.advanceRadM( course, 0 )); + double elevation_under_pilot = 0.0; + if (scenery->get_elevation_m( probe_pilot, elevation_under_pilot, NULL )) { + receiver_height = own_alt - elevation_under_pilot; + } + _elevations.push_front(receiver_height); + + SGGeod probe_sender = SGGeod::fromGeoc(center.advanceRadM( course, distance_m )); + double elevation_under_sender = 0.0; + if (scenery->get_elevation_m( probe_sender, elevation_under_sender, NULL )) { + transceiver_height = sender_alt - elevation_under_sender; + } + // If distance larger than this value (400 km), assume reception imposssible // technically 400 km is no problem if LOS conditions exist, // but we do this to spare resources if (distance_m > 400000) - return -1; + return -1.0; - while (_elevations.size() < (deque::size_type)max_points) { - SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, point_distance )); + int e_size = (deque::size_type)max_points; + + while (_elevations.size() < e_size) { + probe_distance += point_distance; + SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance )); double elevation_m = 0.0; if (scenery->get_elevation_m( probe, elevation_m, NULL )) { _elevations.push_front(elevation_m); + //cerr << "ITM:: Probe elev: " << elevation_m << endl; } } - /* + _elevations.push_front(transceiver_height); double max_alt_between=0.0; for( deque::size_type i = 0; i < _elevations.size(); i++ ) { if (_elevations[i] > max_alt_between) { max_alt_between = _elevations[i]; } } - */ - double num_points= (int)_elevations.size(); - _elevations.push_front(distance_m); + double num_points= (double)_elevations.size(); + cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; + _elevations.push_front(point_distance); _elevations.push_front(num_points -1); - int size= _elevations.size(); + int size = _elevations.size(); double itm_elev[size]; for(int i=0;i Date: Sun, 4 Sep 2011 14:20:41 +0300 Subject: [PATCH 04/47] check for allowTransmission --- src/ATC/trafficcontrol.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 15520295a..f49026f48 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -734,7 +734,9 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) { - double snr = calculate_attenuation(rec, parent, ground_to_air); + + if (rec->allowTransmissions()) { + double snr = calculate_attenuation(rec, parent, ground_to_air); if (snr <= 0) return; if (snr > 0 && snr < 12) { @@ -749,8 +751,6 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, } } - - if (rec->allowTransmissions()) { fgSetString("/sim/messages/atc", text.c_str()); } } From 135c544f6e6a95510743b33da8b790c9a74ef854 Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 4 Sep 2011 16:13:47 +0300 Subject: [PATCH 05/47] bugfix --- src/ATC/trafficcontrol.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index f49026f48..4a8fa7cde 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -834,7 +834,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy if (distance_m > 400000) return -1.0; - int e_size = (deque::size_type)max_points; + unsigned int e_size = (deque::size_type)max_points; while (_elevations.size() < e_size) { probe_distance += point_distance; @@ -888,14 +888,16 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy // !!! small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) // later store this value in aircraft description // ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now + double transmitter_power = 43.0; + double antenna_gain = 2.0; if(ground_to_air) - double transmitter_power = 49.0; + transmitter_power = 49.0; else - double transmitter_power = 43.0; + transmitter_power = 43.0; if(ground_to_air) - double antenna_gain = 5.0; //pilot plane's antenna gain + Controller antenna gain + antenna_gain = 5.0; //pilot plane's antenna gain + Controller antenna gain else - double antenna_gain = 2.0; //pilot plane's antenna gain + AI aircraft antenna gain + antenna_gain = 2.0; //pilot plane's antenna gain + AI aircraft antenna gain double link_budget = transmitter_power - receiver_sensitivity + antenna_gain; From 038251e8af6ff9c24afcc08169c2bcca2c5a63c5 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 5 Sep 2011 07:23:48 +0300 Subject: [PATCH 06/47] fix trx and rx heights and improve calculations --- src/ATC/trafficcontrol.cxx | 65 +++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 7526e66ff..055ec9735 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -742,6 +742,10 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, if (snr > 0 && snr < 12) { //for low SNR values implement a way to make the conversation //hard to understand but audible + //how this works in the real world, is the receiver AGC fails to capture the slope + //and the signal, due to being amplitude modulated, decreases volume after demodulation + //the implementation below is more akin to what would happen on a FM transmission + //therefore the correct way would be to work on the volume string hash_noise = " "; int reps = fabs((int)snr - 11); int t_size = text.size(); @@ -763,11 +767,10 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, int ground_to_air) { - ////////////////////////////////////////////////// - /// Implement radio attenuation // - /// based on the Longley-Rice propagation model// - ////////////////////////////////////////////////// - + + /// Implement radio attenuation + /// based on the Longley-Rice propagation model + FGScenery * scenery = globals->get_scenery(); // player aircraft position double own_lat = fgGetDouble("/position/latitude-deg"); @@ -775,7 +778,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy double own_alt_ft = fgGetDouble("/position/altitude-ft"); double own_alt= own_alt_ft * SG_FEET_TO_METER; - cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; + //cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); SGGeod max_own_pos = SGGeod::fromDegM( own_lon, own_lat, SG_MAX_ELEVATION_M ); @@ -785,49 +788,61 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy // position of sender radio antenna (HAAT) // sender can be aircraft or ground station double ATC_HAAT = 30.0; - double Aircraft_HAAT = 7.0; + double Aircraft_HAAT = 5.0; double sender_alt_ft,sender_alt; double transceiver_height=0.0; double receiver_height=0.0; SGGeod sender_pos; + SGGeod max_sender_pos; if(ground_to_air) { sender_alt_ft = parent->getElevation(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER + ATC_HAAT; + sender_alt = sender_alt_ft * SG_FEET_TO_METER; sender_pos= SGGeod::fromDegM( parent->getLongitude(), parent->getLatitude(), sender_alt ); + max_sender_pos = SGGeod::fromDegM( parent->getLongitude(), + parent->getLatitude(), SG_MAX_ELEVATION_M ); } else { sender_alt_ft = rec->getAltitude(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER + Aircraft_HAAT; + sender_alt = sender_alt_ft * SG_FEET_TO_METER; sender_pos= SGGeod::fromDegM( rec->getLongitude(), rec->getLatitude(), sender_alt ); + max_sender_pos = SGGeod::fromDegM( rec->getLongitude(), + rec->getLatitude(), SG_MAX_ELEVATION_M ); } SGGeoc sender_pos_c = SGGeoc::fromGeod( sender_pos ); - cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; + //cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; double point_distance= 90.0; // regular SRTM is 90 meters double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; - cerr << "ITM:: Distance: " << distance_m << endl; + //cerr << "ITM:: Distance: " << distance_m << endl; double max_points = distance_m / point_distance; deque _elevations; - SGGeod probe_pilot = SGGeod::fromGeoc(center.advanceRadM( course, 0 )); + //SGGeod probe_pilot = SGGeod::fromGeoc(center.advanceRadM( course, 0 )); + SGGeod probe_pilot = max_own_pos; double elevation_under_pilot = 0.0; if (scenery->get_elevation_m( probe_pilot, elevation_under_pilot, NULL )) { - receiver_height = own_alt - elevation_under_pilot; + receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground } - _elevations.push_front(receiver_height); - SGGeod probe_sender = SGGeod::fromGeoc(center.advanceRadM( course, distance_m )); + + //SGGeod probe_sender = SGGeod::fromGeoc(center.advanceRadM( course, distance_m )); + SGGeod probe_sender = max_sender_pos; double elevation_under_sender = 0.0; if (scenery->get_elevation_m( probe_sender, elevation_under_sender, NULL )) { - transceiver_height = sender_alt - elevation_under_sender; + transmitter_height = sender_alt - elevation_under_sender; } + if(ground_to_air) + transmitter_height += ATC_HAAT; + else + transmitter_height += Aircraft_HAAT; + cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << endl; // If distance larger than this value (400 km), assume reception imposssible // technically 400 km is no problem if LOS conditions exist, // but we do this to spare resources @@ -836,7 +851,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy unsigned int e_size = (deque::size_type)max_points; - while (_elevations.size() < e_size) { + while (_elevations.size() <= e_size) { probe_distance += point_distance; SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance )); @@ -847,8 +862,8 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy //cerr << "ITM:: Probe elev: " << elevation_m << endl; } } - - _elevations.push_front(transceiver_height); + _elevations.push_back(elevation_under_pilot); + _elevations.push_front(elevation_under_sender); double max_alt_between=0.0; for( deque::size_type i = 0; i < _elevations.size(); i++ ) { if (_elevations[i] > max_alt_between) { @@ -857,7 +872,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy } double num_points= (double)_elevations.size(); - cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; + //cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; _elevations.push_front(point_distance); _elevations.push_front(num_points -1); int size = _elevations.size(); @@ -874,7 +889,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy double eno = 301.0; double frq_mhz = 125.0; // middle of bandplan int radio_climate = 5; // continental temperate - int pol=1; // assuming vertical polarization + int pol=1; // assuming vertical polarization although this is more complex in reality double conf = 0.90; // my own tests in Radiomobile have worked best with these values double rel = 0.80; // ^^ double dbloss; @@ -882,20 +897,20 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy int errnum; /////////// radio parameters /////////// - double receiver_sensitivity = -112.0; // typical AM receiver sensitivity in dBm + double receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD // AM transmitter power in dBm. // Note this value is calculated from the typical final transistor stage output // !!! small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) // later store this value in aircraft description // ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now double transmitter_power = 43.0; - double antenna_gain = 2.0; + double antenna_gain = 2.0; //real-life gain for conventional monopole/dipole antenna if(ground_to_air) transmitter_power = 49.0; else transmitter_power = 43.0; if(ground_to_air) - antenna_gain = 5.0; //pilot plane's antenna gain + Controller antenna gain + antenna_gain = 5.0; //pilot plane's antenna gain + ground station antenna gain else antenna_gain = 2.0; //pilot plane's antenna gain + AI aircraft antenna gain double link_budget = transmitter_power - receiver_sensitivity + antenna_gain; @@ -909,7 +924,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy // else we need to calculate point to point link loss - point_to_point(itm_elev, sender_alt, own_alt, + point_to_point(itm_elev, transmitter_height, receiver_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, errnum); From 9afa0e90eddbd7e207e80e0808301ca139005bf7 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 5 Sep 2011 07:35:44 +0300 Subject: [PATCH 07/47] print some relevant data on screen to verify accuracy --- src/ATC/trafficcontrol.cxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 055ec9735..1e65c3ef6 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -790,7 +790,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy double ATC_HAAT = 30.0; double Aircraft_HAAT = 5.0; double sender_alt_ft,sender_alt; - double transceiver_height=0.0; + double transmitter_height=0.0; double receiver_height=0.0; SGGeod sender_pos; SGGeod max_sender_pos; @@ -818,7 +818,6 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; - //cerr << "ITM:: Distance: " << distance_m << endl; double max_points = distance_m / point_distance; deque _elevations; @@ -842,7 +841,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy else transmitter_height += Aircraft_HAAT; - cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << endl; + cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << ", Distance: " << distance_m << endl; // If distance larger than this value (400 km), assume reception imposssible // technically 400 km is no problem if LOS conditions exist, // but we do this to spare resources @@ -928,7 +927,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, errnum); - cerr << "ITM:: Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; + cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; //if (errnum !=0 && errnum !=1) // return -1; From c69c91769e15d550d8cff48aec09edbd2a562e7a Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 5 Sep 2011 07:56:13 +0300 Subject: [PATCH 08/47] fix array index out of bounds in dynamics.cxx --- src/Airports/dynamics.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Airports/dynamics.cxx b/src/Airports/dynamics.cxx index c42635f4a..293d2ff6a 100644 --- a/src/Airports/dynamics.cxx +++ b/src/Airports/dynamics.cxx @@ -508,7 +508,7 @@ int FGAirportDynamics::getGroundFrequency(unsigned leg) if ((freqGround.size() < leg) && (leg > 0)) { groundFreq = - (freqGround.size() < + (freqGround.size() <= (leg - 1)) ? freqGround[freqGround.size() - 1] : freqGround[leg - 1]; } From 2c91f179d19cc38175938d5d23967767a8ed7a0c Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 5 Sep 2011 09:46:19 +0300 Subject: [PATCH 09/47] reduce distance to 300 km --- src/ATC/trafficcontrol.cxx | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 1e65c3ef6..f53b17f18 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -817,36 +817,37 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; + // If distance larger than this value (300 km), assume reception imposssible + // technically 300 km is no problem if LOS conditions exist, + // but we do this to spare resources + if (distance_m > 300000) + return -1.0; double max_points = distance_m / point_distance; deque _elevations; - //SGGeod probe_pilot = SGGeod::fromGeoc(center.advanceRadM( course, 0 )); - SGGeod probe_pilot = max_own_pos; + double elevation_under_pilot = 0.0; - if (scenery->get_elevation_m( probe_pilot, elevation_under_pilot, NULL )) { + if (scenery->get_elevation_m( max_own_pos, elevation_under_pilot, NULL )) { receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground } - - //SGGeod probe_sender = SGGeod::fromGeoc(center.advanceRadM( course, distance_m )); - SGGeod probe_sender = max_sender_pos; + double elevation_under_sender = 0.0; - if (scenery->get_elevation_m( probe_sender, elevation_under_sender, NULL )) { + if (scenery->get_elevation_m( max_sender_pos, elevation_under_sender, NULL )) { transmitter_height = sender_alt - elevation_under_sender; } + else { + transmitter_height = sender_alt; + } if(ground_to_air) transmitter_height += ATC_HAAT; else transmitter_height += Aircraft_HAAT; cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << ", Distance: " << distance_m << endl; - // If distance larger than this value (400 km), assume reception imposssible - // technically 400 km is no problem if LOS conditions exist, - // but we do this to spare resources - if (distance_m > 400000) - return -1.0; + unsigned int e_size = (deque::size_type)max_points; @@ -860,6 +861,9 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy _elevations.push_front(elevation_m); //cerr << "ITM:: Probe elev: " << elevation_m << endl; } + else { + _elevations.push_front(0.0); + } } _elevations.push_back(elevation_under_pilot); _elevations.push_front(elevation_under_sender); From 80e5585cc33725a110b428e2c0de88c216f3293c Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 5 Sep 2011 11:51:19 +0300 Subject: [PATCH 10/47] comm radio subsystem --- src/ATC/trafficcontrol.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index f53b17f18..37586233a 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -846,7 +846,7 @@ double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDy else transmitter_height += Aircraft_HAAT; - cerr << "ITM:: RCVhgt: " << receiver_height << ", TRXhgt: " << transmitter_height << ", Distance: " << distance_m << endl; + cerr << "ITM:: RX-height: " << receiver_height << ", TX-height: " << transmitter_height << ", Distance: " << distance_m << endl; unsigned int e_size = (deque::size_type)max_points; From f96123de1cc50af660d00036b70b88cbc3bedf3f Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 6 Sep 2011 10:29:54 +0300 Subject: [PATCH 11/47] begin work on radio subsystem --- src/ATC/CMakeLists.txt | 1 - src/ATC/trafficcontrol.cxx | 199 +------------------ src/ATC/trafficcontrol.hxx | 1 - src/Instrumentation/CMakeLists.txt | 1 + src/Instrumentation/commradio.cxx | 282 +++++++++++++++++++++++++++ src/Instrumentation/commradio.hxx | 69 +++++++ src/{ATC => Instrumentation}/itm.cpp | 0 7 files changed, 353 insertions(+), 200 deletions(-) create mode 100644 src/Instrumentation/commradio.cxx create mode 100644 src/Instrumentation/commradio.hxx rename src/{ATC => Instrumentation}/itm.cpp (100%) diff --git a/src/ATC/CMakeLists.txt b/src/ATC/CMakeLists.txt index 26ccaadbf..e055ec6dd 100644 --- a/src/ATC/CMakeLists.txt +++ b/src/ATC/CMakeLists.txt @@ -5,7 +5,6 @@ set(SOURCES atcdialog.cxx trafficcontrol.cxx CommStation.cxx - itm.cpp ) flightgear_component(ATC "${SOURCES}") diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 37586233a..0f67094b8 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -25,9 +25,6 @@ #endif #include -#include -#include -#include #include #include @@ -50,8 +47,6 @@ #include #include #include -#define WITH_POINT_TO_POINT -#include "itm.cpp" using std::sort; @@ -736,25 +731,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, || (onBoardRadioFreqI1 == stationFreq)) { if (rec->allowTransmissions()) { - double snr = calculate_attenuation(rec, parent, ground_to_air); - if (snr <= 0) - return; - if (snr > 0 && snr < 12) { - //for low SNR values implement a way to make the conversation - //hard to understand but audible - //how this works in the real world, is the receiver AGC fails to capture the slope - //and the signal, due to being amplitude modulated, decreases volume after demodulation - //the implementation below is more akin to what would happen on a FM transmission - //therefore the correct way would be to work on the volume - string hash_noise = " "; - int reps = fabs((int)snr - 11); - int t_size = text.size(); - for (int n=1;n<=reps * 2;n++) { - int pos = rand() % t_size -1; - text.replace(pos,1, hash_noise); - } - - } + fgSetString("/sim/messages/atc", text.c_str()); } } @@ -765,180 +742,6 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, } } -double FGATCController::calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, - int ground_to_air) { - - /// Implement radio attenuation - /// based on the Longley-Rice propagation model - - FGScenery * scenery = globals->get_scenery(); - // player aircraft position - double own_lat = fgGetDouble("/position/latitude-deg"); - double own_lon = fgGetDouble("/position/longitude-deg"); - double own_alt_ft = fgGetDouble("/position/altitude-ft"); - double own_alt= own_alt_ft * SG_FEET_TO_METER; - - //cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; - - SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); - SGGeod max_own_pos = SGGeod::fromDegM( own_lon, own_lat, SG_MAX_ELEVATION_M ); - SGGeoc center = SGGeoc::fromGeod( max_own_pos ); - SGGeoc own_pos_c = SGGeoc::fromGeod( own_pos ); - - // position of sender radio antenna (HAAT) - // sender can be aircraft or ground station - double ATC_HAAT = 30.0; - double Aircraft_HAAT = 5.0; - double sender_alt_ft,sender_alt; - double transmitter_height=0.0; - double receiver_height=0.0; - SGGeod sender_pos; - SGGeod max_sender_pos; - if(ground_to_air) { - sender_alt_ft = parent->getElevation(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( parent->getLongitude(), - parent->getLatitude(), sender_alt ); - max_sender_pos = SGGeod::fromDegM( parent->getLongitude(), - parent->getLatitude(), SG_MAX_ELEVATION_M ); - } - else { - sender_alt_ft = rec->getAltitude(); - sender_alt = sender_alt_ft * SG_FEET_TO_METER; - sender_pos= SGGeod::fromDegM( rec->getLongitude(), - rec->getLatitude(), sender_alt ); - max_sender_pos = SGGeod::fromDegM( rec->getLongitude(), - rec->getLatitude(), SG_MAX_ELEVATION_M ); - } - SGGeoc sender_pos_c = SGGeoc::fromGeod( sender_pos ); - //cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; - - double point_distance= 90.0; // regular SRTM is 90 meters - double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); - double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); - double probe_distance = 0.0; - // If distance larger than this value (300 km), assume reception imposssible - // technically 300 km is no problem if LOS conditions exist, - // but we do this to spare resources - if (distance_m > 300000) - return -1.0; - - - double max_points = distance_m / point_distance; - deque _elevations; - - - double elevation_under_pilot = 0.0; - if (scenery->get_elevation_m( max_own_pos, elevation_under_pilot, NULL )) { - receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground - } - - - double elevation_under_sender = 0.0; - if (scenery->get_elevation_m( max_sender_pos, elevation_under_sender, NULL )) { - transmitter_height = sender_alt - elevation_under_sender; - } - else { - transmitter_height = sender_alt; - } - if(ground_to_air) - transmitter_height += ATC_HAAT; - else - transmitter_height += Aircraft_HAAT; - - cerr << "ITM:: RX-height: " << receiver_height << ", TX-height: " << transmitter_height << ", Distance: " << distance_m << endl; - - - unsigned int e_size = (deque::size_type)max_points; - - while (_elevations.size() <= e_size) { - probe_distance += point_distance; - SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance )); - - double elevation_m = 0.0; - - if (scenery->get_elevation_m( probe, elevation_m, NULL )) { - _elevations.push_front(elevation_m); - //cerr << "ITM:: Probe elev: " << elevation_m << endl; - } - else { - _elevations.push_front(0.0); - } - } - _elevations.push_back(elevation_under_pilot); - _elevations.push_front(elevation_under_sender); - double max_alt_between=0.0; - for( deque::size_type i = 0; i < _elevations.size(); i++ ) { - if (_elevations[i] > max_alt_between) { - max_alt_between = _elevations[i]; - } - } - - double num_points= (double)_elevations.size(); - //cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; - _elevations.push_front(point_distance); - _elevations.push_front(num_points -1); - int size = _elevations.size(); - double itm_elev[size]; - for(int i=0;i +#endif + +#include +#include +#include + +#include + +#define WITH_POINT_TO_POINT 1 +#include "itm.cpp" + + +FGCommRadio::FGCommRadio(SGPropertyNode *node) { + + /////////// radio parameters /////////// + _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD + // AM transmitter power in dBm. + // Note this value is calculated from the typical final transistor stage output + // !!! small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) + // later store this value in aircraft description + // ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now + _transmitter_power = 43.0; + //pilot plane's antenna gain + AI aircraft antenna gain + //real-life gain for conventional monopole/dipole antenna + _antenna_gain = 2.0; + _propagation_model = 2; // in the future choose between models via option: realistic radio on/off + +} + +FGCommRadio::~FGCommRadio() +{ +} + +void FGCommRadio::init () +{ +} + + +void FGCommRadio::bind () +{ +} + + +void FGCommRadio::update () +{ +} + +double FGCommRadio::getFrequency(int radio) { + double freq = 118.0; + switch (radio) { + case 1: + freq = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); + break; + case 2: + freq = fgGetDouble("/instrumentation/comm[1]/frequencies/selected-mhz"); + break; + default: + freq = fgGetDouble("/instrumentation/comm[0]/frequencies/selected-mhz"); + + } + return freq; +} + + + + +void FGCommRadio::receive(SGGeod tx_pos, double freq, string text, + int ground_to_air) { + + comm1 = getFrequency(1); + comm2 = getFrequency(2); + if ( (freq != comm1) && (freq != comm2) ) { + return; + } + else { + double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air); + if (signal <= 0) + return; + if ((signal > 0) && (signal < 12)) { + //for low SNR values implement a way to make the conversation + //hard to understand but audible + //how this works in the real world, is the receiver AGC fails to capture the slope + //and the signal, due to being amplitude modulated, decreases volume after demodulation + //the implementation below is more akin to what would happen on a FM transmission + //therefore the correct way would be to work on the volume + string hash_noise = " "; + int reps = fabs((int)signal - 11); + int t_size = text.size(); + for (int n=1;n<=reps * 2;n++) { + int pos = rand() % t_size -1; + text.replace(pos,1, hash_noise); + } + + } + fgSetString("/sim/messages/atc", text.c_str()); + } + +} + +double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, + int ground_to_air) { + + /// Implement radio attenuation + /// based on the Longley-Rice propagation model + + ////////////// ITM default parameters ////////////// + // in the future perhaps take them from tile materials? + double eps_dielect=15.0; + double sgm_conductivity = 0.005; + double eno = 301.0; + double frq_mhz; + if( (freq < 118.0) || (freq > 137.0) ) + frq_mhz = 125.0; // sane value, middle of bandplan + else + frq_mhz = freq; + int radio_climate = 5; // continental temperate + int pol=1; // assuming vertical polarization although this is more complex in reality + double conf = 0.90; // 90% of situations and time, take into account speed + double rel = 0.90; // ^^ + double dbloss; + char strmode[150]; + int errnum; + + double tx_pow,ant_gain; + double signal = 0.0; + + if(ground_to_air) + tx_pow = _transmitter_power + 6.0; + + if(ground_to_air) + ant_gain = _antenna_gain + 3.0; //pilot plane's antenna gain + ground station antenna gain + + double link_budget = tx_pow - _receiver_sensitivity + ant_gain; + + FGScenery * scenery = globals->get_scenery(); + + double own_lat = fgGetDouble("/position/latitude-deg"); + double own_lon = fgGetDouble("/position/longitude-deg"); + double own_alt_ft = fgGetDouble("/position/altitude-ft"); + double own_alt= own_alt_ft * SG_FEET_TO_METER; + + + //cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; + + SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); + SGGeod max_own_pos = SGGeod::fromDegM( own_lon, own_lat, SG_MAX_ELEVATION_M ); + SGGeoc center = SGGeoc::fromGeod( max_own_pos ); + SGGeoc own_pos_c = SGGeoc::fromGeod( own_pos ); + + // position of sender radio antenna (HAAT) + // sender can be aircraft or ground station + double ATC_HAAT = 30.0; + double Aircraft_HAAT = 5.0; + double sender_alt_ft,sender_alt; + double transmitter_height=0.0; + double receiver_height=0.0; + SGGeod sender_pos = pos; + + sender_alt_ft = sender_pos.getElevationFt(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + SGGeod max_sender_pos = SGGeod::fromGeodM( pos, SG_MAX_ELEVATION_M ); + SGGeoc sender_pos_c = SGGeoc::fromGeod( sender_pos ); + //cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; + + double point_distance= 90.0; // regular SRTM is 90 meters + double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); + double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); + double probe_distance = 0.0; + // If distance larger than this value (300 km), assume reception imposssible to preserve resources + if (distance_m > 300000) + return -1.0; + // If above 9000, consider LOS mode and calculate free-space att + if (own_alt > 9000) { + dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; + signal = link_budget - dbloss; + return signal; + } + + + double max_points = distance_m / point_distance; + deque _elevations; + + double elevation_under_pilot = 0.0; + if (scenery->get_elevation_m( max_own_pos, elevation_under_pilot, NULL )) { + receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground + } + + double elevation_under_sender = 0.0; + if (scenery->get_elevation_m( max_sender_pos, elevation_under_sender, NULL )) { + transmitter_height = sender_alt - elevation_under_sender; + } + else { + transmitter_height = sender_alt; + } + + if(ground_to_air) + transmitter_height += ATC_HAAT; + else + transmitter_height += Aircraft_HAAT; + + cerr << "ITM:: RX-height: " << receiver_height << ", TX-height: " << transmitter_height << ", Distance: " << distance_m << endl; + + unsigned int e_size = (deque::size_type)max_points; + + while (_elevations.size() <= e_size) { + probe_distance += point_distance; + SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance )); + + double elevation_m = 0.0; + + if (scenery->get_elevation_m( probe, elevation_m, NULL )) { + _elevations.push_front(elevation_m); + //cerr << "ITM:: Probe elev: " << elevation_m << endl; + } + else { + _elevations.push_front(0.0); + } + } + _elevations.push_back(elevation_under_pilot); + _elevations.push_front(elevation_under_sender); + + double max_alt_between=0.0; + for( deque::size_type i = 0; i < _elevations.size(); i++ ) { + if (_elevations[i] > max_alt_between) { + max_alt_between = _elevations[i]; + } + } + + double num_points= (double)_elevations.size(); + //cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; + _elevations.push_front(point_distance); + _elevations.push_front(num_points -1); + int size = _elevations.size(); + double itm_elev[size]; + for(int i=0;i +#include + +#include
+ +#include +#include +#include + + +using std::string; + + +class FGCommRadio : public SGSubsystem, public SGPropertyChangeListener +{ +private: + bool isOperable() const + { return _operable; } + bool _operable; ///< is the unit serviceable, on, powered, etc + + double _receiver_sensitivity; + double _transmitter_power; + double _antenna_gain; + + int _propagation_model; /// 0 none, 1 round Earth, 2 ITM + double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air) + +public: + + FGCommRadio(SGPropertyNode *node); + ~FGCommRadio(); + + void init (); + void bind (); + void unbind (); + void update (double dt); + + void setFrequency(double freq, int radio); + double getFrequency(int radio); + void setTxPower(double txpower) { _transmitter_power = txpower; }; + void receive_text(SGGeod tx_pos, double freq, string text, int ground_to_air); + void setPropagationModel(int model) { _propagation_model = model; }; + +}; + + diff --git a/src/ATC/itm.cpp b/src/Instrumentation/itm.cpp similarity index 100% rename from src/ATC/itm.cpp rename to src/Instrumentation/itm.cpp From 66e2bac574408d74ba3556f93290d293d5185351 Mon Sep 17 00:00:00 2001 From: adrian Date: Fri, 16 Sep 2011 16:28:16 +0300 Subject: [PATCH 12/47] commradio subsystem part deux --- src/Instrumentation/commradio.cxx | 56 ++++++++++++++++++++++++------- src/Instrumentation/commradio.hxx | 3 +- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx index cbdfda0ae..6ee5e44d4 100644 --- a/src/Instrumentation/commradio.cxx +++ b/src/Instrumentation/commradio.cxx @@ -45,7 +45,7 @@ FGCommRadio::FGCommRadio(SGPropertyNode *node) { //pilot plane's antenna gain + AI aircraft antenna gain //real-life gain for conventional monopole/dipole antenna _antenna_gain = 2.0; - _propagation_model = 2; // in the future choose between models via option: realistic radio on/off + _propagation_model = 2; // choose between models via option: realistic radio on/off } @@ -65,6 +65,9 @@ void FGCommRadio::bind () void FGCommRadio::update () { + if (dt <= 0.0) { + return; // paused + } } double FGCommRadio::getFrequency(int radio) { @@ -86,7 +89,7 @@ double FGCommRadio::getFrequency(int radio) { -void FGCommRadio::receive(SGGeod tx_pos, double freq, string text, +void FGCommRadio::receive_text(SGGeod tx_pos, double freq, string text, int ground_to_air) { comm1 = getFrequency(1); @@ -105,6 +108,7 @@ void FGCommRadio::receive(SGGeod tx_pos, double freq, string text, //and the signal, due to being amplitude modulated, decreases volume after demodulation //the implementation below is more akin to what would happen on a FM transmission //therefore the correct way would be to work on the volume + /* string hash_noise = " "; int reps = fabs((int)signal - 11); int t_size = text.size(); @@ -112,6 +116,7 @@ void FGCommRadio::receive(SGGeod tx_pos, double freq, string text, int pos = rand() % t_size -1; text.replace(pos,1, hash_noise); } + */ } fgSetString("/sim/messages/atc", text.c_str()); @@ -120,7 +125,7 @@ void FGCommRadio::receive(SGGeod tx_pos, double freq, string text, } double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, - int ground_to_air) { + int transmission_type) { /// Implement radio attenuation /// based on the Longley-Rice propagation model @@ -146,10 +151,10 @@ double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, double tx_pow,ant_gain; double signal = 0.0; - if(ground_to_air) + if(transmission_type == 1) tx_pow = _transmitter_power + 6.0; - if(ground_to_air) + if((transmission_type == 1) || (transmission_type == 3)) ant_gain = _antenna_gain + 3.0; //pilot plane's antenna gain + ground station antenna gain double link_budget = tx_pow - _receiver_sensitivity + ant_gain; @@ -215,7 +220,7 @@ double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, transmitter_height = sender_alt; } - if(ground_to_air) + if(transmission_type == 1) transmitter_height += ATC_HAAT; else transmitter_height += Aircraft_HAAT; @@ -231,15 +236,31 @@ double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, double elevation_m = 0.0; if (scenery->get_elevation_m( probe, elevation_m, NULL )) { - _elevations.push_front(elevation_m); - //cerr << "ITM:: Probe elev: " << elevation_m << endl; + if((transmission_type == 3) || (transmission_type == 4)) { + _elevations.push_back(elevation_m); + } + else { + _elevations.push_front(elevation_m); + } } else { + if((transmission_type == 3) || (transmission_type == 4)) { + _elevations.push_back(elevation_m); + } + else { _elevations.push_front(0.0); + } } } - _elevations.push_back(elevation_under_pilot); - _elevations.push_front(elevation_under_sender); + if((transmission_type == 3) || (transmission_type == 4)) { + _elevations.push_front(elevation_under_pilot); + _elevations.push_back(elevation_under_sender); + } + else { + _elevations.push_back(elevation_under_pilot); + _elevations.push_front(elevation_under_sender); + } + double max_alt_between=0.0; for( deque::size_type i = 0; i < _elevations.size(); i++ ) { @@ -267,10 +288,19 @@ double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, // TODO: If we clear the first Fresnel zone, we are into line of sight teritory // else we need to calculate point to point link loss + if((transmission_type == 3) || (transmission_type == 4)) { + // the sender and receiver roles are switched + point_to_point(itm_elev, receiver_height, transmitter_height, + eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, + pol, conf, rel, dbloss, strmode, errnum); + + } + else { - point_to_point(itm_elev, transmitter_height, receiver_height, - eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, - pol, conf, rel, dbloss, strmode, errnum); + point_to_point(itm_elev, transmitter_height, receiver_height, + eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, + pol, conf, rel, dbloss, strmode, errnum); + } cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; diff --git a/src/Instrumentation/commradio.hxx b/src/Instrumentation/commradio.hxx index 8cd1041ac..0ba3266c8 100644 --- a/src/Instrumentation/commradio.hxx +++ b/src/Instrumentation/commradio.hxx @@ -61,7 +61,8 @@ public: void setFrequency(double freq, int radio); double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; - void receive_text(SGGeod tx_pos, double freq, string text, int ground_to_air); + // transmission_type: 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air + void receive_text(SGGeod tx_pos, double freq, string text, int transmission_type); void setPropagationModel(int model) { _propagation_model = model; }; }; From 9527a0391eb93ed16df93567b9116493fd9303b6 Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 1 Nov 2011 17:44:41 +0200 Subject: [PATCH 13/47] add to project files --- projects/VC90/FlightGear/FlightGear.vcproj | 8 ++++++++ src/{Instrumentation => Include}/itm.cpp | 0 src/Instrumentation/CMakeLists.txt | 2 ++ src/Instrumentation/Makefile.am | 1 + src/Instrumentation/commradio.cxx | 4 ++-- src/Instrumentation/commradio.hxx | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) rename src/{Instrumentation => Include}/itm.cpp (100%) diff --git a/projects/VC90/FlightGear/FlightGear.vcproj b/projects/VC90/FlightGear/FlightGear.vcproj index 58633d244..7b2de36b8 100644 --- a/projects/VC90/FlightGear/FlightGear.vcproj +++ b/projects/VC90/FlightGear/FlightGear.vcproj @@ -3865,6 +3865,14 @@ RelativePath="..\..\..\src\Instrumentation\wxradar.hxx" > + + + + diff --git a/src/Instrumentation/itm.cpp b/src/Include/itm.cpp similarity index 100% rename from src/Instrumentation/itm.cpp rename to src/Include/itm.cpp diff --git a/src/Instrumentation/CMakeLists.txt b/src/Instrumentation/CMakeLists.txt index 7ae7cf343..e092ec369 100644 --- a/src/Instrumentation/CMakeLists.txt +++ b/src/Instrumentation/CMakeLists.txt @@ -26,6 +26,7 @@ set(SOURCES mrg.cxx navradio.cxx newnavradio.cxx + commradio.cxx od_gauge.cxx rad_alt.cxx render_area_2d.cxx @@ -93,6 +94,7 @@ set(HEADERS mrg.hxx navradio.hxx newnavradio.hxx + commradio.hxx od_gauge.hxx rad_alt.hxx render_area_2d.hxx diff --git a/src/Instrumentation/Makefile.am b/src/Instrumentation/Makefile.am index 23292e17c..f51c8f900 100644 --- a/src/Instrumentation/Makefile.am +++ b/src/Instrumentation/Makefile.am @@ -23,6 +23,7 @@ libInstrumentation_a_SOURCES = \ mrg.cxx mrg.hxx\ navradio.cxx navradio.hxx \ newnavradio.cxx newnavradio.hxx \ + commradio.cxx commradio.hxx \ slip_skid_ball.cxx slip_skid_ball.hxx \ transponder.cxx transponder.hxx \ turn_indicator.cxx turn_indicator.hxx \ diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx index 6ee5e44d4..304d2ddee 100644 --- a/src/Instrumentation/commradio.cxx +++ b/src/Instrumentation/commradio.cxx @@ -1,5 +1,5 @@ // commradio.cxx -- implementation of FGCommRadio -// +// Class to manage radio propagation using the ITM model // Written by Adrian Musceac, started August 2011. // // This program is free software; you can redistribute it and/or @@ -89,7 +89,7 @@ double FGCommRadio::getFrequency(int radio) { -void FGCommRadio::receive_text(SGGeod tx_pos, double freq, string text, +void FGCommRadio::receiveText(SGGeod tx_pos, double freq, string text, int ground_to_air) { comm1 = getFrequency(1); diff --git a/src/Instrumentation/commradio.hxx b/src/Instrumentation/commradio.hxx index 0ba3266c8..397fca581 100644 --- a/src/Instrumentation/commradio.hxx +++ b/src/Instrumentation/commradio.hxx @@ -62,7 +62,7 @@ public: double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; // transmission_type: 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air - void receive_text(SGGeod tx_pos, double freq, string text, int transmission_type); + void receiveText(SGGeod tx_pos, double freq, string text, int transmission_type); void setPropagationModel(int model) { _propagation_model = model; }; }; From 332f76b11e0fd7e5c3dc841c2c95b4b10405f8e9 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 04:49:05 +0200 Subject: [PATCH 14/47] remove subsystem --- src/Instrumentation/commradio.cxx | 22 +++------------------- src/Instrumentation/commradio.hxx | 7 +------ 2 files changed, 4 insertions(+), 25 deletions(-) diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx index 304d2ddee..b6a34a432 100644 --- a/src/Instrumentation/commradio.cxx +++ b/src/Instrumentation/commradio.cxx @@ -53,22 +53,6 @@ FGCommRadio::~FGCommRadio() { } -void FGCommRadio::init () -{ -} - - -void FGCommRadio::bind () -{ -} - - -void FGCommRadio::update () -{ - if (dt <= 0.0) { - return; // paused - } -} double FGCommRadio::getFrequency(int radio) { double freq = 118.0; @@ -92,8 +76,8 @@ double FGCommRadio::getFrequency(int radio) { void FGCommRadio::receiveText(SGGeod tx_pos, double freq, string text, int ground_to_air) { - comm1 = getFrequency(1); - comm2 = getFrequency(2); + double comm1 = getFrequency(1); + double comm2 = getFrequency(2); if ( (freq != comm1) && (freq != comm2) ) { return; } @@ -143,7 +127,7 @@ double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int radio_climate = 5; // continental temperate int pol=1; // assuming vertical polarization although this is more complex in reality double conf = 0.90; // 90% of situations and time, take into account speed - double rel = 0.90; // ^^ + double rel = 0.90; double dbloss; char strmode[150]; int errnum; diff --git a/src/Instrumentation/commradio.hxx b/src/Instrumentation/commradio.hxx index 397fca581..70cafd937 100644 --- a/src/Instrumentation/commradio.hxx +++ b/src/Instrumentation/commradio.hxx @@ -28,13 +28,12 @@ #include #include -#include using std::string; -class FGCommRadio : public SGSubsystem, public SGPropertyChangeListener +class FGCommRadio { private: bool isOperable() const @@ -53,10 +52,6 @@ public: FGCommRadio(SGPropertyNode *node); ~FGCommRadio(); - void init (); - void bind (); - void unbind (); - void update (double dt); void setFrequency(double freq, int radio); double getFrequency(int radio); From b55fd4de91933bfb83ab2c4a9bc355d9fc161d8b Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 05:02:42 +0200 Subject: [PATCH 15/47] separate implementation --- src/Instrumentation/CMakeLists.txt | 2 -- src/Radio/CMakeLists.txt | 14 ++++++++++++++ src/{Include => Radio}/itm.cpp | 0 .../commradio.cxx => Radio/radio.cxx} | 8 ++++---- .../commradio.hxx => Radio/radio.hxx} | 6 +++--- 5 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 src/Radio/CMakeLists.txt rename src/{Include => Radio}/itm.cpp (100%) rename src/{Instrumentation/commradio.cxx => Radio/radio.cxx} (97%) rename src/{Instrumentation/commradio.hxx => Radio/radio.hxx} (96%) diff --git a/src/Instrumentation/CMakeLists.txt b/src/Instrumentation/CMakeLists.txt index e092ec369..7ae7cf343 100644 --- a/src/Instrumentation/CMakeLists.txt +++ b/src/Instrumentation/CMakeLists.txt @@ -26,7 +26,6 @@ set(SOURCES mrg.cxx navradio.cxx newnavradio.cxx - commradio.cxx od_gauge.cxx rad_alt.cxx render_area_2d.cxx @@ -94,7 +93,6 @@ set(HEADERS mrg.hxx navradio.hxx newnavradio.hxx - commradio.hxx od_gauge.hxx rad_alt.hxx render_area_2d.hxx diff --git a/src/Radio/CMakeLists.txt b/src/Radio/CMakeLists.txt new file mode 100644 index 000000000..aa6d5483b --- /dev/null +++ b/src/Radio/CMakeLists.txt @@ -0,0 +1,14 @@ +include(FlightGearComponent) + +set(SOURCES + radio.cxx + itm.cpp + ) + +set(HEADERS + radio.hxx + ) + + +flightgear_component(Radio "${SOURCES}" "${HEADERS}") + diff --git a/src/Include/itm.cpp b/src/Radio/itm.cpp similarity index 100% rename from src/Include/itm.cpp rename to src/Radio/itm.cpp diff --git a/src/Instrumentation/commradio.cxx b/src/Radio/radio.cxx similarity index 97% rename from src/Instrumentation/commradio.cxx rename to src/Radio/radio.cxx index b6a34a432..c1ca246b1 100644 --- a/src/Instrumentation/commradio.cxx +++ b/src/Radio/radio.cxx @@ -32,7 +32,7 @@ #include "itm.cpp" -FGCommRadio::FGCommRadio(SGPropertyNode *node) { +FGRadio::FGRadio() { /////////// radio parameters /////////// _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD @@ -49,7 +49,7 @@ FGCommRadio::FGCommRadio(SGPropertyNode *node) { } -FGCommRadio::~FGCommRadio() +FGRadio::~FGRadio() { } @@ -73,7 +73,7 @@ double FGCommRadio::getFrequency(int radio) { -void FGCommRadio::receiveText(SGGeod tx_pos, double freq, string text, +void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, int ground_to_air) { double comm1 = getFrequency(1); @@ -108,7 +108,7 @@ void FGCommRadio::receiveText(SGGeod tx_pos, double freq, string text, } -double FGCommRadio::ITM_calculate_attenuation(SGGeod pos, double freq, +double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { /// Implement radio attenuation diff --git a/src/Instrumentation/commradio.hxx b/src/Radio/radio.hxx similarity index 96% rename from src/Instrumentation/commradio.hxx rename to src/Radio/radio.hxx index 70cafd937..7915d2e77 100644 --- a/src/Instrumentation/commradio.hxx +++ b/src/Radio/radio.hxx @@ -33,7 +33,7 @@ using std::string; -class FGCommRadio +class FGRadio { private: bool isOperable() const @@ -49,8 +49,8 @@ private: public: - FGCommRadio(SGPropertyNode *node); - ~FGCommRadio(); + FGRadio(); + ~FGRadio(); void setFrequency(double freq, int radio); From 322a15f8da7eb2ba73f29ff28e4caee9d373eac1 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 05:38:15 +0200 Subject: [PATCH 16/47] add to CMake --- src/CMakeLists.txt | 1 + src/Radio/radio.cxx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3edc66fae..f3ebefd91 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ include_directories(${PROJECT_SOURCE_DIR}) foreach( mylibfolder Airports Aircraft + Radio ATC ATCDCL Autopilot diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index c1ca246b1..fdd8dd416 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -177,7 +177,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; - // If distance larger than this value (300 km), assume reception imposssible to preserve resources + // If distance larger than this value (300 km), assume reception imposssible if (distance_m > 300000) return -1.0; // If above 9000, consider LOS mode and calculate free-space att From 655b88dd17cc4f84446f00573547237b08b381a1 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 05:40:10 +0200 Subject: [PATCH 17/47] remove old files from VCproj --- projects/VC90/FlightGear/FlightGear.vcproj | 8 -------- 1 file changed, 8 deletions(-) diff --git a/projects/VC90/FlightGear/FlightGear.vcproj b/projects/VC90/FlightGear/FlightGear.vcproj index 7b2de36b8..58633d244 100644 --- a/projects/VC90/FlightGear/FlightGear.vcproj +++ b/projects/VC90/FlightGear/FlightGear.vcproj @@ -3865,14 +3865,6 @@ RelativePath="..\..\..\src\Instrumentation\wxradar.hxx" > - - - - From 98a94d83efa48753e4d1bec260d25fdfb60de931 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 06:20:59 +0200 Subject: [PATCH 18/47] call from FGATCController::transmit() --- src/ATC/trafficcontrol.cxx | 22 +++++++++++++++++++++- src/ATC/trafficcontrol.hxx | 2 +- src/Radio/radio.cxx | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index aefc39b42..3e27f9190 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -743,7 +743,27 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, || (onBoardRadioFreqI1 == stationFreq)) { if (rec->allowTransmissions()) { - fgSetString("/sim/messages/atc", text.c_str()); + if( fgGetBool( "/instrumentation/use-itm-attenuation", false ) ) { + FGRadio* radio = new FGRadio(); + SGGeod sender_pos; + double sender_alt_ft, sender_alt; + if(ground_to_air) { + sender_alt_ft = parent->getElevation(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_pos= SGGeod::fromDegM( parent->getLongitude(), + parent->getLatitude(), sender_alt ); + } + else { + sender_alt_ft = rec->getAltitude(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + sender_pos= SGGeod::fromDegM( rec->getLongitude(), + rec->getLatitude(), sender_alt ); + } + radio->receiveText(sender_pos, stationFreq, text, ground_to_air); + } + else { + fgSetString("/sim/messages/atc", text.c_str()); + } } } } else { diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 90c83edb4..7c17665f9 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -453,7 +453,7 @@ public: void setDt(double dt) { dt_count = dt; }; - void transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); + void transmit(FGTrafficRecord *rec, FGAirportDynamics *parent, AtcMsgId msgId, AtcMsgDir msgDir, bool audible); string getGateName(FGAIAircraft *aircraft); virtual void render(bool) = 0; virtual string getName() = 0; diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index fdd8dd416..5ce656f6c 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -54,7 +54,7 @@ FGRadio::~FGRadio() } -double FGCommRadio::getFrequency(int radio) { +double FGRadio::getFrequency(int radio) { double freq = 118.0; switch (radio) { case 1: From 9bcc3a87b64fed976e8172e2f6fe643345007631 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 06:39:54 +0200 Subject: [PATCH 19/47] cleanup --- src/ATC/trafficcontrol.cxx | 2 ++ src/Radio/itm.cpp | 2 +- src/Radio/radio.cxx | 7 ++++--- src/Radio/radio.hxx | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 3e27f9190..507c8f113 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -47,6 +47,7 @@ #include #include #include +#include using std::sort; @@ -760,6 +761,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, rec->getLatitude(), sender_alt ); } radio->receiveText(sender_pos, stationFreq, text, ground_to_air); + delete radio; } else { fgSetString("/sim/messages/atc", text.c_str()); diff --git a/src/Radio/itm.cpp b/src/Radio/itm.cpp index 20862355b..9c2f010fc 100644 --- a/src/Radio/itm.cpp +++ b/src/Radio/itm.cpp @@ -37,7 +37,7 @@ #include #include #include - +#include const float THIRD = (1.0/3.0); const float f_0 = 47.7; // 47.7 MHz from [Alg 1.1], to convert frequency into wavenumber and vica versa diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 5ce656f6c..83068a285 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -25,7 +25,7 @@ #include #include #include - +#include "radio.hxx" #include #define WITH_POINT_TO_POINT 1 @@ -132,7 +132,8 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, char strmode[150]; int errnum; - double tx_pow,ant_gain; + double tx_pow = _transmitter_power; + double ant_gain = _antenna_gain; double signal = 0.0; if(transmission_type == 1) @@ -269,7 +270,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, // frequency in the middle of the bandplan, more accuracy is not necessary double fz_clr= 8.657 * sqrt(distance_m / 0.125); - // TODO: If we clear the first Fresnel zone, we are into line of sight teritory + // TODO: If we clear the first Fresnel zone, we are into line of sight territory // else we need to calculate point to point link loss if((transmission_type == 3) || (transmission_type == 4)) { diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 7915d2e77..91d028047 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -45,7 +45,7 @@ private: double _antenna_gain; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM - double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air) + double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); public: From e17852f6793303016945189ef8c7503eda92c26e Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 10:00:28 +0200 Subject: [PATCH 20/47] prepare for festival volume --- src/ATC/trafficcontrol.cxx | 6 ++++-- src/CMakeLists.txt | 2 +- src/Radio/radio.cxx | 40 +++++++++++++++++++++++++++----------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 507c8f113..2b0d20c67 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -744,7 +744,8 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, || (onBoardRadioFreqI1 == stationFreq)) { if (rec->allowTransmissions()) { - if( fgGetBool( "/instrumentation/use-itm-attenuation", false ) ) { + if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) { + //cerr << "Using ITM radio propagation" << endl; FGRadio* radio = new FGRadio(); SGGeod sender_pos; double sender_alt_ft, sender_alt; @@ -760,7 +761,8 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, sender_pos= SGGeod::fromDegM( rec->getLongitude(), rec->getLatitude(), sender_alt ); } - radio->receiveText(sender_pos, stationFreq, text, ground_to_air); + double frequency = ((double)stationFreq) / 100; + radio->receiveText(sender_pos, frequency, text, ground_to_air); delete radio; } else { diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f3ebefd91..bf2d6e30d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,9 +6,9 @@ include_directories(${PROJECT_SOURCE_DIR}) foreach( mylibfolder Airports Aircraft - Radio ATC ATCDCL + Radio Autopilot Cockpit Environment diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 83068a285..7e9c4cba9 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -76,16 +76,22 @@ double FGRadio::getFrequency(int radio) { void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, int ground_to_air) { + /* double comm1 = getFrequency(1); double comm2 = getFrequency(2); if ( (freq != comm1) && (freq != comm2) ) { + cerr << "Frequency not tuned: " << freq << " Radio1: " << comm1 << " Radio2: " << comm2 << endl; return; } else { + */ double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air); - if (signal <= 0) + //cerr << "Signal: " << signal << endl; + if (signal <= 0.0) { + //cerr << "Signal below sensitivity: " << signal << " dBm" << endl; return; - if ((signal > 0) && (signal < 12)) { + } + if ((signal > 0.0) && (signal < 12.0)) { //for low SNR values implement a way to make the conversation //hard to understand but audible //how this works in the real world, is the receiver AGC fails to capture the slope @@ -94,17 +100,28 @@ void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, //therefore the correct way would be to work on the volume /* string hash_noise = " "; - int reps = fabs((int)signal - 11); + int reps = (int) (fabs(floor(signal - 11.0)) * 2); + cerr << "Reps: " << reps << endl; int t_size = text.size(); - for (int n=1;n<=reps * 2;n++) { - int pos = rand() % t_size -1; + for (int n = 1; n <= reps; ++n) { + int pos = rand() % (t_size -1); + cerr << "Pos: " << pos << endl; text.replace(pos,1, hash_noise); } */ - + double volume = (fabs(signal - 12.0) / 12); + double old_volume = fgGetDouble("/sim/sound/voices/voice/volume"); + //cerr << "Usable signal at limit: " << signal << endl; + fgSetDouble("/sim/sound/voices/voice/volume", volume); + fgSetString("/sim/messages/atc", text.c_str()); + fgSetDouble("/sim/sound/voices/voice/volume", old_volume); } - fgSetString("/sim/messages/atc", text.c_str()); - } + else { + //cerr << "Signal completely readable: " << signal << " dBm" << endl; + fgSetString("/sim/messages/atc", text.c_str()); + } + + //} } @@ -181,9 +198,10 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, // If distance larger than this value (300 km), assume reception imposssible if (distance_m > 300000) return -1.0; - // If above 9000, consider LOS mode and calculate free-space att - if (own_alt > 9000) { + // If above 8000, consider LOS mode and calculate free-space att + if (own_alt > 8000) { dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; + cerr << "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation" << endl; signal = link_budget - dbloss; return signal; } @@ -210,7 +228,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, else transmitter_height += Aircraft_HAAT; - cerr << "ITM:: RX-height: " << receiver_height << ", TX-height: " << transmitter_height << ", Distance: " << distance_m << endl; + cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; unsigned int e_size = (deque::size_type)max_points; From 0dd93d56d6d0d62acbf2e0808b080850c14c900c Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 10:19:07 +0200 Subject: [PATCH 21/47] cleanup --- src/Radio/radio.cxx | 6 +++--- src/Radio/radio.hxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 7e9c4cba9..6ce93ba63 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -1,4 +1,4 @@ -// commradio.cxx -- implementation of FGCommRadio +// radio.cxx -- implementation of FGRadio // Class to manage radio propagation using the ITM model // Written by Adrian Musceac, started August 2011. // @@ -101,11 +101,11 @@ void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, /* string hash_noise = " "; int reps = (int) (fabs(floor(signal - 11.0)) * 2); - cerr << "Reps: " << reps << endl; + //cerr << "Reps: " << reps << endl; int t_size = text.size(); for (int n = 1; n <= reps; ++n) { int pos = rand() % (t_size -1); - cerr << "Pos: " << pos << endl; + //cerr << "Pos: " << pos << endl; text.replace(pos,1, hash_noise); } */ diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 91d028047..4b2b67c85 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -1,4 +1,4 @@ -// commradio.hxx -- class to manage a comm radio instance +// radio.hxx -- FGRadio: class to manage radio propagation // // Written by Adrian Musceac, started August 2011. // From f023c3bc0ac483034ee01cd93b825c4aa8874dc7 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 15:37:33 +0200 Subject: [PATCH 22/47] log to SG_BULK --- src/Radio/radio.cxx | 131 ++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 53 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 6ce93ba63..9b3d0320f 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -34,16 +34,20 @@ FGRadio::FGRadio() { - /////////// radio parameters /////////// + /** radio parameters (which should probably be set for each radio via constructor) */ _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD - // AM transmitter power in dBm. - // Note this value is calculated from the typical final transistor stage output - // !!! small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) - // later store this value in aircraft description - // ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now + + /** AM transmitter power in dBm. + * Note this value is calculated from the typical final transistor stage output + * small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) + * later store this value in aircraft description + * ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now + **/ _transmitter_power = 43.0; - //pilot plane's antenna gain + AI aircraft antenna gain - //real-life gain for conventional monopole/dipole antenna + + /** pilot plane's antenna gain + AI aircraft antenna gain + * real-life gain for conventional monopole/dipole antenna + **/ _antenna_gain = 2.0; _propagation_model = 2; // choose between models via option: realistic radio on/off @@ -85,54 +89,68 @@ void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, } else { */ - double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air); - //cerr << "Signal: " << signal << endl; - if (signal <= 0.0) { - //cerr << "Signal below sensitivity: " << signal << " dBm" << endl; - return; + if ( _propagation_model == 0) { + fgSetString("/sim/messages/atc", text.c_str()); } - if ((signal > 0.0) && (signal < 12.0)) { - //for low SNR values implement a way to make the conversation - //hard to understand but audible - //how this works in the real world, is the receiver AGC fails to capture the slope - //and the signal, due to being amplitude modulated, decreases volume after demodulation - //the implementation below is more akin to what would happen on a FM transmission - //therefore the correct way would be to work on the volume - /* - string hash_noise = " "; - int reps = (int) (fabs(floor(signal - 11.0)) * 2); - //cerr << "Reps: " << reps << endl; - int t_size = text.size(); - for (int n = 1; n <= reps; ++n) { - int pos = rand() % (t_size -1); - //cerr << "Pos: " << pos << endl; - text.replace(pos,1, hash_noise); + else if ( _propagation_model == 1 ) { + // free space, round earth + } + else if ( _propagation_model == 2 ) { + // Use ITM propagation model + double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air); + if (signal <= 0.0) { + SG_LOG(SG_GENERAL, SG_BULK, "Signal below receiver minimum sensitivity: " << signal); + //cerr << "Signal below receiver minimum sensitivity: " << signal << endl; + return; } - */ - double volume = (fabs(signal - 12.0) / 12); - double old_volume = fgGetDouble("/sim/sound/voices/voice/volume"); - //cerr << "Usable signal at limit: " << signal << endl; - fgSetDouble("/sim/sound/voices/voice/volume", volume); - fgSetString("/sim/messages/atc", text.c_str()); - fgSetDouble("/sim/sound/voices/voice/volume", old_volume); - } - else { - //cerr << "Signal completely readable: " << signal << " dBm" << endl; - fgSetString("/sim/messages/atc", text.c_str()); + if ((signal > 0.0) && (signal < 12.0)) { + /** for low SNR values implement a way to make the conversation + * hard to understand but audible + * in the real world, the receiver AGC fails to capture the slope + * and the signal, due to being amplitude modulated, decreases volume after demodulation + * the workaround below is more akin to what would happen on a FM transmission + * therefore the correct way would be to work on the volume + **/ + /* + string hash_noise = " "; + int reps = (int) (fabs(floor(signal - 11.0)) * 2); + int t_size = text.size(); + for (int n = 1; n <= reps; ++n) { + int pos = rand() % (t_size -1); + text.replace(pos,1, hash_noise); + } + */ + double volume = (fabs(signal - 12.0) / 12); + double old_volume = fgGetDouble("/sim/sound/voices/voice/volume"); + SG_LOG(SG_GENERAL, SG_BULK, "Usable signal at limit: " << signal); + //cerr << "Usable signal at limit: " << signal << endl; + fgSetDouble("/sim/sound/voices/voice/volume", volume); + fgSetString("/sim/messages/atc", text.c_str()); + fgSetDouble("/sim/sound/voices/voice/volume", old_volume); + } + else { + SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); + //cerr << "Signal completely readable: " << signal << endl; + fgSetString("/sim/messages/atc", text.c_str()); + } + } //} } +/*** Implement radio attenuation + based on the Longley-Rice propagation model +***/ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { - /// Implement radio attenuation - /// based on the Longley-Rice propagation model - ////////////// ITM default parameters ////////////// - // in the future perhaps take them from tile materials? + + /** ITM default parameters + TODO: take them from tile materials (especially for sea)? + **/ double eps_dielect=15.0; double sgm_conductivity = 0.005; double eno = 301.0; @@ -176,8 +194,9 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, SGGeoc center = SGGeoc::fromGeod( max_own_pos ); SGGeoc own_pos_c = SGGeoc::fromGeod( own_pos ); - // position of sender radio antenna (HAAT) - // sender can be aircraft or ground station + /** position of sender radio antenna (HAAT) + sender can be aircraft or ground station + **/ double ATC_HAAT = 30.0; double Aircraft_HAAT = 5.0; double sender_alt_ft,sender_alt; @@ -195,13 +214,15 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; - // If distance larger than this value (300 km), assume reception imposssible + /** If distance larger than this value (300 km), assume reception imposssible */ if (distance_m > 300000) return -1.0; - // If above 8000, consider LOS mode and calculate free-space att + /** If above 8000 meters, consider LOS mode and calculate free-space att */ if (own_alt > 8000) { dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; - cerr << "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation" << endl; + SG_LOG(SG_GENERAL, SG_BULK, + "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation"); + //cerr << "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation" << endl; signal = link_budget - dbloss; return signal; } @@ -228,6 +249,8 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, else transmitter_height += Aircraft_HAAT; + SG_LOG(SG_GENERAL, SG_BULK, + "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters"); cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; unsigned int e_size = (deque::size_type)max_points; @@ -284,8 +307,9 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, } - // first Fresnel zone radius - // frequency in the middle of the bandplan, more accuracy is not necessary + /** first Fresnel zone radius + frequency in the middle of the bandplan, more accuracy is not necessary + */ double fz_clr= 8.657 * sqrt(distance_m / 0.125); // TODO: If we clear the first Fresnel zone, we are into line of sight territory @@ -304,10 +328,11 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, errnum); } - + SG_LOG(SG_GENERAL, SG_BULK, + "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum); cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; - //if (errnum == 4) + //if (errnum == 4) // if parameters are outside sane values for lrprop, the alternative method is used // return -1; signal = link_budget - dbloss; return signal; From 2c6946c3e8b446c2aa4e3f2bda6f8eb05e59c873 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 17:25:49 +0200 Subject: [PATCH 23/47] refactor receiveATC(), implement simple LOS routine, set comm1-signal property --- src/ATC/trafficcontrol.cxx | 2 +- src/Radio/radio.cxx | 106 ++++++++++++++++++++++++++++++++++--- src/Radio/radio.hxx | 6 ++- 3 files changed, 103 insertions(+), 11 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 2b0d20c67..cf0b16f31 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -762,7 +762,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, rec->getLatitude(), sender_alt ); } double frequency = ((double)stationFreq) / 100; - radio->receiveText(sender_pos, frequency, text, ground_to_air); + radio->receiveATC(sender_pos, frequency, text, ground_to_air); delete radio; } else { diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 9b3d0320f..260894655 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -34,14 +34,15 @@ FGRadio::FGRadio() { - /** radio parameters (which should probably be set for each radio via constructor) */ + /** radio parameters (which should probably be set for each radio) */ + _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD /** AM transmitter power in dBm. * Note this value is calculated from the typical final transistor stage output * small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) - * later store this value in aircraft description - * ATC comms usually operate high power equipment, thus making the link asymetrical; this is ignored for now + * later possibly store this value in aircraft description + * ATC comms usually operate high power equipment, thus making the link asymetrical; this is taken care of in propagation routines **/ _transmitter_power = 43.0; @@ -74,10 +75,16 @@ double FGRadio::getFrequency(int radio) { return freq; } +/*** TODO: receive multiplayer chat message and voice +***/ +void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, + int ground_to_air) { +} - -void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, +/*** Receive ATC radio communication as text +***/ +void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) { /* @@ -93,7 +100,19 @@ void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, fgSetString("/sim/messages/atc", text.c_str()); } else if ( _propagation_model == 1 ) { - // free space, round earth + // TODO: free space, round earth + double signal = LOS_calculate_attenuation(tx_pos, freq, ground_to_air); + if (signal <= 0.0) { + SG_LOG(SG_GENERAL, SG_BULK, "Signal below receiver minimum sensitivity: " << signal); + //cerr << "Signal below receiver minimum sensitivity: " << signal << endl; + return; + } + else { + SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); + //cerr << "Signal completely readable: " << signal << endl; + fgSetString("/sim/messages/atc", text.c_str()); + fgSetDouble("/sim/radio/comm1-signal", signal); + } } else if ( _propagation_model == 2 ) { // Use ITM propagation model @@ -126,12 +145,14 @@ void FGRadio::receiveText(SGGeod tx_pos, double freq, string text, //cerr << "Usable signal at limit: " << signal << endl; fgSetDouble("/sim/sound/voices/voice/volume", volume); fgSetString("/sim/messages/atc", text.c_str()); + fgSetDouble("/sim/radio/comm1-signal", signal); fgSetDouble("/sim/sound/voices/voice/volume", old_volume); } else { SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); //cerr << "Signal completely readable: " << signal << endl; fgSetString("/sim/messages/atc", text.c_str()); + fgSetDouble("/sim/radio/comm1-signal", signal); } } @@ -221,8 +242,8 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, if (own_alt > 8000) { dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; SG_LOG(SG_GENERAL, SG_BULK, - "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation"); - //cerr << "LOS-mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation" << endl; + "ITM Free-space mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation"); + //cerr << "ITM Free-space mode:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, free-space attenuation" << endl; signal = link_budget - dbloss; return signal; } @@ -338,3 +359,72 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, return signal; } + +/*** implement simple LOS propagation model (WIP) +***/ +double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, + int transmission_type) { + double frq_mhz; + if( (freq < 118.0) || (freq > 137.0) ) + frq_mhz = 125.0; // sane value, middle of bandplan + else + frq_mhz = freq; + double dbloss; + double tx_pow = _transmitter_power; + double ant_gain = _antenna_gain; + double signal = 0.0; + double ATC_HAAT = 30.0; + double Aircraft_HAAT = 5.0; + double sender_alt_ft,sender_alt; + double transmitter_height=0.0; + double receiver_height=0.0; + double own_lat = fgGetDouble("/position/latitude-deg"); + double own_lon = fgGetDouble("/position/longitude-deg"); + double own_alt_ft = fgGetDouble("/position/altitude-ft"); + double own_alt= own_alt_ft * SG_FEET_TO_METER; + + if(transmission_type == 1) + tx_pow = _transmitter_power + 6.0; + + if((transmission_type == 1) || (transmission_type == 3)) + ant_gain = _antenna_gain + 3.0; //pilot plane's antenna gain + ground station antenna gain + + double link_budget = tx_pow - _receiver_sensitivity + ant_gain; + + //cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; + + SGGeod own_pos = SGGeod::fromDegM( own_lon, own_lat, own_alt ); + + SGGeod sender_pos = pos; + + sender_alt_ft = sender_pos.getElevationFt(); + sender_alt = sender_alt_ft * SG_FEET_TO_METER; + + receiver_height = own_alt; + transmitter_height = sender_alt; + + double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); + + if(transmission_type == 1) + transmitter_height += ATC_HAAT; + else + transmitter_height += Aircraft_HAAT; + + /** radio horizon calculation with wave bending k=4/3 */ + double receiver_horizon = 4.12 * sqrt(receiver_height); + double transmitter_horizon = 4.12 * sqrt(transmitter_height); + double total_horizon = receiver_horizon + transmitter_horizon; + + if (distance_m > total_horizon) { + return -1; + } + + // free-space loss (distance calculation should be changed) + dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; + signal = link_budget - dbloss; + SG_LOG(SG_GENERAL, SG_BULK, + "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm "); + cerr << "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm " << endl; + return signal; + +} diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 4b2b67c85..378cfa37b 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -46,6 +46,7 @@ private: int _propagation_model; /// 0 none, 1 round Earth, 2 ITM double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); + double LOS_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); public: @@ -56,8 +57,9 @@ public: void setFrequency(double freq, int radio); double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; - // transmission_type: 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air - void receiveText(SGGeod tx_pos, double freq, string text, int transmission_type); + // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air + void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); + void receiveChat(SGGeod tx_pos, double freq, string text, int transmission_type); void setPropagationModel(int model) { _propagation_model = model; }; }; From ff89b811973455e62ed9a583405b70744431fec9 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 17:40:37 +0200 Subject: [PATCH 24/47] add function for navaid reception --- src/Radio/radio.cxx | 16 ++++++++++++++++ src/Radio/radio.hxx | 5 ++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 260894655..0b31647c0 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -82,6 +82,22 @@ void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, } +/*** TODO: receive navaid +***/ +double FGRadio::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { + + // need to implement transmitter power + if ( _propagation_model == 1) { + return LOS_calculate_attenuation(tx_pos, freq, 1); + } + else if ( _propagation_model == 2) { + return ITM_calculate_attenuation(tx_pos, freq, 1); + } + + return -1; + +} + /*** Receive ATC radio communication as text ***/ void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 378cfa37b..6f37a5201 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -57,10 +57,13 @@ public: void setFrequency(double freq, int radio); double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; + void setPropagationModel(int model) { _propagation_model = model; }; // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); void receiveChat(SGGeod tx_pos, double freq, string text, int transmission_type); - void setPropagationModel(int model) { _propagation_model = model; }; + // returns signal quality + // transmission_type: 0 for VOR, 1 for ILS + double receiveNav(SGGeod tx_pos, double freq, int transmission_type); }; From a551bf671c9333677f65d5638dc8deff354bdaed Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 18:17:13 +0200 Subject: [PATCH 25/47] check if tuned on frequency --- src/Radio/radio.cxx | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 0b31647c0..44306f3ab 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -77,8 +77,7 @@ double FGRadio::getFrequency(int radio) { /*** TODO: receive multiplayer chat message and voice ***/ -void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, - int ground_to_air) { +void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, int ground_to_air) { } @@ -100,18 +99,17 @@ double FGRadio::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { /*** Receive ATC radio communication as text ***/ -void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, - int ground_to_air) { +void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) { - /* + double comm1 = getFrequency(1); double comm2 = getFrequency(2); - if ( (freq != comm1) && (freq != comm2) ) { - cerr << "Frequency not tuned: " << freq << " Radio1: " << comm1 << " Radio2: " << comm2 << endl; + if ( !(fabs(freq - comm1) <= 0.0001) && !(fabs(freq - comm2) <= 0.0001) ) { + //cerr << "Frequency not tuned: " << freq << " Radio1: " << comm1 << " Radio2: " << comm2 << endl; return; } else { - */ + if ( _propagation_model == 0) { fgSetString("/sim/messages/atc", text.c_str()); } @@ -127,6 +125,9 @@ void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); //cerr << "Signal completely readable: " << signal << endl; fgSetString("/sim/messages/atc", text.c_str()); + /** write signal strength above threshold to the property tree + * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) + **/ fgSetDouble("/sim/radio/comm1-signal", signal); } } @@ -168,20 +169,22 @@ void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); //cerr << "Signal completely readable: " << signal << endl; fgSetString("/sim/messages/atc", text.c_str()); + /** write signal strength above threshold to the property tree + * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) + **/ fgSetDouble("/sim/radio/comm1-signal", signal); } } - //} + } } /*** Implement radio attenuation based on the Longley-Rice propagation model ***/ -double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, - int transmission_type) { +double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { @@ -378,8 +381,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, /*** implement simple LOS propagation model (WIP) ***/ -double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, - int transmission_type) { +double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { double frq_mhz; if( (freq < 118.0) || (freq > 137.0) ) frq_mhz = 125.0; // sane value, middle of bandplan From 7b101c3c70a3cd79d90e91688f3e663ce8ecfbb7 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 24 Nov 2011 18:29:17 +0200 Subject: [PATCH 26/47] cleanup --- src/Radio/radio.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 44306f3ab..e1b7ad9fe 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -291,7 +291,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss SG_LOG(SG_GENERAL, SG_BULK, "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters"); - cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; + //cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; unsigned int e_size = (deque::size_type)max_points; @@ -363,7 +363,6 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss } else { - point_to_point(itm_elev, transmitter_height, receiver_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, errnum); @@ -442,7 +441,7 @@ double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmiss signal = link_budget - dbloss; SG_LOG(SG_GENERAL, SG_BULK, "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm "); - cerr << "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm " << endl; + //cerr << "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm " << endl; return signal; } From b2e83c4dcc0b9c458e45afaba6ea51176a118a67 Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 26 Nov 2011 09:03:29 +0200 Subject: [PATCH 27/47] document transceiver specs --- src/Radio/radio.cxx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index e1b7ad9fe..fb73dffed 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -40,9 +40,15 @@ FGRadio::FGRadio() { /** AM transmitter power in dBm. * Note this value is calculated from the typical final transistor stage output - * small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) + * small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) others operate in the range 10-20 W * later possibly store this value in aircraft description * ATC comms usually operate high power equipment, thus making the link asymetrical; this is taken care of in propagation routines + * Typical output powers for ATC ground equipment, VHF-UHF: + * 40 dBm - 10 W (ground, clearance) + * 44 dBm - 20 W (tower) + * 47 dBm - 50 W (center, sectors) + * 50 dBm - 100 W (center, sectors) + * 53 dBm - 200 W (sectors, on directional arrays) **/ _transmitter_power = 43.0; @@ -85,7 +91,9 @@ void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, int ground_to ***/ double FGRadio::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { - // need to implement transmitter power + // typical VOR/LOC transmitter power appears to be 200 Watt ~ 53 dBm + // vor/loc typical sensitivity between -107 and -101 dBm + // glideslope sensitivity between -85 and -81 dBm if ( _propagation_model == 1) { return LOS_calculate_attenuation(tx_pos, freq, 1); } From dd6588d4f03f1a6bc365c92000427642731cfaa5 Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 26 Nov 2011 18:51:33 +0200 Subject: [PATCH 28/47] Use landcover to determine ground clutter path loss for LOS modified: src/Radio/itm.cpp modified: src/Radio/radio.cxx modified: src/Radio/radio.hxx --- src/Radio/itm.cpp | 32 +++++-- src/Radio/radio.cxx | 223 ++++++++++++++++++++++++++++++++++++++++++-- src/Radio/radio.hxx | 8 +- 3 files changed, 248 insertions(+), 15 deletions(-) diff --git a/src/Radio/itm.cpp b/src/Radio/itm.cpp index 9c2f010fc..79360a201 100644 --- a/src/Radio/itm.cpp +++ b/src/Radio/itm.cpp @@ -1519,6 +1519,8 @@ void point_to_point(double elev[], double rel, // 0.01 .. .99, Fractions of time double &dbloss, char *strmode, + int &p_mode, // propagation mode selector + double (&horizons)[2], // horizon distances int &errnum) { // radio_climate: 1-Equatorial, 2-Continental Subtropical, 3-Maritime Tropical, @@ -1568,22 +1570,38 @@ void point_to_point(double elev[], fs = 32.45 + 20.0 * log10(frq_mhz) + 20.0 * log10(prop.d / 1000.0); q = prop.d - prop.d_L; + horizons[0] = 0.0; + horizons[1] = 0.0; if (int(q) < 0.0) { strcpy(strmode, "Line-Of-Sight Mode"); + p_mode = 0; } else { - if (int(q) == 0.0) + if (int(q) == 0.0) { strcpy(strmode, "Single Horizon"); + horizons[0] = prop.d_Lj[0]; + p_mode = 1; + } - else - if (int(q) > 0.0) + else { + if (int(q) > 0.0) { strcpy(strmode, "Double Horizon"); + horizons[0] = prop.d_Lj[0]; + horizons[1] = prop.d_Lj[1]; + p_mode = 1; + } + } - if (prop.d <= prop.d_Ls || prop.d <= prop.dx) + if (prop.d <= prop.d_Ls || prop.d <= prop.dx) { strcat(strmode, ", Diffraction Dominant"); + p_mode = 1; + } - else - if (prop.d > prop.dx) - strcat(strmode, ", Troposcatter Dominant"); + else { + if (prop.d > prop.dx) { + strcat(strmode, ", Troposcatter Dominant"); + p_mode = 2; + } + } } dbloss = avar(zr, 0.0, zc, prop, propv) + fs; diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index fb73dffed..72fdb15d8 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -26,6 +26,7 @@ #include #include #include "radio.hxx" +#include #include #define WITH_POINT_TO_POINT 1 @@ -213,8 +214,11 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss double rel = 0.90; double dbloss; char strmode[150]; + int p_mode = 0; // propgation mode selector: 0 LOS, 1 diffraction dominant, 2 troposcatter + double horizons[2]; int errnum; + double clutter_loss; // loss due to vegetation and urban double tx_pow = _transmitter_power; double ant_gain = _antenna_gain; double signal = 0.0; @@ -278,6 +282,8 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss double max_points = distance_m / point_distance; deque _elevations; + deque materials; + double elevation_under_pilot = 0.0; if (scenery->get_elevation_m( max_own_pos, elevation_under_pilot, NULL )) { @@ -306,23 +312,39 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss while (_elevations.size() <= e_size) { probe_distance += point_distance; SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, probe_distance )); - + const SGMaterial *mat = 0; double elevation_m = 0.0; - if (scenery->get_elevation_m( probe, elevation_m, NULL )) { + if (scenery->get_elevation_m( probe, elevation_m, &mat )) { if((transmission_type == 3) || (transmission_type == 4)) { _elevations.push_back(elevation_m); + if(mat) { + const std::vector mat_names = mat->get_names(); + materials.push_back(mat_names[0]); + } + else { + materials.push_back("None"); + } } else { _elevations.push_front(elevation_m); + if(mat) { + const std::vector mat_names = mat->get_names(); + materials.push_front(mat_names[0]); + } + else { + materials.push_front("None"); + } } } else { if((transmission_type == 3) || (transmission_type == 4)) { - _elevations.push_back(elevation_m); + _elevations.push_back(0.0); + materials.push_back("None"); } else { - _elevations.push_front(0.0); + _elevations.push_front(0.0); + materials.push_front("None"); } } } @@ -367,25 +389,205 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss // the sender and receiver roles are switched point_to_point(itm_elev, receiver_height, transmitter_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, - pol, conf, rel, dbloss, strmode, errnum); + pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); } else { point_to_point(itm_elev, transmitter_height, receiver_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, - pol, conf, rel, dbloss, strmode, errnum); + pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); } SG_LOG(SG_GENERAL, SG_BULK, "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum); cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; + clutterLoss(frq_mhz, distance_m, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss); + cerr << "Clutter loss: " << clutter_loss << endl; //if (errnum == 4) // if parameters are outside sane values for lrprop, the alternative method is used // return -1; - signal = link_budget - dbloss; + signal = link_budget - dbloss - clutter_loss; return signal; } +/*** Calculate losses due to vegetation and urban clutter (WIP) +* We are only worried about clutter loss, terrain influence +* on the first Fresnel zone is calculated in the ITM functions +***/ +void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deque materials, + double transmitter_height, double receiver_height, int p_mode, + double horizons[], double &clutter_loss) { + + if (p_mode == 0) { // LOS: take each point and see how clutter height affects first Fresnel zone + int j=1; // first point is TX elevation, last is RX elevation + for (int k=3;k < (int)itm_elev[0];k++) { + + double clutter_height = 0.0; // clutter height hard-coded to 15 for now + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[j-1], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double d1 = j * itm_elev[1]; + if (fabs(min_elev - itm_elev[2]) <= 0.0001) + d1 = (itm_elev[0] - j) * itm_elev[1]; + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + clutter_loss +=0.0; + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad *2 ) ) * freq/100; + } + else { + clutter_loss += 0.0; + } + j++; + } + + } + else if (p_mode == 1) { // diffraction + + if (horizons[1] == 0.0) { // single horizon: same as above, except pass twice using the highest point + + } + else { // double horizon: same as single horizon, except there are 3 segments + + } + } + else if (p_mode == 2) { // troposcatter: use the first smooth earth horizon as mid point + + } + +} + +/*** Material properties database +* height: median clutter height +* density: radiowave attenuation factor +***/ +void FGRadio::get_material_properties(string mat_name, double &height, double &density) { + + if(mat_name == "Landmass") { + height = 15.0; + density = 0.2; + } + + else if(mat_name == "SomeSort") { + height = 15.0; + density = 0.2; + } + + else if(mat_name == "Island") { + height = 15.0; + density = 0.2; + } + else if(mat_name == "Default") { + height = 15.0; + density = 0.2; + } + else if(mat_name == "EvergreenBroadCover") { + height = 20.0; + density = 0.2; + } + else if(mat_name == "EvergreenForest") { + height = 20.0; + density = 0.2; + } + else if(mat_name == "DeciduousBroadCover") { + height = 15.0; + density = 0.3; + } + else if(mat_name == "DeciduousForest") { + height = 15.0; + density = 0.3; + } + else if(mat_name == "MixedForestCover") { + height = 20.0; + density = 0.25; + } + else if(mat_name == "MixedForest") { + height = 15.0; + density = 0.25; + } + else if(mat_name == "RainForest") { + height = 25.0; + density = 0.55; + } + else if(mat_name == "EvergreenNeedleCover") { + height = 15.0; + density = 0.2; + } + else if(mat_name == "WoodedTundraCover") { + height = 5.0; + density = 0.15; + } + else if(mat_name == "DeciduousNeedleCover") { + height = 5.0; + density = 0.2; + } + else if(mat_name == "ScrubCover") { + height = 3.0; + density = 0.15; + } + else if(mat_name == "BuiltUpCover") { + height = 30.0; + density = 0.7; + } + else if(mat_name == "Urban") { + height = 30.0; + density = 0.7; + } + else if(mat_name == "Construction") { + height = 30.0; + density = 0.7; + } + else if(mat_name == "Industrial") { + height = 30.0; + density = 0.7; + } + else if(mat_name == "Port") { + height = 30.0; + density = 0.7; + } + else if(mat_name == "Town") { + height = 10.0; + density = 0.5; + } + else if(mat_name == "SubUrban") { + height = 10.0; + density = 0.5; + } + else if(mat_name == "CropWoodCover") { + height = 10.0; + density = 0.1; + } + else if(mat_name == "CropWood") { + height = 10.0; + density = 0.1; + } + else if(mat_name == "AgroForest") { + height = 10.0; + density = 0.1; + } + else { + height = 0.0; + density = 0.0; + } + +} + /*** implement simple LOS propagation model (WIP) ***/ double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { @@ -453,3 +655,10 @@ double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmiss return signal; } + +/*** Material properties database +***/ +void FGRadio::set_material_properties() { + + +} diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 6f37a5201..d6cc77e82 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -23,7 +23,7 @@ #include #include - +#include #include
#include @@ -43,10 +43,16 @@ private: double _receiver_sensitivity; double _transmitter_power; double _antenna_gain; + std::map _mat_database; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); double LOS_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); + void clutterLoss(double freq, double distance_m, double itm_elev[], std::deque materials, + double transmitter_height, double receiver_height, int p_mode, + double horizons[], double &clutter_loss); + void set_material_properties(); + void get_material_properties(string mat_name, double &height, double &density); public: From 6b24aa0c14f92d268faead3cfe1dc3fd601898dd Mon Sep 17 00:00:00 2001 From: adrian Date: Sun, 27 Nov 2011 12:53:30 +0200 Subject: [PATCH 29/47] Add clutter loss for single horizon diffraction --- src/Radio/radio.cxx | 108 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 94 insertions(+), 14 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 72fdb15d8..314e40c63 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -376,15 +376,6 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss //cerr << "ITM:: itm_elev: " << _elevations[i] << endl; } - - /** first Fresnel zone radius - frequency in the middle of the bandplan, more accuracy is not necessary - */ - double fz_clr= 8.657 * sqrt(distance_m / 0.125); - - // TODO: If we clear the first Fresnel zone, we are into line of sight territory - - // else we need to calculate point to point link loss if((transmission_type == 3) || (transmission_type == 4)) { // the sender and receiver roles are switched point_to_point(itm_elev, receiver_height, transmitter_height, @@ -422,23 +413,25 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq int j=1; // first point is TX elevation, last is RX elevation for (int k=3;k < (int)itm_elev[0];k++) { - double clutter_height = 0.0; // clutter height hard-coded to 15 for now + double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[j-1], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); double d1 = j * itm_elev[1]; - if (fabs(min_elev - itm_elev[2]) <= 0.0001) + if ((itm_elev[2] + transmitter_height) > ( itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { d1 = (itm_elev[0] - j) * itm_elev[1]; + } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; - double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad; + cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); //cerr << "Clutter:: clearance: " << clearance << endl; if (clearance >= 0) { @@ -449,7 +442,7 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad *2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; } else { clutter_loss += 0.0; @@ -461,6 +454,93 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq else if (p_mode == 1) { // diffraction if (horizons[1] == 0.0) { // single horizon: same as above, except pass twice using the highest point + int num_points_1st = (int)floor( horizons[1] * (double)itm_elev[0] / distance_m ); + int num_points_2nd = (int)floor( (distance_m - horizons[1]) * (double)itm_elev[0] / distance_m ); + int last = 1; + /** perform the first pass */ + + int j=1; // first point is TX elevation, last is obstruction elevation + for (int k=3;k < num_points_1st ;k++) { + + double clutter_height = 0.0; // mean clutter height for a certain terrain type + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[j-1], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); + + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); + double d1 = j * itm_elev[1]; + if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 2] + clutter_height) ) { + d1 = (num_points_1st - j) * itm_elev[1]; + } + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + clutter_loss +=0.0; + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + } + else { + clutter_loss += 0.0; + } + j++; + last = k+1; + } + + /** and the second pass */ + + int l =1; + for (int k=last;k < num_points_2nd ;k++) { + + double clutter_height = 0.0; // mean clutter height for a certain terrain type + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[j-1], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (l * itm_elev[1] * (num_points_2nd - l) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); + + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double d1 = l * itm_elev[1]; + if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { + d1 = (num_points_2nd - l) * itm_elev[1]; + } + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + clutter_loss +=0.0; + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + } + else { + clutter_loss += 0.0; + } + j++; + } } else { // double horizon: same as single horizon, except there are 3 segments From 4c0c79fa4cb6a86d765382186b05542888a03b19 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 28 Nov 2011 07:37:37 +0200 Subject: [PATCH 30/47] Decouple material index from other variables --- src/Radio/radio.cxx | 210 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 176 insertions(+), 34 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 314e40c63..6f7a7601f 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -218,7 +218,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss double horizons[2]; int errnum; - double clutter_loss; // loss due to vegetation and urban + double clutter_loss = 0.0; // loss due to vegetation and urban double tx_pow = _transmitter_power; double ant_gain = _antenna_gain; double signal = 0.0; @@ -305,7 +305,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss SG_LOG(SG_GENERAL, SG_BULK, "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters"); - //cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; + cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; unsigned int e_size = (deque::size_type)max_points; @@ -410,32 +410,33 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq double horizons[], double &clutter_loss) { if (p_mode == 0) { // LOS: take each point and see how clutter height affects first Fresnel zone + int mat = 0; int j=1; // first point is TX elevation, last is RX elevation for (int k=3;k < (int)itm_elev[0];k++) { double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave - get_material_properties(materials[j-1], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); double d1 = j * itm_elev[1]; - if ((itm_elev[2] + transmitter_height) > ( itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { + if ((itm_elev[2] + transmitter_height) > ( itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { d1 = (itm_elev[0] - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); //cerr << "Clutter:: clearance: " << clearance << endl; if (clearance >= 0) { - clutter_loss +=0.0; + // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { @@ -445,37 +446,38 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; } else { - clutter_loss += 0.0; + // no losses } j++; + mat++; } } else if (p_mode == 1) { // diffraction if (horizons[1] == 0.0) { // single horizon: same as above, except pass twice using the highest point - int num_points_1st = (int)floor( horizons[1] * (double)itm_elev[0] / distance_m ); - int num_points_2nd = (int)floor( (distance_m - horizons[1]) * (double)itm_elev[0] / distance_m ); + int num_points_1st = (int)floor( horizons[0] * (double)itm_elev[0] / distance_m ); + int num_points_2nd = (int)floor( (distance_m - horizons[0]) * (double)itm_elev[0] / distance_m ); int last = 1; /** perform the first pass */ - - int j=1; // first point is TX elevation, last is obstruction elevation + int mat = 0; + int j=1; // first point is TX elevation, 2nd is obstruction elevation for (int k=3;k < num_points_1st ;k++) { double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave - get_material_properties(materials[j-1], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 1] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 1] + clutter_height); double d1 = j * itm_elev[1]; - if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 2] + clutter_height) ) { + if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 1] + clutter_height) ) { d1 = (num_points_1st - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; @@ -484,7 +486,7 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq double intrusion = fabs(clearance); //cerr << "Clutter:: clearance: " << clearance << endl; if (clearance >= 0) { - clutter_loss +=0.0; + // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { @@ -494,31 +496,32 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; } else { - clutter_loss += 0.0; + // no losses } j++; - last = k+1; + mat++; + last = k; } /** and the second pass */ - int l =1; - for (int k=last;k < num_points_2nd ;k++) { + int l =1; // first point is diffraction edge, 2nd the RX elevation + for (int k=last+1;k < num_points_2nd ;k++) { double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave - get_material_properties(materials[j-1], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[j-1] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (l * itm_elev[1] * (num_points_2nd - l) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); double d1 = l * itm_elev[1]; - if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { + if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { d1 = (num_points_2nd - l) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; @@ -527,7 +530,7 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq double intrusion = fabs(clearance); //cerr << "Clutter:: clearance: " << clearance << endl; if (clearance >= 0) { - clutter_loss +=0.0; + // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { @@ -537,23 +540,162 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; } else { - clutter_loss += 0.0; + // no losses } j++; + l++; + mat++; } } else { // double horizon: same as single horizon, except there are 3 segments + int num_points_1st = (int)floor( horizons[0] * (double)itm_elev[0] / distance_m ); + int num_points_2nd = (int)floor( (horizons[1] - horizons[0]) * (double)itm_elev[0] / distance_m ); + int num_points_3rd = (int)floor( (distance_m - horizons[1]) * (double)itm_elev[0] / distance_m ); + int last = 1; + /** perform the first pass */ + int mat = 0; + int j=1; // first point is TX elevation, 2nd is obstruction elevation + for (int k=3;k < num_points_1st ;k++) { + + double clutter_height = 0.0; // mean clutter height for a certain terrain type + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 1] + clutter_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); + + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 1] + clutter_height); + double d1 = j * itm_elev[1]; + if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 1] + clutter_height) ) { + d1 = (num_points_1st - j) * itm_elev[1]; + } + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + // no losses + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + } + else { + // no losses + } + j++; + last = k; + } + + /** and the second pass */ + + int l =1; // first point is 1st obstruction elevation, 2nd is 2nd obstruction elevation + for (int k=last;k < num_points_2nd ;k++) { + + double clutter_height = 0.0; // mean clutter height for a certain terrain type + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last] + clutter_height - itm_elev[num_points_1st + num_points_2nd + 1] + clutter_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (l * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); + + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height); + double d1 = l * itm_elev[1]; + if ( (itm_elev[last] + clutter_height) > (itm_elev[num_points_1st + num_points_2nd + 1] + clutter_height) ) { + d1 = (num_points_2nd - l) * itm_elev[1]; + } + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + // no losses + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + } + else { + // no losses + } + j++; + l++; + mat++; + last = k; + } + + /** third and final pass */ + + int m =1; // first point is 2nd obstruction elevation, 3rd is RX elevation + for (int k=last;k < num_points_3rd ;k++) { + + double clutter_height = 0.0; // mean clutter height for a certain terrain type + double clutter_density = 0.0; // percent of reflected wave + get_material_properties(materials[mat], clutter_height, clutter_density); + //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; + // First Fresnel radius + double frs_rad = 548 * sqrt( (m * itm_elev[1] * (num_points_3rd - m) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); + + //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 + + double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); + double d1 = m * itm_elev[1]; + if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { + d1 = (num_points_3rd - m) * itm_elev[1]; + } + double ray_height = (grad * d1) + min_elev; + //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; + double intrusion = fabs(clearance); + //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { + // no losses + } + else if (clearance < 0 && (intrusion < clutter_height)) { + + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + } + else if (clearance < 0 && (intrusion > clutter_height)) { + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + } + else { + // no losses + } + j++; + m++; + mat++; + last = k+1; + } + } } - else if (p_mode == 2) { // troposcatter: use the first smooth earth horizon as mid point - + else if (p_mode == 2) { // troposcatter: ignore ground clutter for now... + clutter_loss = 0.0; } } -/*** Material properties database +/*** Temporary material properties database * height: median clutter height * density: radiowave attenuation factor ***/ From d23fbc3c134b8d417056ca7abaf6f950a194a964 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 28 Nov 2011 08:31:04 +0200 Subject: [PATCH 31/47] make clutter loss calculations switchable via property --- src/Radio/radio.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 6f7a7601f..f9b415067 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -381,18 +381,20 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss point_to_point(itm_elev, receiver_height, transmitter_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); - + if( fgGetBool( "/sim/radio/use-clutter-attenuation", false ) ) + clutterLoss(frq_mhz, distance_m, itm_elev, materials, receiver_height, transmitter_height, p_mode, horizons, clutter_loss); } else { point_to_point(itm_elev, transmitter_height, receiver_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); + if( fgGetBool( "/sim/radio/use-clutter-attenuation", false ) ) + clutterLoss(frq_mhz, distance_m, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss); } SG_LOG(SG_GENERAL, SG_BULK, "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum); cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; - clutterLoss(frq_mhz, distance_m, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss); cerr << "Clutter loss: " << clutter_loss << endl; //if (errnum == 4) // if parameters are outside sane values for lrprop, the alternative method is used // return -1; From 6be68f475daad1e663396dc199950a82357e9f16 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 28 Nov 2011 08:36:56 +0200 Subject: [PATCH 32/47] remove unnecessary function --- src/Radio/radio.cxx | 7 +------ src/Radio/radio.hxx | 1 - 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index f9b415067..6f056a297 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -880,9 +880,4 @@ double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmiss } -/*** Material properties database -***/ -void FGRadio::set_material_properties() { - - -} + diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index d6cc77e82..2e67c5aef 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -51,7 +51,6 @@ private: void clutterLoss(double freq, double distance_m, double itm_elev[], std::deque materials, double transmitter_height, double receiver_height, int p_mode, double horizons[], double &clutter_loss); - void set_material_properties(); void get_material_properties(string mat_name, double &height, double &density); public: From a6b9beca9d9770c7589d4e1415afb750ce30c514 Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 28 Nov 2011 10:38:58 +0200 Subject: [PATCH 33/47] rename FGRadio to FGRadioTransmission, add RX and TX antenna heights --- src/ATC/trafficcontrol.cxx | 2 +- src/Radio/radio.cxx | 22 +++++++++++++--------- src/Radio/radio.hxx | 7 ++++++- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index cf0b16f31..e42def60d 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -746,7 +746,7 @@ void FGATCController::transmit(FGTrafficRecord * rec, FGAirportDynamics *parent, if( fgGetBool( "/sim/radio/use-itm-attenuation", false ) ) { //cerr << "Using ITM radio propagation" << endl; - FGRadio* radio = new FGRadio(); + FGRadioTransmission* radio = new FGRadioTransmission(); SGGeod sender_pos; double sender_alt_ft, sender_alt; if(ground_to_air) { diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 6f056a297..ac278bf2f 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -33,7 +33,7 @@ #include "itm.cpp" -FGRadio::FGRadio() { +FGRadioTransmission::FGRadioTransmission() { /** radio parameters (which should probably be set for each radio) */ @@ -53,6 +53,10 @@ FGRadio::FGRadio() { **/ _transmitter_power = 43.0; + _tx_antenna_height = 2.0; // TX antenna height above ground level + + _rx_antenna_height = 2.0; // RX antenna height above ground level + /** pilot plane's antenna gain + AI aircraft antenna gain * real-life gain for conventional monopole/dipole antenna **/ @@ -66,7 +70,7 @@ FGRadio::~FGRadio() } -double FGRadio::getFrequency(int radio) { +double FGRadioTransmission::getFrequency(int radio) { double freq = 118.0; switch (radio) { case 1: @@ -84,13 +88,13 @@ double FGRadio::getFrequency(int radio) { /*** TODO: receive multiplayer chat message and voice ***/ -void FGRadio::receiveChat(SGGeod tx_pos, double freq, string text, int ground_to_air) { +void FGRadioTransmission::receiveChat(SGGeod tx_pos, double freq, string text, int ground_to_air) { } /*** TODO: receive navaid ***/ -double FGRadio::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { +double FGRadioTransmission::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { // typical VOR/LOC transmitter power appears to be 200 Watt ~ 53 dBm // vor/loc typical sensitivity between -107 and -101 dBm @@ -108,7 +112,7 @@ double FGRadio::receiveNav(SGGeod tx_pos, double freq, int transmission_type) { /*** Receive ATC radio communication as text ***/ -void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) { +void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) { double comm1 = getFrequency(1); @@ -193,7 +197,7 @@ void FGRadio::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_ /*** Implement radio attenuation based on the Longley-Rice propagation model ***/ -double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { +double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { @@ -407,7 +411,7 @@ double FGRadio::ITM_calculate_attenuation(SGGeod pos, double freq, int transmiss * We are only worried about clutter loss, terrain influence * on the first Fresnel zone is calculated in the ITM functions ***/ -void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deque materials, +void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm_elev[], deque materials, double transmitter_height, double receiver_height, int p_mode, double horizons[], double &clutter_loss) { @@ -701,7 +705,7 @@ void FGRadio::clutterLoss(double freq, double distance_m, double itm_elev[], deq * height: median clutter height * density: radiowave attenuation factor ***/ -void FGRadio::get_material_properties(string mat_name, double &height, double &density) { +void FGRadioTransmission::get_material_properties(string mat_name, double &height, double &density) { if(mat_name == "Landmass") { height = 15.0; @@ -814,7 +818,7 @@ void FGRadio::get_material_properties(string mat_name, double &height, double &d /*** implement simple LOS propagation model (WIP) ***/ -double FGRadio::LOS_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { +double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, int transmission_type) { double frq_mhz; if( (freq < 118.0) || (freq > 137.0) ) frq_mhz = 125.0; // sane value, middle of bandplan diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 2e67c5aef..25d4144cf 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -33,7 +33,7 @@ using std::string; -class FGRadio +class FGRadioTransmission { private: bool isOperable() const @@ -42,6 +42,8 @@ private: double _receiver_sensitivity; double _transmitter_power; + double _tx_antenna_height; + double _rx_antenna_height; double _antenna_gain; std::map _mat_database; @@ -62,6 +64,9 @@ public: void setFrequency(double freq, int radio); double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; + void setRxSensitivity(double sensitivity) { _receiver_sensitivity = sensitivity; }; + void setTxAntennaHeight(double tx_antenna_height) { _tx_antenna_height = tx_antenna_height; }; + void setRxAntennaHeight(double rx_antenna_height) { _rx_antenna_height = rx_antenna_height; }; void setPropagationModel(int model) { _propagation_model = model; }; // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); From d988e4c4ad2366a8eb6354d8a4973f8f12ea0fbe Mon Sep 17 00:00:00 2001 From: adrian Date: Mon, 28 Nov 2011 10:49:05 +0200 Subject: [PATCH 34/47] forgot about constructor and destructor --- src/Radio/radio.cxx | 2 +- src/Radio/radio.hxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index ac278bf2f..62e023178 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -65,7 +65,7 @@ FGRadioTransmission::FGRadioTransmission() { } -FGRadio::~FGRadio() +FGRadioTransmission::~FGRadioTransmission() { } diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 25d4144cf..da84313fc 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -57,8 +57,8 @@ private: public: - FGRadio(); - ~FGRadio(); + FGRadioTransmission(); + ~FGRadioTransmission(); void setFrequency(double freq, int radio); From dcc915e5bd8bfe0272182c0084bcc321467d19cf Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 29 Nov 2011 15:00:49 +0200 Subject: [PATCH 35/47] Add separate fields for receiver and transmitter: - antenna gain and cable losses will be set individualy - Fix the calculations of the number of elevation points - Improve clutter loss calculations to yield sane results --- src/Radio/radio.cxx | 155 +++++++++++++++++++++++++------------------- src/Radio/radio.hxx | 5 ++ 2 files changed, 92 insertions(+), 68 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 62e023178..3ea219ff7 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -23,6 +23,7 @@ #endif #include +#include #include #include #include "radio.hxx" @@ -61,8 +62,15 @@ FGRadioTransmission::FGRadioTransmission() { * real-life gain for conventional monopole/dipole antenna **/ _antenna_gain = 2.0; - _propagation_model = 2; // choose between models via option: realistic radio on/off + _rx_antenna_gain = 1.0; + _tx_antenna_gain = 1.0; + + _rx_line_losses = 2.0; // to be configured for each station + _tx_line_losses = 2.0; + + _propagation_model = 2; // choose between models via option: realistic radio on/off + _terrain_sampling_distance = 90.0; // regular SRTM is 90 meters } FGRadioTransmission::~FGRadioTransmission() @@ -266,7 +274,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i SGGeoc sender_pos_c = SGGeoc::fromGeod( sender_pos ); //cerr << "ITM:: sender Lat: " << parent->getLatitude() << ", Lon: " << parent->getLongitude() << ", Alt: " << sender_alt << endl; - double point_distance= 90.0; // regular SRTM is 90 meters + double point_distance= _terrain_sampling_distance; double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c); double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); double probe_distance = 0.0; @@ -284,7 +292,9 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i } - double max_points = distance_m / point_distance; + int max_points = (int)floor(distance_m / point_distance); + double delta_last = fmod(distance_m, point_distance); + deque _elevations; deque materials; @@ -354,11 +364,13 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i } if((transmission_type == 3) || (transmission_type == 4)) { _elevations.push_front(elevation_under_pilot); - _elevations.push_back(elevation_under_sender); + if (delta_last > (point_distance / 2) ) // only add last point if it's farther than half point_distance + _elevations.push_back(elevation_under_sender); } else { _elevations.push_back(elevation_under_pilot); - _elevations.push_front(elevation_under_sender); + if (delta_last > (point_distance / 2) ) + _elevations.push_front(elevation_under_sender); } @@ -415,25 +427,26 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double transmitter_height, double receiver_height, int p_mode, double horizons[], double &clutter_loss) { + distance_m = itm_elev[0] * itm_elev[1]; // only consider elevation points if (p_mode == 0) { // LOS: take each point and see how clutter height affects first Fresnel zone int mat = 0; int j=1; // first point is TX elevation, last is RX elevation - for (int k=3;k < (int)itm_elev[0];k++) { + for (int k=3;k < (int)(itm_elev[0]) + 2;k++) { double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); - + assert(frs_rad > 0); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); double d1 = j * itm_elev[1]; - if ((itm_elev[2] + transmitter_height) > ( itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { + if ((itm_elev[2] + transmitter_height) > ( itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { d1 = (itm_elev[0] - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; @@ -462,28 +475,30 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm else if (p_mode == 1) { // diffraction if (horizons[1] == 0.0) { // single horizon: same as above, except pass twice using the highest point - int num_points_1st = (int)floor( horizons[0] * (double)itm_elev[0] / distance_m ); - int num_points_2nd = (int)floor( (distance_m - horizons[0]) * (double)itm_elev[0] / distance_m ); + int num_points_1st = (int)floor( horizons[0] * itm_elev[0]/ distance_m ); + int num_points_2nd = (int)ceil( (distance_m - horizons[0]) * itm_elev[0] / distance_m ); + cerr << "Diffraction 1 horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << endl; int last = 1; /** perform the first pass */ int mat = 0; int j=1; // first point is TX elevation, 2nd is obstruction elevation - for (int k=3;k < num_points_1st ;k++) { - + for (int k=3;k < num_points_1st + 2;k++) { + if (num_points_1st < 1) + break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 1] + clutter_height) / distance_m; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius - double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); - + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); + assert(frs_rad > 0); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 1] + clutter_height); + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); double d1 = j * itm_elev[1]; - if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 1] + clutter_height) ) { + if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 2] + clutter_height) ) { d1 = (num_points_1st - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; @@ -510,25 +525,26 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm } /** and the second pass */ - - int l =1; // first point is diffraction edge, 2nd the RX elevation - for (int k=last+1;k < num_points_2nd ;k++) { - + mat +=1; + j =1; // first point is diffraction edge, 2nd the RX elevation + for (int k=last+2;k < (int)(itm_elev[0]) + 2;k++) { + if (num_points_2nd < 1) + break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; + double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius - double frs_rad = 548 * sqrt( (l * itm_elev[1] * (num_points_2nd - l) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); + assert(frs_rad > 0); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); - double d1 = l * itm_elev[1]; - if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { - d1 = (num_points_2nd - l) * itm_elev[1]; + double min_elev = SGMiscd::min(itm_elev[last+1] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double d1 = j * itm_elev[1]; + if ( (itm_elev[last+1] + clutter_height) > (itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { + d1 = (num_points_2nd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; @@ -549,36 +565,38 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm // no losses } j++; - l++; mat++; } } else { // double horizon: same as single horizon, except there are 3 segments - int num_points_1st = (int)floor( horizons[0] * (double)itm_elev[0] / distance_m ); - int num_points_2nd = (int)floor( (horizons[1] - horizons[0]) * (double)itm_elev[0] / distance_m ); - int num_points_3rd = (int)floor( (distance_m - horizons[1]) * (double)itm_elev[0] / distance_m ); + int num_points_1st = (int)floor( horizons[0] * itm_elev[0] / distance_m ); + int num_points_2nd = (int)ceil( (horizons[1] - horizons[0]) * itm_elev[0] / distance_m ); + int num_points_3rd = (int)itm_elev[0] - num_points_1st - num_points_2nd; + + cerr << "Clutter:: points1: " << num_points_1st << " points2: " << num_points_2nd << " points3: " << num_points_3rd << endl; int last = 1; /** perform the first pass */ int mat = 0; int j=1; // first point is TX elevation, 2nd is obstruction elevation - for (int k=3;k < num_points_1st ;k++) { - + for (int k=3;k < num_points_1st +2;k++) { + if (num_points_1st < 1) + break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 1] + clutter_height) / distance_m; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); - + assert(frs_rad > 0); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 1] + clutter_height); + double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); double d1 = j * itm_elev[1]; - if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 1] + clutter_height) ) { + if ( (itm_elev[2] + transmitter_height) > (itm_elev[num_points_1st + 2] + clutter_height) ) { d1 = (num_points_1st - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; @@ -602,27 +620,28 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm j++; last = k; } - + mat +=1; /** and the second pass */ - - int l =1; // first point is 1st obstruction elevation, 2nd is 2nd obstruction elevation - for (int k=last;k < num_points_2nd ;k++) { - + int last2=1; + j =1; // first point is 1st obstruction elevation, 2nd is 2nd obstruction elevation + for (int k=last+2;k < num_points_1st + num_points_2nd +2;k++) { + if (num_points_2nd < 1) + break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[last] + clutter_height - itm_elev[num_points_1st + num_points_2nd + 1] + clutter_height) / distance_m; + double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height) / distance_m; // First Fresnel radius - double frs_rad = 548 * sqrt( (l * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); + //cerr << "Clutter:: fresnel radius: " << frs_rad << " points2: " << num_points_2nd << " j: " << j << endl; + assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height); - double d1 = l * itm_elev[1]; - if ( (itm_elev[last] + clutter_height) > (itm_elev[num_points_1st + num_points_2nd + 1] + clutter_height) ) { - d1 = (num_points_2nd - l) * itm_elev[1]; + double min_elev = SGMiscd::min(itm_elev[last+1] + clutter_height, itm_elev[num_points_1st + num_points_2nd +2] + clutter_height); + double d1 = j * itm_elev[1]; + if ( (itm_elev[last+1] + clutter_height) > (itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height) ) { + d1 = (num_points_2nd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; @@ -643,31 +662,32 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm // no losses } j++; - l++; mat++; - last = k; + last2 = k; } /** third and final pass */ - - int m =1; // first point is 2nd obstruction elevation, 3rd is RX elevation - for (int k=last;k < num_points_3rd ;k++) { - + mat +=1; + j =1; // first point is 2nd obstruction elevation, 3rd is RX elevation + for (int k=last2+2;k < (int)itm_elev[0] + 2;k++) { + if (num_points_3rd < 1) + break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; - double grad = fabs(itm_elev[last] + clutter_height - itm_elev[(int)itm_elev[0] + 1] + receiver_height) / distance_m; + double grad = fabs(itm_elev[last2+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius - double frs_rad = 548 * sqrt( (m * itm_elev[1] * (num_points_3rd - m) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); - + double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_3rd - j) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); + cerr << "Clutter:: fresnel radius: " << frs_rad << " points2: " << num_points_3rd << " j: " << j << endl; + assert(frs_rad > 0); //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 - double min_elev = SGMiscd::min(itm_elev[last] + clutter_height, itm_elev[(int)itm_elev[0] + 1] + receiver_height); - double d1 = m * itm_elev[1]; - if ( (itm_elev[last] + clutter_height) > (itm_elev[(int)itm_elev[0] + 1] + receiver_height) ) { - d1 = (num_points_3rd - m) * itm_elev[1]; + double min_elev = SGMiscd::min(itm_elev[last2+1] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); + double d1 = j * itm_elev[1]; + if ( (itm_elev[last2+1] + clutter_height) > (itm_elev[(int)itm_elev[0] + 2] + receiver_height) ) { + d1 = (num_points_3rd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; @@ -688,9 +708,8 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm // no losses } j++; - m++; mat++; - last = k+1; + } } diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index da84313fc..4c2bb7f64 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -45,6 +45,11 @@ private: double _tx_antenna_height; double _rx_antenna_height; double _antenna_gain; + double _rx_antenna_gain; + double _tx_antenna_gain; + double _rx_line_losses; + double _tx_line_losses; + double _terrain_sampling_distance; std::map _mat_database; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM From 8928e0c415da5c259b12b5713e314a1b4b5d3bec Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 29 Nov 2011 16:15:06 +0200 Subject: [PATCH 36/47] Clutter loss doesn't depend anymore on sampling distance Also, fix double horizon diffraction, the second horizon is relative to the first horizon, not to the beginning of the path. --- src/Radio/radio.cxx | 87 +++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 43 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 3ea219ff7..a16dee5e4 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -70,7 +70,7 @@ FGRadioTransmission::FGRadioTransmission() { _tx_line_losses = 2.0; _propagation_model = 2; // choose between models via option: realistic radio on/off - _terrain_sampling_distance = 90.0; // regular SRTM is 90 meters + _terrain_sampling_distance = fgGetDouble("/sim/radio/sampling-distance", 90.0); // regular SRTM is 90 meters } FGRadioTransmission::~FGRadioTransmission() @@ -428,20 +428,21 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double horizons[], double &clutter_loss) { distance_m = itm_elev[0] * itm_elev[1]; // only consider elevation points + if (p_mode == 0) { // LOS: take each point and see how clutter height affects first Fresnel zone int mat = 0; - int j=1; // first point is TX elevation, last is RX elevation + int j=1; for (int k=3;k < (int)(itm_elev[0]) + 2;k++) { double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); assert(frs_rad > 0); - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); @@ -450,19 +451,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (itm_elev[0] - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses @@ -481,19 +482,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm int last = 1; /** perform the first pass */ int mat = 0; - int j=1; // first point is TX elevation, 2nd is obstruction elevation + int j=1; for (int k=3;k < num_points_1st + 2;k++) { if (num_points_1st < 1) break; double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); assert(frs_rad > 0); - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); @@ -502,19 +503,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (num_points_1st - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses @@ -533,12 +534,12 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); assert(frs_rad > 0); - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[last+1] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); @@ -547,19 +548,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (num_points_2nd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses @@ -572,10 +573,10 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm else { // double horizon: same as single horizon, except there are 3 segments int num_points_1st = (int)floor( horizons[0] * itm_elev[0] / distance_m ); - int num_points_2nd = (int)ceil( (horizons[1] - horizons[0]) * itm_elev[0] / distance_m ); + int num_points_2nd = (int)floor(horizons[1] * itm_elev[0] / distance_m ); int num_points_3rd = (int)itm_elev[0] - num_points_1st - num_points_2nd; - - cerr << "Clutter:: points1: " << num_points_1st << " points2: " << num_points_2nd << " points3: " << num_points_3rd << endl; + cerr << "Double horizon:: horizon1: " << horizons[0] << " horizon2: " << horizons[1] << " distance: " << distance_m << endl; + cerr << "Double horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << " points3: " << num_points_3rd << endl; int last = 1; /** perform the first pass */ int mat = 0; @@ -586,12 +587,12 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); assert(frs_rad > 0); - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[2] + transmitter_height, itm_elev[num_points_1st + 2] + clutter_height); @@ -600,19 +601,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (num_points_1st - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses @@ -630,11 +631,11 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - //cerr << "Clutter:: fresnel radius: " << frs_rad << " points2: " << num_points_2nd << " j: " << j << endl; + //cerr << "Double horizon second pass:: fresnel radius: " << frs_rad << " points2: " << num_points_2nd << " j: " << j << endl; assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -644,19 +645,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (num_points_2nd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses @@ -675,13 +676,13 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double clutter_height = 0.0; // mean clutter height for a certain terrain type double clutter_density = 0.0; // percent of reflected wave get_material_properties(materials[mat], clutter_height, clutter_density); - //cerr << "Clutter:: material: " << materials[mat] << " height: " << clutter_height << ", density: " << clutter_density << endl; + double grad = fabs(itm_elev[last2+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_3rd - j) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); - cerr << "Clutter:: fresnel radius: " << frs_rad << " points2: " << num_points_3rd << " j: " << j << endl; + //cerr << "Double horizon third pass:: fresnel radius: " << frs_rad << " points3: " << num_points_3rd << " j: " << j << endl; assert(frs_rad > 0); - //cerr << "Clutter:: fresnel radius: " << frs_rad << endl; + //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[last2+1] + clutter_height, itm_elev[(int)itm_elev[0] + 2] + receiver_height); @@ -690,19 +691,19 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm d1 = (num_points_3rd - j) * itm_elev[1]; } double ray_height = (grad * d1) + min_elev; - //cerr << "Clutter:: ray height: " << ray_height << " ground height:" << itm_elev[k] << endl; + double clearance = ray_height - (itm_elev[k] + clutter_height) - frs_rad * 8/10; double intrusion = fabs(clearance); - //cerr << "Clutter:: clearance: " << clearance << endl; + if (clearance >= 0) { // no losses } else if (clearance < 0 && (intrusion < clutter_height)) { - clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * freq/100; + clutter_loss += clutter_density * (intrusion / (frs_rad * 2) ) * (freq/100) * (itm_elev[1]/100); } else if (clearance < 0 && (intrusion > clutter_height)) { - clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * freq/100; + clutter_loss += clutter_density * (clutter_height / (frs_rad * 2 ) ) * (freq/100) * (itm_elev[1]/100); } else { // no losses From 0c66ca67852a87c47046edfedbf2978bc024ba91 Mon Sep 17 00:00:00 2001 From: adrian Date: Tue, 29 Nov 2011 16:38:02 +0200 Subject: [PATCH 37/47] remove debugging code --- src/Radio/radio.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index a16dee5e4..994f3aee9 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -23,6 +23,7 @@ #endif #include +#define NDEBUG #include #include #include @@ -478,7 +479,7 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm if (horizons[1] == 0.0) { // single horizon: same as above, except pass twice using the highest point int num_points_1st = (int)floor( horizons[0] * itm_elev[0]/ distance_m ); int num_points_2nd = (int)ceil( (distance_m - horizons[0]) * itm_elev[0] / distance_m ); - cerr << "Diffraction 1 horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << endl; + //cerr << "Diffraction 1 horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << endl; int last = 1; /** perform the first pass */ int mat = 0; @@ -575,8 +576,8 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm int num_points_1st = (int)floor( horizons[0] * itm_elev[0] / distance_m ); int num_points_2nd = (int)floor(horizons[1] * itm_elev[0] / distance_m ); int num_points_3rd = (int)itm_elev[0] - num_points_1st - num_points_2nd; - cerr << "Double horizon:: horizon1: " << horizons[0] << " horizon2: " << horizons[1] << " distance: " << distance_m << endl; - cerr << "Double horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << " points3: " << num_points_3rd << endl; + //cerr << "Double horizon:: horizon1: " << horizons[0] << " horizon2: " << horizons[1] << " distance: " << distance_m << endl; + //cerr << "Double horizon:: points1: " << num_points_1st << " points2: " << num_points_2nd << " points3: " << num_points_3rd << endl; int last = 1; /** perform the first pass */ int mat = 0; @@ -635,7 +636,7 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[num_points_1st + num_points_2nd + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - //cerr << "Double horizon second pass:: fresnel radius: " << frs_rad << " points2: " << num_points_2nd << " j: " << j << endl; + assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -680,7 +681,7 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[last2+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_3rd - j) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); - //cerr << "Double horizon third pass:: fresnel radius: " << frs_rad << " points3: " << num_points_3rd << " j: " << j << endl; + assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 From 1c1e954de22a775dfa72ce1c9066e3637783b340 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 1 Dec 2011 13:34:34 +0200 Subject: [PATCH 38/47] Remove hard-coded values wherever possible; Make most of the station parameters configurable outside the attenuation functions with the ultimate goal of exposing them via the property tree --- src/Radio/radio.cxx | 86 ++++++++++++++------------------------------- src/Radio/radio.hxx | 5 ++- 2 files changed, 31 insertions(+), 60 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 994f3aee9..4f2772e4e 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -23,8 +23,7 @@ #endif #include -#define NDEBUG -#include + #include #include #include "radio.hxx" @@ -37,15 +36,10 @@ FGRadioTransmission::FGRadioTransmission() { - /** radio parameters (which should probably be set for each radio) */ _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD /** AM transmitter power in dBm. - * Note this value is calculated from the typical final transistor stage output - * small aircraft have portable transmitters which operate at 36 dBm output (4 Watts) others operate in the range 10-20 W - * later possibly store this value in aircraft description - * ATC comms usually operate high power equipment, thus making the link asymetrical; this is taken care of in propagation routines * Typical output powers for ATC ground equipment, VHF-UHF: * 40 dBm - 10 W (ground, clearance) * 44 dBm - 20 W (tower) @@ -59,18 +53,14 @@ FGRadioTransmission::FGRadioTransmission() { _rx_antenna_height = 2.0; // RX antenna height above ground level - /** pilot plane's antenna gain + AI aircraft antenna gain - * real-life gain for conventional monopole/dipole antenna - **/ - _antenna_gain = 2.0; - _rx_antenna_gain = 1.0; + _rx_antenna_gain = 1.0; // gain expressed in dBi _tx_antenna_gain = 1.0; _rx_line_losses = 2.0; // to be configured for each station _tx_line_losses = 2.0; - _propagation_model = 2; // choose between models via option: realistic radio on/off + _propagation_model = 2; _terrain_sampling_distance = fgGetDouble("/sim/radio/sampling-distance", 90.0); // regular SRTM is 90 meters } @@ -124,10 +114,16 @@ double FGRadioTransmission::receiveNav(SGGeod tx_pos, double freq, int transmiss void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) { + if(ground_to_air == 1) { + _transmitter_power += 6.0; + _tx_antenna_height += 30.0; + _tx_antenna_gain += 3.0; + } + + double comm1 = getFrequency(1); double comm2 = getFrequency(2); if ( !(fabs(freq - comm1) <= 0.0001) && !(fabs(freq - comm2) <= 0.0001) ) { - //cerr << "Frequency not tuned: " << freq << " Radio1: " << comm1 << " Radio2: " << comm2 << endl; return; } else { @@ -139,13 +135,10 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in // TODO: free space, round earth double signal = LOS_calculate_attenuation(tx_pos, freq, ground_to_air); if (signal <= 0.0) { - SG_LOG(SG_GENERAL, SG_BULK, "Signal below receiver minimum sensitivity: " << signal); - //cerr << "Signal below receiver minimum sensitivity: " << signal << endl; return; } else { - SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); - //cerr << "Signal completely readable: " << signal << endl; + fgSetString("/sim/messages/atc", text.c_str()); /** write signal strength above threshold to the property tree * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) @@ -157,8 +150,6 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in // Use ITM propagation model double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air); if (signal <= 0.0) { - SG_LOG(SG_GENERAL, SG_BULK, "Signal below receiver minimum sensitivity: " << signal); - //cerr << "Signal below receiver minimum sensitivity: " << signal << endl; return; } if ((signal > 0.0) && (signal < 12.0)) { @@ -188,8 +179,6 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in fgSetDouble("/sim/sound/voices/voice/volume", old_volume); } else { - SG_LOG(SG_GENERAL, SG_BULK, "Signal completely readable: " << signal); - //cerr << "Signal completely readable: " << signal << endl; fgSetString("/sim/messages/atc", text.c_str()); /** write signal strength above threshold to the property tree * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) @@ -233,16 +222,11 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i double clutter_loss = 0.0; // loss due to vegetation and urban double tx_pow = _transmitter_power; - double ant_gain = _antenna_gain; + double ant_gain = _rx_antenna_gain + _tx_antenna_gain; double signal = 0.0; - if(transmission_type == 1) - tx_pow = _transmitter_power + 6.0; - - if((transmission_type == 1) || (transmission_type == 3)) - ant_gain = _antenna_gain + 3.0; //pilot plane's antenna gain + ground station antenna gain - double link_budget = tx_pow - _receiver_sensitivity + ant_gain; + double link_budget = tx_pow - _receiver_sensitivity - _rx_line_losses - _tx_line_losses + ant_gain; FGScenery * scenery = globals->get_scenery(); @@ -259,11 +243,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i SGGeoc center = SGGeoc::fromGeod( max_own_pos ); SGGeoc own_pos_c = SGGeoc::fromGeod( own_pos ); - /** position of sender radio antenna (HAAT) - sender can be aircraft or ground station - **/ - double ATC_HAAT = 30.0; - double Aircraft_HAAT = 5.0; + double sender_alt_ft,sender_alt; double transmitter_height=0.0; double receiver_height=0.0; @@ -302,7 +282,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i double elevation_under_pilot = 0.0; if (scenery->get_elevation_m( max_own_pos, elevation_under_pilot, NULL )) { - receiver_height = own_alt - elevation_under_pilot + 3; //assume antenna located 3 meters above ground + receiver_height = own_alt - elevation_under_pilot; } double elevation_under_sender = 0.0; @@ -313,10 +293,10 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i transmitter_height = sender_alt; } - if(transmission_type == 1) - transmitter_height += ATC_HAAT; - else - transmitter_height += Aircraft_HAAT; + + transmitter_height += _tx_antenna_height; + receiver_height += _rx_antenna_height; + SG_LOG(SG_GENERAL, SG_BULK, "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters"); @@ -383,7 +363,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i } double num_points= (double)_elevations.size(); - //cerr << "ITM:: Max alt between: " << max_alt_between << ", num points:" << num_points << endl; + _elevations.push_front(point_distance); _elevations.push_front(num_points -1); int size = _elevations.size(); @@ -442,7 +422,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (itm_elev[0] - j) * itm_elev[1] / 1000000) / ( distance_m * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -494,7 +473,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -539,7 +517,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[last+1] + clutter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -592,7 +569,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm double grad = fabs(itm_elev[2] + transmitter_height - itm_elev[num_points_1st + 2] + clutter_height) / distance_m; // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_1st - j) * itm_elev[1] / 1000000) / ( num_points_1st * itm_elev[1] * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -637,7 +613,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_2nd - j) * itm_elev[1] / 1000000) / ( num_points_2nd * itm_elev[1] * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 double min_elev = SGMiscd::min(itm_elev[last+1] + clutter_height, itm_elev[num_points_1st + num_points_2nd +2] + clutter_height); @@ -682,7 +657,6 @@ void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm // First Fresnel radius double frs_rad = 548 * sqrt( (j * itm_elev[1] * (num_points_3rd - j) * itm_elev[1] / 1000000) / ( num_points_3rd * itm_elev[1] * freq / 1000) ); - assert(frs_rad > 0); //double earth_h = distance_m * (distance_m - j * itm_elev[1]) / ( 1000000 * 12.75 * 1.33 ); // K=4/3 @@ -847,10 +821,9 @@ double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, i frq_mhz = freq; double dbloss; double tx_pow = _transmitter_power; - double ant_gain = _antenna_gain; + double ant_gain = _rx_antenna_gain + _tx_antenna_gain; double signal = 0.0; - double ATC_HAAT = 30.0; - double Aircraft_HAAT = 5.0; + double sender_alt_ft,sender_alt; double transmitter_height=0.0; double receiver_height=0.0; @@ -859,13 +832,8 @@ double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, i double own_alt_ft = fgGetDouble("/position/altitude-ft"); double own_alt= own_alt_ft * SG_FEET_TO_METER; - if(transmission_type == 1) - tx_pow = _transmitter_power + 6.0; - - if((transmission_type == 1) || (transmission_type == 3)) - ant_gain = _antenna_gain + 3.0; //pilot plane's antenna gain + ground station antenna gain - double link_budget = tx_pow - _receiver_sensitivity + ant_gain; + double link_budget = tx_pow - _receiver_sensitivity - _rx_line_losses - _tx_line_losses + ant_gain; //cerr << "ITM:: pilot Lat: " << own_lat << ", Lon: " << own_lon << ", Alt: " << own_alt << endl; @@ -881,10 +849,10 @@ double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, i double distance_m = SGGeodesy::distanceM(own_pos, sender_pos); - if(transmission_type == 1) - transmitter_height += ATC_HAAT; - else - transmitter_height += Aircraft_HAAT; + + transmitter_height += _tx_antenna_height; + receiver_height += _rx_antenna_height; + /** radio horizon calculation with wave bending k=4/3 */ double receiver_horizon = 4.12 * sqrt(receiver_height); diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 4c2bb7f64..1261b7c51 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -44,7 +44,6 @@ private: double _transmitter_power; double _tx_antenna_height; double _rx_antenna_height; - double _antenna_gain; double _rx_antenna_gain; double _tx_antenna_gain; double _rx_line_losses; @@ -72,6 +71,10 @@ public: void setRxSensitivity(double sensitivity) { _receiver_sensitivity = sensitivity; }; void setTxAntennaHeight(double tx_antenna_height) { _tx_antenna_height = tx_antenna_height; }; void setRxAntennaHeight(double rx_antenna_height) { _rx_antenna_height = rx_antenna_height; }; + void setTxAntennaGain(double tx_antenna_gain) { _tx_antenna_gain = tx_antenna_gain; }; + void setRxAntennaGain(double rx_antenna_gain) { _rx_antenna_gain = rx_antenna_gain; }; + void setTxLineLosses(double tx_line_losses) { _tx_line_losses = tx_line_losses; }; + void setRxLineLosses(double rx_line_losses) { _rx_line_losses = rx_line_losses; }; void setPropagationModel(int model) { _propagation_model = model; }; // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); From 1db2d8a660b6d7282d75920abaf1b4284071f379 Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 1 Dec 2011 14:28:20 +0200 Subject: [PATCH 39/47] Start implementing antenna properties --- src/Radio/antenna.cxx | 17 +++++++++++++++++ src/Radio/antenna.hxx | 17 +++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 src/Radio/antenna.cxx create mode 100644 src/Radio/antenna.hxx diff --git a/src/Radio/antenna.cxx b/src/Radio/antenna.cxx new file mode 100644 index 000000000..e3909103b --- /dev/null +++ b/src/Radio/antenna.cxx @@ -0,0 +1,17 @@ +// antenna.cxx -- implementation of FGRadioAntenna +// Class to represent a virtual radio antenna properties +// Written by Adrian Musceac, started December 2011. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. diff --git a/src/Radio/antenna.hxx b/src/Radio/antenna.hxx new file mode 100644 index 000000000..9d54c4cac --- /dev/null +++ b/src/Radio/antenna.hxx @@ -0,0 +1,17 @@ +// antenna.hxx -- FGRadioAntenna: class to represent antenna properties +// +// Written by Adrian Musceac, started December 2011. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. From ad6b0b81da93a70cc692479f512a9e51c1d153ab Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 1 Dec 2011 14:33:25 +0200 Subject: [PATCH 40/47] Make polarization a configurable parameter --- src/Radio/radio.cxx | 4 +++- src/Radio/radio.hxx | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 4f2772e4e..d2d6a159b 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -60,6 +60,8 @@ FGRadioTransmission::FGRadioTransmission() { _rx_line_losses = 2.0; // to be configured for each station _tx_line_losses = 2.0; + _polarization = 1; // default vertical + _propagation_model = 2; _terrain_sampling_distance = fgGetDouble("/sim/radio/sampling-distance", 90.0); // regular SRTM is 90 meters } @@ -211,7 +213,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i else frq_mhz = freq; int radio_climate = 5; // continental temperate - int pol=1; // assuming vertical polarization although this is more complex in reality + int pol= _polarization; double conf = 0.90; // 90% of situations and time, take into account speed double rel = 0.90; double dbloss; diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 1261b7c51..ac6dc9e3d 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -49,6 +49,7 @@ private: double _rx_line_losses; double _tx_line_losses; double _terrain_sampling_distance; + int _polarization; std::map _mat_database; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM @@ -76,6 +77,7 @@ public: void setTxLineLosses(double tx_line_losses) { _tx_line_losses = tx_line_losses; }; void setRxLineLosses(double rx_line_losses) { _rx_line_losses = rx_line_losses; }; void setPropagationModel(int model) { _propagation_model = model; }; + void setPolarization(int polarization) { _polarization = polarization; }; // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); void receiveChat(SGGeod tx_pos, double freq, string text, int transmission_type); From e3e23b091541f39d4f26e255e0dce7d0aad0d3bb Mon Sep 17 00:00:00 2001 From: adrian Date: Thu, 1 Dec 2011 22:46:46 +0200 Subject: [PATCH 41/47] Add function to calculate polarization loss This function is reliable only for vertical polarization --- src/Radio/CMakeLists.txt | 2 ++ src/Radio/antenna.cxx | 34 ++++++++++++++++++++++++++++++++++ src/Radio/antenna.hxx | 36 ++++++++++++++++++++++++++++++++++++ src/Radio/radio.cxx | 26 +++++++++++++++++++------- src/Radio/radio.hxx | 1 + 5 files changed, 92 insertions(+), 7 deletions(-) diff --git a/src/Radio/CMakeLists.txt b/src/Radio/CMakeLists.txt index aa6d5483b..72b1705f5 100644 --- a/src/Radio/CMakeLists.txt +++ b/src/Radio/CMakeLists.txt @@ -1,11 +1,13 @@ include(FlightGearComponent) set(SOURCES + antenna.cxx radio.cxx itm.cpp ) set(HEADERS + antenna.hxx radio.hxx ) diff --git a/src/Radio/antenna.cxx b/src/Radio/antenna.cxx index e3909103b..22496d81e 100644 --- a/src/Radio/antenna.cxx +++ b/src/Radio/antenna.cxx @@ -15,3 +15,37 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include + +#include +#include "antenna.hxx" + + +FGRadioAntenna::FGRadioAntenna() { + + _mirror_y = 1; + _mirror_z = 1; +} + +FGRadioAntenna::~FGRadioAntenna() { + +} + +double FGRadioAntenna::calculate_gain(double azimuth, double theta) { + return 0; +} + + + + +void FGRadioAntenna::_load_antenna_pattern() { + +} diff --git a/src/Radio/antenna.hxx b/src/Radio/antenna.hxx index 9d54c4cac..ec5d30253 100644 --- a/src/Radio/antenna.hxx +++ b/src/Radio/antenna.hxx @@ -15,3 +15,39 @@ // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include +#include +#include
+ +#include +#include + +class FGRadioAntenna +{ +private: + void _load_antenna_pattern(); + int _mirror_y; + int _mirror_z; + double _heading; + struct AntennaGain { + double azimuth; + double elevation_angle; + double gain; + }; + + typedef std::vector AntennaPattern; + AntennaPattern _pattern; + +public: + + FGRadioAntenna(); + ~FGRadioAntenna(); + double calculate_gain(double azimuth, double theta); + + +}; diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index d2d6a159b..d5211615a 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -357,13 +357,6 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i } - double max_alt_between=0.0; - for( deque::size_type i = 0; i < _elevations.size(); i++ ) { - if (_elevations[i] > max_alt_between) { - max_alt_between = _elevations[i]; - } - } - double num_points= (double)_elevations.size(); _elevations.push_front(point_distance); @@ -875,4 +868,23 @@ double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, i } +/*** calculate loss due to polarization mismatch +* this function is only reliable for vertical polarization +* due to the V-shape of horizontally polarized antennas +***/ +double FGRadioTransmission::polarization_loss() { + + double theta_deg; + double roll = fgGetDouble("/orientation/roll-deg"); + double pitch = fgGetDouble("/orientation/pitch-deg"); + double theta = acos( sqrt( cos(roll) * cos(roll) + cos(pitch) * cos(pitch) )); + if (_polarization == 1) + theta_deg = 90.0 - theta; + else + theta_deg = theta; + if (fabs(theta_deg) > 85.0) // we don't want to converge into infinity + theta_deg = 85.0; + return 10 * log10(cos(theta_deg) * cos(theta_deg)); +} + diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index ac6dc9e3d..cafa2b386 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -53,6 +53,7 @@ private: std::map _mat_database; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM + double polarization_loss(); double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); double LOS_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); void clutterLoss(double freq, double distance_m, double itm_elev[], std::deque materials, From 6179c26a04acfb1060d672c0bf0dc83af8c8b415 Mon Sep 17 00:00:00 2001 From: adrian Date: Fri, 2 Dec 2011 17:38:52 +0200 Subject: [PATCH 42/47] Set a couple of properties using the node /sim/radio Also use correct the polarization calculations, using Simgear constants for degree to radians conversions --- src/Radio/antenna.cxx | 7 ++++-- src/Radio/antenna.hxx | 8 ++++--- src/Radio/radio.cxx | 54 +++++++++++++++++++++++++++++++------------ src/Radio/radio.hxx | 2 +- 4 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/Radio/antenna.cxx b/src/Radio/antenna.cxx index 22496d81e..dc6983723 100644 --- a/src/Radio/antenna.cxx +++ b/src/Radio/antenna.cxx @@ -33,19 +33,22 @@ FGRadioAntenna::FGRadioAntenna() { _mirror_y = 1; _mirror_z = 1; + _invert_ground = 0; } FGRadioAntenna::~FGRadioAntenna() { } -double FGRadioAntenna::calculate_gain(double azimuth, double theta) { +double FGRadioAntenna::calculate_gain(double azimuth, double elevation) { return 0; } - +/*** load external plot file generated by NEC4 +* +***/ void FGRadioAntenna::_load_antenna_pattern() { } diff --git a/src/Radio/antenna.hxx b/src/Radio/antenna.hxx index ec5d30253..f24c133ee 100644 --- a/src/Radio/antenna.hxx +++ b/src/Radio/antenna.hxx @@ -33,10 +33,12 @@ private: void _load_antenna_pattern(); int _mirror_y; int _mirror_z; - double _heading; + int _invert_ground; + double _heading_deg; + double _elevation_angle_deg; struct AntennaGain { double azimuth; - double elevation_angle; + double elevation; double gain; }; @@ -47,7 +49,7 @@ public: FGRadioAntenna(); ~FGRadioAntenna(); - double calculate_gain(double azimuth, double theta); + double calculate_gain(double azimuth, double elevation); }; diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index d5211615a..9f21a936e 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -63,7 +63,9 @@ FGRadioTransmission::FGRadioTransmission() { _polarization = 1; // default vertical _propagation_model = 2; - _terrain_sampling_distance = fgGetDouble("/sim/radio/sampling-distance", 90.0); // regular SRTM is 90 meters + + _root_node = fgGetNode("sim/radio", true); + _terrain_sampling_distance = _root_node->getDoubleValue("sampling-distance", 90.0); // regular SRTM is 90 meters } FGRadioTransmission::~FGRadioTransmission() @@ -145,7 +147,7 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in /** write signal strength above threshold to the property tree * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) **/ - fgSetDouble("/sim/radio/comm1-signal", signal); + _root_node->setDoubleValue("station[0]/signal", signal); } } else if ( _propagation_model == 2 ) { @@ -177,7 +179,7 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in //cerr << "Usable signal at limit: " << signal << endl; fgSetDouble("/sim/sound/voices/voice/volume", volume); fgSetString("/sim/messages/atc", text.c_str()); - fgSetDouble("/sim/radio/comm1-signal", signal); + _root_node->setDoubleValue("station[0]/signal", signal); fgSetDouble("/sim/sound/voices/voice/volume", old_volume); } else { @@ -185,7 +187,7 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in /** write signal strength above threshold to the property tree * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) **/ - fgSetDouble("/sim/radio/comm1-signal", signal); + _root_node->setDoubleValue("station[0]/signal", signal); } } @@ -302,7 +304,10 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i SG_LOG(SG_GENERAL, SG_BULK, "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters"); - cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; + //cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; + _root_node->setDoubleValue("station[0]/rx-height", receiver_height); + _root_node->setDoubleValue("station[0]/tx-height", transmitter_height); + _root_node->setDoubleValue("station[0]/distance", distance_m); unsigned int e_size = (deque::size_type)max_points; @@ -373,24 +378,33 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i point_to_point(itm_elev, receiver_height, transmitter_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); - if( fgGetBool( "/sim/radio/use-clutter-attenuation", false ) ) + if( _root_node->getBoolValue( "use-clutter-attenuation", false ) ) clutterLoss(frq_mhz, distance_m, itm_elev, materials, receiver_height, transmitter_height, p_mode, horizons, clutter_loss); } else { point_to_point(itm_elev, transmitter_height, receiver_height, eps_dielect, sgm_conductivity, eno, frq_mhz, radio_climate, pol, conf, rel, dbloss, strmode, p_mode, horizons, errnum); - if( fgGetBool( "/sim/radio/use-clutter-attenuation", false ) ) + if( _root_node->getBoolValue( "use-clutter-attenuation", false ) ) clutterLoss(frq_mhz, distance_m, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss); } + + double pol_loss = 0.0; + if (_polarization == 1) { + pol_loss = polarization_loss(); + } SG_LOG(SG_GENERAL, SG_BULK, "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum); - cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; - - cerr << "Clutter loss: " << clutter_loss << endl; + //cerr << "ITM:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm, " << strmode << ", Error: " << errnum << endl; + _root_node->setDoubleValue("station[0]/link-budget", link_budget); + _root_node->setDoubleValue("station[0]/terrain-attenuation", dbloss); + _root_node->setStringValue("station[0]/prop-mode", strmode); + _root_node->setDoubleValue("station[0]/clutter-attenuation", clutter_loss); + _root_node->setDoubleValue("station[0]/polarization-attenuation", pol_loss); + //cerr << "Clutter loss: " << clutter_loss << endl; //if (errnum == 4) // if parameters are outside sane values for lrprop, the alternative method is used // return -1; - signal = link_budget - dbloss - clutter_loss; + signal = link_budget - dbloss - clutter_loss + pol_loss; return signal; } @@ -876,15 +890,25 @@ double FGRadioTransmission::polarization_loss() { double theta_deg; double roll = fgGetDouble("/orientation/roll-deg"); + if (fabs(roll) > 85.0) + roll = 85.0; double pitch = fgGetDouble("/orientation/pitch-deg"); - double theta = acos( sqrt( cos(roll) * cos(roll) + cos(pitch) * cos(pitch) )); - if (_polarization == 1) + if (fabs(pitch) > 85.0) + pitch = 85.0; + double theta = fabs( atan( sqrt( + pow(tan(roll * SGD_DEGREES_TO_RADIANS), 2) + + pow(tan(pitch * SGD_DEGREES_TO_RADIANS), 2) )) * SGD_RADIANS_TO_DEGREES); + + if (_polarization == 0) theta_deg = 90.0 - theta; else theta_deg = theta; - if (fabs(theta_deg) > 85.0) // we don't want to converge into infinity + if (theta_deg > 85.0) // we don't want to converge into infinity theta_deg = 85.0; - return 10 * log10(cos(theta_deg) * cos(theta_deg)); + + double loss = 10 * log10( pow(cos(theta_deg * SGD_DEGREES_TO_RADIANS), 2) ); + //cerr << "Polarization loss: " << loss << " dBm " << endl; + return loss; } diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index cafa2b386..92ce66fbc 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -51,7 +51,7 @@ private: double _terrain_sampling_distance; int _polarization; std::map _mat_database; - + SGPropertyNode *_root_node; int _propagation_model; /// 0 none, 1 round Earth, 2 ITM double polarization_loss(); double ITM_calculate_attenuation(SGGeod tx_pos, double freq, int ground_to_air); From 3340694170d958799bbfc4f35434bfe45caba718 Mon Sep 17 00:00:00 2001 From: adrian Date: Fri, 2 Dec 2011 19:13:53 +0200 Subject: [PATCH 43/47] Add some functions to convert between various units --- src/Radio/radio.cxx | 20 ++++++++++++++++++-- src/Radio/radio.hxx | 8 +++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 9f21a936e..0bc503dc9 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -871,10 +871,13 @@ double FGRadioTransmission::LOS_calculate_attenuation(SGGeod pos, double freq, i if (distance_m > total_horizon) { return -1; } - + double pol_loss = 0.0; + if (_polarization == 1) { + pol_loss = polarization_loss(); + } // free-space loss (distance calculation should be changed) dbloss = 20 * log10(distance_m) +20 * log10(frq_mhz) -27.55; - signal = link_budget - dbloss; + signal = link_budget - dbloss + pol_loss; SG_LOG(SG_GENERAL, SG_BULK, "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm "); //cerr << "LOS:: Link budget: " << link_budget << ", Attenuation: " << dbloss << " dBm " << endl; @@ -912,3 +915,16 @@ double FGRadioTransmission::polarization_loss() { } +double FGRadioTransmission::power_to_dbm(double power_watt) { + return 10 * log10(1000 * power_watt); // returns dbm +} + +double FGRadioTransmission::dbm_to_power(double dbm) { + return exp( (dbm-30) * log(10) / 10); // returns Watts +} + +double FGRadioTransmission::dbm_to_microvolt(double dbm) { + return sqrt(dbm_to_power(dbm) * 50) * 1000000; // returns microvolts +} + + diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 92ce66fbc..619de3950 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -66,7 +66,7 @@ public: FGRadioTransmission(); ~FGRadioTransmission(); - + // a couple of setters and getters for convenience void setFrequency(double freq, int radio); double getFrequency(int radio); void setTxPower(double txpower) { _transmitter_power = txpower; }; @@ -79,6 +79,12 @@ public: void setRxLineLosses(double rx_line_losses) { _rx_line_losses = rx_line_losses; }; void setPropagationModel(int model) { _propagation_model = model; }; void setPolarization(int polarization) { _polarization = polarization; }; + // accessory functions for unit conversions + double power_to_dbm(double power_watt); + double dbm_to_power(double dbm); + double dbm_to_microvolt(double dbm); + + // transmission_type: 0 for air to ground 1 for ground to air, 2 for air to air, 3 for pilot to ground, 4 for pilot to air void receiveATC(SGGeod tx_pos, double freq, string text, int transmission_type); void receiveChat(SGGeod tx_pos, double freq, string text, int transmission_type); From e4f511fb31ee652724b41334492656ef2a4360f1 Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 3 Dec 2011 16:00:14 +0200 Subject: [PATCH 44/47] Write a few more properties: signal-dbm - signal strength in dBm field-strength-uV - signal strength in microvolts --- src/Radio/antenna.cxx | 3 +-- src/Radio/antenna.hxx | 2 +- src/Radio/radio.cxx | 8 +++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/Radio/antenna.cxx b/src/Radio/antenna.cxx index dc6983723..d61848907 100644 --- a/src/Radio/antenna.cxx +++ b/src/Radio/antenna.cxx @@ -47,8 +47,7 @@ double FGRadioAntenna::calculate_gain(double azimuth, double elevation) { /*** load external plot file generated by NEC4 -* ***/ -void FGRadioAntenna::_load_antenna_pattern() { +void FGRadioAntenna::load_antenna_pattern() { } diff --git a/src/Radio/antenna.hxx b/src/Radio/antenna.hxx index f24c133ee..3e6da2566 100644 --- a/src/Radio/antenna.hxx +++ b/src/Radio/antenna.hxx @@ -30,7 +30,7 @@ class FGRadioAntenna { private: - void _load_antenna_pattern(); + void load_antenna_pattern(); int _mirror_y; int _mirror_z; int _invert_ground; diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 0bc503dc9..20322a0cf 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -133,10 +133,11 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in else { if ( _propagation_model == 0) { + // skip propagation routines entirely fgSetString("/sim/messages/atc", text.c_str()); } else if ( _propagation_model == 1 ) { - // TODO: free space, round earth + // Use free-space, round earth double signal = LOS_calculate_attenuation(tx_pos, freq, ground_to_air); if (signal <= 0.0) { return; @@ -231,6 +232,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i double link_budget = tx_pow - _receiver_sensitivity - _rx_line_losses - _tx_line_losses + ant_gain; + double signal_strength = tx_pow - _rx_line_losses - _tx_line_losses + ant_gain; FGScenery * scenery = globals->get_scenery(); @@ -405,6 +407,10 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i //if (errnum == 4) // if parameters are outside sane values for lrprop, the alternative method is used // return -1; signal = link_budget - dbloss - clutter_loss + pol_loss; + double signal_strength_dbm = signal_strength - dbloss - clutter_loss + pol_loss; + double field_strength_uV = dbm_to_microvolt(signal_strength_dbm); + _root_node->setDoubleValue("station[0]/signal-dbm", signal_strength_dbm); + _root_node->setDoubleValue("station[0]/field-strength-uV", field_strength_uV); return signal; } From 52bdb959c79d58ff260bd5292ef2e377fb1efeaa Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 3 Dec 2011 18:48:24 +0200 Subject: [PATCH 45/47] Rename conversion functions to be more explicit ...and set some more properties: tx-erp, etc. --- src/Radio/radio.cxx | 22 +++++++++------------- src/Radio/radio.hxx | 4 ++-- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 20322a0cf..038f4a830 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -145,10 +145,7 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in else { fgSetString("/sim/messages/atc", text.c_str()); - /** write signal strength above threshold to the property tree - * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) - **/ - _root_node->setDoubleValue("station[0]/signal", signal); + } } else if ( _propagation_model == 2 ) { @@ -180,15 +177,10 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in //cerr << "Usable signal at limit: " << signal << endl; fgSetDouble("/sim/sound/voices/voice/volume", volume); fgSetString("/sim/messages/atc", text.c_str()); - _root_node->setDoubleValue("station[0]/signal", signal); fgSetDouble("/sim/sound/voices/voice/volume", old_volume); } else { fgSetString("/sim/messages/atc", text.c_str()); - /** write signal strength above threshold to the property tree - * to implement a simple S-meter just divide by 3 dB per grade (VHF norm) - **/ - _root_node->setDoubleValue("station[0]/signal", signal); } } @@ -233,6 +225,8 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i double link_budget = tx_pow - _receiver_sensitivity - _rx_line_losses - _tx_line_losses + ant_gain; double signal_strength = tx_pow - _rx_line_losses - _tx_line_losses + ant_gain; + double tx_erp = dbm_to_watt(tx_pow + _tx_antenna_gain - _tx_line_losses); + FGScenery * scenery = globals->get_scenery(); @@ -309,7 +303,7 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i //cerr << "ITM:: RX-height: " << receiver_height << " meters, TX-height: " << transmitter_height << " meters, Distance: " << distance_m << " meters" << endl; _root_node->setDoubleValue("station[0]/rx-height", receiver_height); _root_node->setDoubleValue("station[0]/tx-height", transmitter_height); - _root_node->setDoubleValue("station[0]/distance", distance_m); + _root_node->setDoubleValue("station[0]/distance", distance_m / 1000); unsigned int e_size = (deque::size_type)max_points; @@ -411,6 +405,8 @@ double FGRadioTransmission::ITM_calculate_attenuation(SGGeod pos, double freq, i double field_strength_uV = dbm_to_microvolt(signal_strength_dbm); _root_node->setDoubleValue("station[0]/signal-dbm", signal_strength_dbm); _root_node->setDoubleValue("station[0]/field-strength-uV", field_strength_uV); + _root_node->setDoubleValue("station[0]/signal", signal); + _root_node->setDoubleValue("station[0]/tx-erp", tx_erp); return signal; } @@ -921,16 +917,16 @@ double FGRadioTransmission::polarization_loss() { } -double FGRadioTransmission::power_to_dbm(double power_watt) { +double FGRadioTransmission::watt_to_dbm(double power_watt) { return 10 * log10(1000 * power_watt); // returns dbm } -double FGRadioTransmission::dbm_to_power(double dbm) { +double FGRadioTransmission::dbm_to_watt(double dbm) { return exp( (dbm-30) * log(10) / 10); // returns Watts } double FGRadioTransmission::dbm_to_microvolt(double dbm) { - return sqrt(dbm_to_power(dbm) * 50) * 1000000; // returns microvolts + return sqrt(dbm_to_watt(dbm) * 50) * 1000000; // returns microvolts } diff --git a/src/Radio/radio.hxx b/src/Radio/radio.hxx index 619de3950..7b07d927e 100644 --- a/src/Radio/radio.hxx +++ b/src/Radio/radio.hxx @@ -80,8 +80,8 @@ public: void setPropagationModel(int model) { _propagation_model = model; }; void setPolarization(int polarization) { _polarization = polarization; }; // accessory functions for unit conversions - double power_to_dbm(double power_watt); - double dbm_to_power(double dbm); + double watt_to_dbm(double power_watt); + double dbm_to_watt(double dbm); double dbm_to_microvolt(double dbm); From b685fffd820d98c7798d02466a060a7f29d0fbc4 Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 3 Dec 2011 18:55:11 +0200 Subject: [PATCH 46/47] Set ATC ground tx-power and antenna gain to smaller values --- src/Radio/radio.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Radio/radio.cxx b/src/Radio/radio.cxx index 038f4a830..be8dc1ef3 100644 --- a/src/Radio/radio.cxx +++ b/src/Radio/radio.cxx @@ -119,9 +119,9 @@ void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, in if(ground_to_air == 1) { - _transmitter_power += 6.0; + _transmitter_power += 4.0; _tx_antenna_height += 30.0; - _tx_antenna_gain += 3.0; + _tx_antenna_gain += 2.0; } From 693f868930c8fd6ed146b764ad68a3acc351250d Mon Sep 17 00:00:00 2001 From: adrian Date: Sat, 3 Dec 2011 20:08:50 +0200 Subject: [PATCH 47/47] Get rid of annoying printf messages in itm.cpp --- src/Radio/itm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Radio/itm.cpp b/src/Radio/itm.cpp index 79360a201..5fe6abc8e 100644 --- a/src/Radio/itm.cpp +++ b/src/Radio/itm.cpp @@ -132,8 +132,8 @@ double FORTRAN_DIM(const double &x, const double &y) return 0.0; } -#define set_warn(txt, err) printf("%s:%d %s\n", __func__, __LINE__, txt); - +//#define set_warn(txt, err) printf("%s:%d %s\n", __func__, __LINE__, txt); +#define set_warn(txt, err) 1; // :13: single-knife attenuation, page 6 /*