From f96123de1cc50af660d00036b70b88cbc3bedf3f Mon Sep 17 00:00:00 2001 From: adrian <adrian@localhost.com> Date: Tue, 6 Sep 2011 10:29:54 +0300 Subject: [PATCH] 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 <algorithm> -#include <math.h> -#include <stdlib.h> -#include <deque> #include <osg/Geode> #include <osg/Geometry> @@ -50,8 +47,6 @@ #include <Airports/groundnetwork.hxx> #include <Airports/dynamics.hxx> #include <Airports/simple.hxx> -#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<double> _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<unsigned>::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<double>::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<size;i++) { - itm_elev[i]=_elevations[i]; - //cerr << "ITM:: itm_elev: " << _elevations[i] << endl; - } - - ////////////// ITM default parameters ////////////// - // later perhaps take them from tile materials? - double eps_dielect=15.0; - double sgm_conductivity = 0.005; - double eno = 301.0; - double frq_mhz = 125.0; // middle of bandplan - int radio_climate = 5; // continental temperate - 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; - char strmode[150]; - int errnum; - - /////////// radio parameters /////////// - 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; //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 + 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; - - - // 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 teritory - - // else we need to calculate point to point link loss - - 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; - - //if (errnum !=0 && errnum !=1) - // return -1; - double snr = link_budget - dbloss; - return snr; - -} string FGATCController::formatATCFrequency3_2(int freq) { diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 7d885303a..0ae1bb374 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -304,7 +304,6 @@ public: string getGateName(FGAIAircraft *aircraft); virtual void render(bool) = 0; virtual string getName() = 0; - double calculate_attenuation(FGTrafficRecord * rec, FGAirportDynamics *parent, int ground_to_air); private: diff --git a/src/Instrumentation/CMakeLists.txt b/src/Instrumentation/CMakeLists.txt index 60d9457d3..c9bd8da88 100644 --- a/src/Instrumentation/CMakeLists.txt +++ b/src/Instrumentation/CMakeLists.txt @@ -1,6 +1,7 @@ include(FlightGearComponent) set(SOURCES + itm.cpp adf.cxx agradar.cxx airspeed_indicator.cxx diff --git a/src/Instrumentation/commradio.cxx b/src/Instrumentation/commradio.cxx new file mode 100644 index 000000000..cbdfda0ae --- /dev/null +++ b/src/Instrumentation/commradio.cxx @@ -0,0 +1,282 @@ +// commradio.cxx -- implementation of FGCommRadio +// +// Written by Adrian Musceac, started August 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. + + + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include <math.h> +#include <stdlib.h> +#include <deque> + +#include <Scenery/scenery.hxx> + +#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<double> _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<unsigned>::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<double>::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<size;i++) { + itm_elev[i]=_elevations[i]; + //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 teritory + + // else we need to calculate point to point link loss + + 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; + + //if (errnum == 4) + // return -1; + signal = link_budget - dbloss; + return signal; + +} diff --git a/src/Instrumentation/commradio.hxx b/src/Instrumentation/commradio.hxx new file mode 100644 index 000000000..8cd1041ac --- /dev/null +++ b/src/Instrumentation/commradio.hxx @@ -0,0 +1,69 @@ +// commradio.hxx -- class to manage a comm radio instance +// +// Written by Adrian Musceac, started August 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. + + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include <simgear/compiler.h> +#include <simgear/structure/subsystem_mgr.hxx> + +#include <Main/fg_props.hxx> + +#include <simgear/math/sg_geodesy.hxx> +#include <simgear/debug/logstream.hxx> +#include <string> + + +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