FGRadioTransmission::FGRadioTransmission() {
- _receiver_sensitivity = -110.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD
+ _receiver_sensitivity = -105.0; // typical AM receiver sensitivity seems to be 0.8 microVolt at 12dB SINAD
/** AM transmitter power in dBm.
* Typical output powers for ATC ground equipment, VHF-UHF:
_rx_antenna_height = 2.0; // RX antenna height above ground level
- _rx_antenna_gain = 1.0; // gain expressed in dBi
+ _rx_antenna_gain = 1.0; // maximum antenna gain expressed in dBi
_tx_antenna_gain = 1.0;
_rx_line_losses = 2.0; // to be configured for each station
_root_node = fgGetNode("sim/radio", true);
_terrain_sampling_distance = _root_node->getDoubleValue("sampling-distance", 90.0); // regular SRTM is 90 meters
+
+
}
FGRadioTransmission::~FGRadioTransmission()
}
+double FGRadioTransmission::receiveBeacon(double lat, double lon, double elev, double heading, double pitch) {
+
+
+ _transmitter_power = 36;
+ _tx_antenna_height += 0.0;
+ _tx_antenna_gain += 0.5;
+ elev = elev * SG_FEET_TO_METER;
+ double freq = _root_node->getDoubleValue("station[0]/frequency", 118.0);
+ int ground_to_air = 1;
+ string text = "Beacon1";
+ double comm1 = getFrequency(1);
+ double comm2 = getFrequency(2);
+ if ( !(fabs(freq - comm1) <= 0.0001) && !(fabs(freq - comm2) <= 0.0001) ) {
+ return -1;
+ }
+ SGGeod tx_pos = SGGeod::fromDegM( lon, lat, elev );
+ double signal = ITM_calculate_attenuation(tx_pos, freq, ground_to_air);
+
+ return signal;
+}
+
+
/*** Receive ATC radio communication as text
***/
void FGRadioTransmission::receiveATC(SGGeod tx_pos, double freq, string text, int ground_to_air) {
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;
}
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;
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 ) {
//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);
}
}
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;
+ double frq_mhz = freq;
+
int radio_climate = 5; // continental temperate
int pol= _polarization;
double conf = 0.90; // 90% of situations and time, take into account speed
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();
double own_lat = fgGetDouble("/position/latitude-deg");
double own_lon = fgGetDouble("/position/longitude-deg");
double own_alt_ft = fgGetDouble("/position/altitude-ft");
+ double own_heading = fgGetDouble("/orientation/heading-deg");
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 );
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= _terrain_sampling_distance;
double course = SGGeodesy::courseRad(own_pos_c, sender_pos_c);
+ double reverse_course = SGGeodesy::courseRad(sender_pos_c, own_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 */
int max_points = (int)floor(distance_m / point_distance);
double delta_last = fmod(distance_m, point_distance);
- deque<double> _elevations;
+ deque<double> elevations;
deque<string> materials;
//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<unsigned>::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 ));
const SGMaterial *mat = 0;
if (scenery->get_elevation_m( probe, elevation_m, &mat )) {
if((transmission_type == 3) || (transmission_type == 4)) {
- _elevations.push_back(elevation_m);
+ elevations.push_back(elevation_m);
if(mat) {
const std::vector<string> mat_names = mat->get_names();
materials.push_back(mat_names[0]);
}
}
else {
- _elevations.push_front(elevation_m);
+ elevations.push_front(elevation_m);
if(mat) {
const std::vector<string> mat_names = mat->get_names();
materials.push_front(mat_names[0]);
}
else {
if((transmission_type == 3) || (transmission_type == 4)) {
- _elevations.push_back(0.0);
+ elevations.push_back(0.0);
materials.push_back("None");
}
else {
- _elevations.push_front(0.0);
+ elevations.push_front(0.0);
materials.push_front("None");
}
}
}
if((transmission_type == 3) || (transmission_type == 4)) {
- _elevations.push_front(elevation_under_pilot);
+ elevations.push_front(elevation_under_pilot);
if (delta_last > (point_distance / 2) ) // only add last point if it's farther than half point_distance
- _elevations.push_back(elevation_under_sender);
+ elevations.push_back(elevation_under_sender);
}
else {
- _elevations.push_back(elevation_under_pilot);
+ elevations.push_back(elevation_under_pilot);
if (delta_last > (point_distance / 2) )
- _elevations.push_front(elevation_under_sender);
+ elevations.push_front(elevation_under_sender);
}
- double num_points= (double)_elevations.size();
+ double num_points= (double)elevations.size();
+
+
+ elevations.push_front(point_distance);
+ elevations.push_front(num_points -1);
+
+ int size = elevations.size();
+ double *itm_elev;
+ itm_elev = new double[size];
- _elevations.push_front(point_distance);
- _elevations.push_front(num_points -1);
- int size = _elevations.size();
- double itm_elev[size];
for(int i=0;i<size;i++) {
- itm_elev[i]=_elevations[i];
- //cerr << "ITM:: itm_elev: " << _elevations[i] << endl;
- }
+ itm_elev[i]=elevations[i];
+
+ }
+
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, p_mode, horizons, errnum);
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);
+ calculate_clutter_loss(frq_mhz, 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( _root_node->getBoolValue( "use-clutter-attenuation", false ) )
- clutterLoss(frq_mhz, distance_m, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss);
+ calculate_clutter_loss(frq_mhz, itm_elev, materials, transmitter_height, receiver_height, p_mode, horizons, clutter_loss);
}
double pol_loss = 0.0;
_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 + pol_loss;
+ double tx_pattern_gain = 0.0;
+ double rx_pattern_gain = 0.0;
+ if (_root_node->getBoolValue("use-antenna-pattern", false)) {
+ double sender_heading = 270.0; // due West
+ double tx_antenna_bearing = sender_heading - reverse_course * SGD_RADIANS_TO_DEGREES;
+ double rx_antenna_bearing = own_heading - course * SGD_RADIANS_TO_DEGREES;
+ double rx_elev_angle = atan((itm_elev[2] + transmitter_height - itm_elev[(int)itm_elev[0] + 2] + receiver_height) / distance_m) * SGD_RADIANS_TO_DEGREES;
+ double tx_elev_angle = 0.0 - rx_elev_angle;
+ FGRadioAntenna* TX_antenna;
+ FGRadioAntenna* RX_antenna;
+ TX_antenna = new FGRadioAntenna("Plot2");
+ TX_antenna->set_heading(sender_heading);
+ TX_antenna->set_elevation_angle(0);
+ tx_pattern_gain = TX_antenna->calculate_gain(tx_antenna_bearing, tx_elev_angle);
+ RX_antenna = new FGRadioAntenna("Plot2");
+ RX_antenna->set_heading(own_heading);
+ RX_antenna->set_elevation_angle(fgGetDouble("/orientation/pitch-deg"));
+ rx_pattern_gain = RX_antenna->calculate_gain(rx_antenna_bearing, rx_elev_angle);
+
+ delete TX_antenna;
+ delete RX_antenna;
+ }
+
+ signal = link_budget - dbloss - clutter_loss + pol_loss + rx_pattern_gain + tx_pattern_gain;
+ double signal_strength_dbm = signal_strength - dbloss - clutter_loss + pol_loss + rx_pattern_gain + tx_pattern_gain;
+ 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);
+
+ //_root_node->setDoubleValue("station[0]/tx-pattern-gain", tx_pattern_gain);
+ //_root_node->setDoubleValue("station[0]/rx-pattern-gain", rx_pattern_gain);
+
+ delete[] itm_elev;
+
return signal;
}
* We are only worried about clutter loss, terrain influence
* on the first Fresnel zone is calculated in the ITM functions
***/
-void FGRadioTransmission::clutterLoss(double freq, double distance_m, double itm_elev[], deque<string> materials,
+void FGRadioTransmission::calculate_clutter_loss(double freq, double itm_elev[], deque<string> &materials,
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
+ double 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;
}
-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) {
- return exp( (dbm-30) * log(10) / 10); // returns Watts
+double FGRadioTransmission::dbm_to_watt(double dbm) {
+ return exp( (dbm-30) * log(10.0) / 10.0); // 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
}