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 <Airports/airport.hxx>
38 #include <Main/fg_props.hxx>
39 #include <Navaids/navlist.hxx>
41 #include "frequencyformatter.hxx"
43 namespace Instrumentation {
45 using simgear::PropertyObject;
49 SignalQualityComputer::~SignalQualityComputer()
53 class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
56 SimpleDistanceSquareSignalQualityComputer( double range ) : _rangeM(range), _rangeM2(range*range) {}
57 virtual double computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const;
58 virtual double computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const;
59 virtual double computeSignalQuality( double slantDistanceM ) const;
65 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const
67 return computeSignalQuality( dist( sender, receiver ) );
70 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const
72 return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) );
75 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const
77 return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) );
82 virtual void onExit() = 0;
87 OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
88 ~OnExit() { _onExitHandler->onExit(); }
90 OnExitHandler * _onExitHandler;
94 class OutputProperties : public OnExitHandler {
96 OutputProperties( SGPropertyNode_ptr rootNode ) :
98 _signalQuality_norm(0.0),
99 _slantDistance_m(0.0),
100 _trueBearingTo_deg(0.0),
101 _trueBearingFrom_deg(0.0),
102 _trackDistance_m(0.0),
103 _heightAboveStation_ft(0.0),
105 _PO_stationType( rootNode->getNode("station-type", true ) ),
106 _PO_stationName( rootNode->getNode("station-name", true ) ),
107 _PO_airportId( rootNode->getNode("airport-id", true ) ),
108 _PO_signalQuality_norm( rootNode->getNode("signal-quality-norm",true) ),
109 _PO_slantDistance_m( rootNode->getNode("slant-distance-m",true) ),
110 _PO_trueBearingTo_deg( rootNode->getNode("true-bearing-to-deg",true) ),
111 _PO_trueBearingFrom_deg( rootNode->getNode("true-bearing-from-deg",true) ),
112 _PO_trackDistance_m( rootNode->getNode("track-distance-m",true) ),
113 _PO_heightAboveStation_ft( rootNode->getNode("height-above-station-ft",true) )
117 SGPropertyNode_ptr _rootNode;
119 std::string _stationType;
120 std::string _stationName;
121 std::string _airportId;
122 double _signalQuality_norm;
123 double _slantDistance_m;
124 double _trueBearingTo_deg;
125 double _trueBearingFrom_deg;
126 double _trackDistance_m;
127 double _heightAboveStation_ft;
130 PropertyObject<string> _PO_stationType;
131 PropertyObject<string> _PO_stationName;
132 PropertyObject<string> _PO_airportId;
133 PropertyObject<double> _PO_signalQuality_norm;
134 PropertyObject<double> _PO_slantDistance_m;
135 PropertyObject<double> _PO_trueBearingTo_deg;
136 PropertyObject<double> _PO_trueBearingFrom_deg;
137 PropertyObject<double> _PO_trackDistance_m;
138 PropertyObject<double> _PO_heightAboveStation_ft;
140 virtual void onExit() {
141 _PO_stationType = _stationType;
142 _PO_stationName = _stationName;
143 _PO_airportId = _airportId;
144 _PO_signalQuality_norm = _signalQuality_norm;
145 _PO_slantDistance_m = _slantDistance_m;
146 _PO_trueBearingTo_deg = _trueBearingTo_deg;
147 _PO_trueBearingFrom_deg = _trueBearingFrom_deg;
148 _PO_trackDistance_m = _trackDistance_m;
149 _PO_heightAboveStation_ft = _heightAboveStation_ft;
153 /* ------------- The CommRadio implementation ---------------------- */
155 class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
162 void requestMetarForId( std::string & id );
164 void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
165 void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
168 virtual void valueChanged(SGPropertyNode * );
171 std::string _requestedId;
172 SGPropertyNode_ptr _metarPropertiesNode;
173 SGPropertyNode * _atisNode;
174 ATISEncoder _atisEncoder;
176 typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
178 MetarBridge::MetarBridge() :
183 MetarBridge::~MetarBridge()
187 void MetarBridge::bind()
189 _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
192 void MetarBridge::unbind()
194 _metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this );
197 void MetarBridge::requestMetarForId( std::string & id )
199 std::string uppercaseId = simgear::strutils::uppercase( id );
200 if( _requestedId == uppercaseId ) return;
201 _requestedId = uppercaseId;
202 _metarPropertiesNode->getNode( "station-id", true )->setStringValue( uppercaseId );
203 _metarPropertiesNode->getNode( "valid", true )->setBoolValue( false );
204 _metarPropertiesNode->getNode( "time-to-live", true )->setDoubleValue( 0.0 );
207 void MetarBridge::clearMetar()
210 requestMetarForId( empty );
213 void MetarBridge::valueChanged(SGPropertyNode * node )
215 // check for raising edge of valid flag
216 if( NULL == node || false == node->getBoolValue() )
219 std::string responseId = simgear::strutils::uppercase(
220 _metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
222 // unrequested metar!?
223 if( responseId != _requestedId )
226 if( NULL != _atisNode ) {
227 MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
228 _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
232 /* ------------- The CommRadio implementation ---------------------- */
234 class CommRadioImpl : public CommRadio, OutputProperties {
237 CommRadioImpl( SGPropertyNode_ptr node );
238 virtual ~CommRadioImpl();
240 virtual void update( double dt );
247 MetarBridgeRef _metarBridge;
248 FrequencyFormatter _useFrequencyFormatter;
249 FrequencyFormatter _stbyFrequencyFormatter;
250 const SignalQualityComputerRef _signalQualityComputer;
254 flightgear::CommStationRef _commStationForFrequency;
256 PropertyObject<bool> _serviceable;
257 PropertyObject<bool> _power_btn;
258 PropertyObject<bool> _power_good;
259 PropertyObject<double> _volume_norm;
260 PropertyObject<string> _atis;
265 CommRadioImpl::CommRadioImpl( SGPropertyNode_ptr node ) :
266 OutputProperties( fgGetNode("/instrumentation",true)->getNode(
267 node->getStringValue("name", "comm"),
268 node->getIntValue("number", 0), true)),
269 _num( node->getIntValue("number",0)),
270 _metarBridge( new MetarBridge() ),
271 _useFrequencyFormatter( _rootNode->getNode("frequencies/selected-mhz",true),
272 _rootNode->getNode("frequencies/selected-mhz-fmt",true), 0.025 ),
273 _stbyFrequencyFormatter( _rootNode->getNode("frequencies/standby-mhz",true),
274 _rootNode->getNode("frequencies/standby-mhz-fmt",true), 0.025 ),
275 _signalQualityComputer( new SimpleDistanceSquareSignalQualityComputer(10*SG_NM_TO_METER) ),
279 _commStationForFrequency(NULL),
281 _serviceable( _rootNode->getNode("serviceable",true) ),
282 _power_btn( _rootNode->getNode("power-btn",true) ),
283 _power_good( _rootNode->getNode("power-good",true) ),
284 _volume_norm( _rootNode->getNode("volume",true) ),
285 _atis( _rootNode->getNode("atis",true) )
289 CommRadioImpl::~CommRadioImpl()
293 void CommRadioImpl::bind()
295 _metarBridge->setAtisNode( _atis.node() );
296 // link the metar node. /environment/metar[3] is comm1 and /environment[4] is comm2.
297 // see FGDATA/Environment/environment.xml
298 _metarBridge->setMetarPropertiesRoot( fgGetNode( "/environment",true)->getNode("metar", _num+3, true ) );
299 _metarBridge->bind();
302 void CommRadioImpl::unbind()
304 _metarBridge->unbind();
307 void CommRadioImpl::init()
311 void CommRadioImpl::update( double dt )
313 if( dt < SGLimitsd::min() ) return;
316 // Ensure all output properties get written on exit of this method
320 try { position = globals->get_aircraft_position(); }
321 catch( std::exception & ) { return; }
323 if( false == (_power_btn )) {
329 if( _frequency != _useFrequencyFormatter.getFrequency() ) {
330 _frequency = _useFrequencyFormatter.getFrequency();
334 if( _stationTTL <= 0.0 ) {
337 // Note: 122.375 must be rounded DOWN to 122370
338 // in order to be consistent with apt.dat et cetera.
339 int freqKhz = 10 * static_cast<int>(_frequency * 100 + 0.25);
340 _commStationForFrequency = flightgear::CommStation::findByFreq( freqKhz, position, NULL );
344 if( false == _commStationForFrequency.valid() ) return;
346 _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
348 SGGeodesy::inverse(position, _commStationForFrequency->geod(),
350 _trueBearingFrom_deg,
353 _heightAboveStation_ft =
354 SGMiscd::max(0.0, position.getElevationFt()
355 - _commStationForFrequency->airport()->elevation());
357 _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
358 _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
359 _stationName = _commStationForFrequency->ident();
360 _airportId = _commStationForFrequency->airport()->getId();
362 switch( _commStationForFrequency->type() ) {
363 case FGPositioned::FREQ_ATIS:
364 case FGPositioned::FREQ_AWOS: {
365 if( _signalQuality_norm > 0.01 ) {
366 _metarBridge->requestMetarForId( _airportId );
368 _metarBridge->clearMetar();
375 _metarBridge->clearMetar();
382 SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode )
384 return new CommRadioImpl( rootNode );
387 } // namespace Instrumentation