1 // commradio.cxx -- class to manage a nav radio instance
3 // Written by Torsten Dreyer, February 2014
5 // Copyright (C) 2000 - 2011 Curtis L. Olson - http://www.flightgear.org/~curt
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26 #include "commradio.hxx"
29 #include <boost/foreach.hpp>
31 #include <simgear/sg_inlines.h>
32 #include <simgear/props/propertyObject.hxx>
33 #include <simgear/misc/strutils.hxx>
35 #include <ATC/CommStation.hxx>
36 #include <ATC/MetarPropertiesATISInformationProvider.hxx>
37 #include <ATC/CurrentWeatherATISInformationProvider.hxx>
38 #include <Airports/airport.hxx>
39 #include <Main/fg_props.hxx>
40 #include <Navaids/navlist.hxx>
42 #include "frequencyformatter.hxx"
44 namespace Instrumentation {
46 using simgear::PropertyObject;
50 SignalQualityComputer::~SignalQualityComputer()
54 class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
57 SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {}
58 virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const;
59 virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const;
60 virtual double computeSignalQuality( double slantDistanceM ) const;
66 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const
68 return computeSignalQuality( dist( sender, receiver ) );
71 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const
73 return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) );
76 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const
78 return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) );
83 virtual void onExit() = 0;
84 virtual ~OnExitHandler() {}
89 OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
90 ~OnExit() { _onExitHandler->onExit(); }
92 OnExitHandler * _onExitHandler;
96 class OutputProperties : public OnExitHandler {
98 OutputProperties( SGPropertyNode_ptr rootNode ) :
100 _signalQuality_norm(0.0),
101 _slantDistance_m(0.0),
102 _trueBearingTo_deg(0.0),
103 _trueBearingFrom_deg(0.0),
104 _trackDistance_m(0.0),
105 _heightAboveStation_ft(0.0),
107 _PO_stationType( rootNode->getNode("station-type", true ) ),
108 _PO_stationName( rootNode->getNode("station-name", true ) ),
109 _PO_airportId( rootNode->getNode("airport-id", true ) ),
110 _PO_signalQuality_norm( rootNode->getNode("signal-quality-norm",true) ),
111 _PO_slantDistance_m( rootNode->getNode("slant-distance-m",true) ),
112 _PO_trueBearingTo_deg( rootNode->getNode("true-bearing-to-deg",true) ),
113 _PO_trueBearingFrom_deg( rootNode->getNode("true-bearing-from-deg",true) ),
114 _PO_trackDistance_m( rootNode->getNode("track-distance-m",true) ),
115 _PO_heightAboveStation_ft( rootNode->getNode("height-above-station-ft",true) )
117 virtual ~OutputProperties() {}
120 SGPropertyNode_ptr _rootNode;
122 std::string _stationType;
123 std::string _stationName;
124 std::string _airportId;
125 double _signalQuality_norm;
126 double _slantDistance_m;
127 double _trueBearingTo_deg;
128 double _trueBearingFrom_deg;
129 double _trackDistance_m;
130 double _heightAboveStation_ft;
133 PropertyObject<string> _PO_stationType;
134 PropertyObject<string> _PO_stationName;
135 PropertyObject<string> _PO_airportId;
136 PropertyObject<double> _PO_signalQuality_norm;
137 PropertyObject<double> _PO_slantDistance_m;
138 PropertyObject<double> _PO_trueBearingTo_deg;
139 PropertyObject<double> _PO_trueBearingFrom_deg;
140 PropertyObject<double> _PO_trackDistance_m;
141 PropertyObject<double> _PO_heightAboveStation_ft;
143 virtual void onExit() {
144 _PO_stationType = _stationType;
145 _PO_stationName = _stationName;
146 _PO_airportId = _airportId;
147 _PO_signalQuality_norm = _signalQuality_norm;
148 _PO_slantDistance_m = _slantDistance_m;
149 _PO_trueBearingTo_deg = _trueBearingTo_deg;
150 _PO_trueBearingFrom_deg = _trueBearingFrom_deg;
151 _PO_trackDistance_m = _trackDistance_m;
152 _PO_heightAboveStation_ft = _heightAboveStation_ft;
156 /* ------------- The CommRadio implementation ---------------------- */
158 class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
165 void requestMetarForId( std::string & id );
167 void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
168 void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
171 virtual void valueChanged(SGPropertyNode * );
174 std::string _requestedId;
175 SGPropertyNode_ptr _realWxEnabledNode;
176 SGPropertyNode_ptr _metarPropertiesNode;
177 SGPropertyNode * _atisNode;
178 ATISEncoder _atisEncoder;
180 typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
182 MetarBridge::MetarBridge() :
187 MetarBridge::~MetarBridge()
191 void MetarBridge::bind()
193 _realWxEnabledNode = fgGetNode( "/environment/realwx/enabled", true );
194 _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
197 void MetarBridge::unbind()
199 _metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this );
202 void MetarBridge::requestMetarForId( std::string & id )
204 std::string uppercaseId = simgear::strutils::uppercase( id );
205 if( _requestedId == uppercaseId ) return;
206 _requestedId = uppercaseId;
208 if( _realWxEnabledNode->getBoolValue() ) {
209 // trigger a METAR request for the associated metarproperties
210 _metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId );
211 _metarPropertiesNode->getNode( "valid", true )->setBoolValue( false );
212 _metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 );
214 // use the present weather to generate the ATIS.
215 if( NULL != _atisNode && false == _requestedId.empty() ) {
216 CurrentWeatherATISInformationProvider provider( _requestedId );
217 _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
222 void MetarBridge::clearMetar()
225 requestMetarForId( empty );
228 void MetarBridge::valueChanged(SGPropertyNode * node )
230 // check for raising edge of valid flag
231 if( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue() )
234 std::string responseId = simgear::strutils::uppercase(
235 _metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
237 // unrequested metar!?
238 if( responseId != _requestedId )
241 if( NULL != _atisNode ) {
242 MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
243 _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
247 /* ------------- The CommRadio implementation ---------------------- */
249 class CommRadioImpl : public CommRadio, OutputProperties {
252 CommRadioImpl( SGPropertyNode_ptr node );
253 virtual ~CommRadioImpl();
255 virtual void update( double dt );
262 MetarBridgeRef _metarBridge;
263 FrequencyFormatter _useFrequencyFormatter;
264 FrequencyFormatter _stbyFrequencyFormatter;
265 const SignalQualityComputerRef _signalQualityComputer;
269 flightgear::CommStationRef _commStationForFrequency;
271 PropertyObject<bool> _serviceable;
272 PropertyObject<bool> _power_btn;
273 PropertyObject<bool> _power_good;
274 PropertyObject<double> _volume_norm;
275 PropertyObject<string> _atis;
280 CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) :
281 OutputProperties( fgGetNode("/instrumentation",true)->getNode(
282 node->getStringValue("name", "comm"),
283 node->getIntValue("number", 0), true)),
284 _num( node->getIntValue("number",0)),
285 _metarBridge( new MetarBridge() ),
286 _useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true),
287 _rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025, 118.0, 136.0 ),
288 _stbyFrequencyFormatter( _rootNode->getNode("frequencies/standby-mhz",true),
289 _rootNode->getNode("frequencies/standby-mhz-fmt",true), 0.025, 118.0, 136.0 ),
290 _signalQualityComputer( new SimpleDistanceSquareSignalQualityComputer(10*SG_NM_TO_METER) ),
294 _commStationForFrequency(NULL),
296 _serviceable( _rootNode->getNode("serviceable",true) ),
297 _power_btn( _rootNode->getNode("power-btn",true) ),
298 _power_good( _rootNode->getNode("power-good",true) ),
299 _volume_norm( _rootNode->getNode("volume",true) ),
300 _atis( _rootNode->getNode("atis",true) )
304 CommRadioImpl::~CommRadioImpl()
308 void CommRadioImpl::bind()
310 _metarBridge->setAtisNode( _atis.node() );
311 // link the metar node. /environment/metar[3] is comm1 and /environment[4] is comm2.
312 // see FGDATA/Environment/environment.xml
313 _metarBridge->setMetarPropertiesRoot( fgGetNode( "/environment",true)->getNode("metar", _num+3, true ) );
314 _metarBridge->bind();
317 void CommRadioImpl::unbind()
319 _metarBridge->unbind();
322 void CommRadioImpl::init()
326 void CommRadioImpl::update( double dt )
328 if( dt < SGLimitsd::min() ) return;
331 // Ensure all output properties get written on exit of this method
335 try { position = globals->get_aircraft_position(); }
336 catch( std::exception & ) { return; }
338 if( false == (_power_btn )) {
344 if( _frequency != _useFrequencyFormatter.getFrequency() ) {
345 _frequency = _useFrequencyFormatter.getFrequency();
349 if( _stationTTL <= 0.0 ) {
352 // Note: 122.375 must be rounded DOWN to 122370
353 // in order to be consistent with apt.dat et cetera.
354 int freqKhz = 10 * static_cast<int>(_frequency * 100 + 0.25);
355 _commStationForFrequency = flightgear::CommStation::findByFreq( freqKhz, position, NULL );
359 if( false == _commStationForFrequency.valid() ) return;
361 _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
363 SGGeodesy::inverse(position, _commStationForFrequency->geod(),
365 _trueBearingFrom_deg,
368 _heightAboveStation_ft =
369 SGMiscd::max(0.0, position.getElevationFt()
370 - _commStationForFrequency->airport()->elevation());
372 _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
373 _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
374 _stationName = _commStationForFrequency->ident();
375 _airportId = _commStationForFrequency->airport()->getId();
377 switch( _commStationForFrequency->type() ) {
378 case FGPositioned::FREQ_ATIS:
379 case FGPositioned::FREQ_AWOS: {
380 if( _signalQuality_norm > 0.01 ) {
381 _metarBridge->requestMetarForId( _airportId );
383 _metarBridge->clearMetar();
390 _metarBridge->clearMetar();
397 SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode )
399 return new CommRadioImpl( rootNode );
402 } // namespace Instrumentation