#endif
#include <math.h>
-#include <assert.h>
+
#include <stdlib.h>
#include <deque>
#include "radio.hxx"
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)
_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
- _terrain_sampling_distance = 90.0; // regular SRTM is 90 meters
+ _polarization = 1; // default vertical
+
+ _propagation_model = 2;
+ _terrain_sampling_distance = fgGetDouble("/sim/radio/sampling-distance", 90.0); // regular SRTM is 90 meters
}
FGRadioTransmission::~FGRadioTransmission()
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 {
// 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)
// 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)) {
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)
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;
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();
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;
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;
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");
}
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 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);
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
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;
- 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);
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
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);
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
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;
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);
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
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;
- 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);
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
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;
- 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);
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
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;
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;
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);