]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/commradio.cxx
Canvas: update for new bounding box getters.
[flightgear.git] / src / Instrumentation / commradio.cxx
1 // commradio.cxx -- class to manage a nav radio instance
2 //
3 // Written by Torsten Dreyer, February 2014
4 //
5 // Copyright (C) 2000 - 2011  Curtis L. Olson - http://www.flightgear.org/~curt
6 //
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.
11 //
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.
16 //
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.
20 //
21
22 #ifdef HAVE_CONFIG_H
23 #  include <config.h>
24 #endif
25
26 #include "commradio.hxx"
27
28 #include <assert.h>
29 #include <boost/foreach.hpp>
30
31 #include <simgear/sg_inlines.h>
32 #include <simgear/props/propertyObject.hxx>
33 #include <simgear/misc/strutils.hxx>
34
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>
41
42 #include "frequencyformatter.hxx"
43
44 namespace Instrumentation {
45
46 using simgear::PropertyObject;
47 using std::string;
48
49
50 SignalQualityComputer::~SignalQualityComputer()
51 {
52 }
53
54 class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
55 {
56 public:
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;
61 private:
62   double _rangeM;
63   double _rangeM2;
64 };
65
66 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const
67 {
68     return computeSignalQuality( dist( sender, receiver ) );
69 }
70
71 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const
72 {
73     return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) );
74 }
75
76 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const
77 {
78     return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) );
79 }
80
81 class OnExitHandler {
82   public:
83     virtual void onExit() = 0;
84     virtual ~OnExitHandler() {}
85 };
86
87 class OnExit {
88   public:
89     OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
90     ~OnExit() { _onExitHandler->onExit(); }
91   private:
92     OnExitHandler * _onExitHandler;
93 };
94
95
96 class OutputProperties : public OnExitHandler {
97   public:
98     OutputProperties( SGPropertyNode_ptr rootNode ) : 
99       _rootNode(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),
106
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) )
116       {}
117     virtual ~OutputProperties() {}
118
119 protected:
120     SGPropertyNode_ptr _rootNode;
121
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;
131
132 private:
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;
142
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;
153     }
154 };
155
156 /* ------------- The CommRadio implementation ---------------------- */
157
158 class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
159 public:
160   MetarBridge();
161   ~MetarBridge();
162
163   void bind();
164   void unbind();
165   void requestMetarForId( std::string & id );
166   void clearMetar();
167   void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
168   void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
169
170 protected:
171   virtual void valueChanged(SGPropertyNode * );
172
173 private:
174   std::string _requestedId;
175   SGPropertyNode_ptr _realWxEnabledNode;
176   SGPropertyNode_ptr _metarPropertiesNode;
177   SGPropertyNode * _atisNode;
178   ATISEncoder _atisEncoder;
179 };
180 typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
181
182 MetarBridge::MetarBridge() :
183   _atisNode(0)
184 {
185 }
186
187 MetarBridge::~MetarBridge()
188 {
189 }
190
191 void MetarBridge::bind()
192 {
193   _realWxEnabledNode = fgGetNode( "/environment/realwx/enabled", true );
194   _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
195 }
196
197 void MetarBridge::unbind()
198 {
199   _metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this );
200 }
201
202 void MetarBridge::requestMetarForId( std::string & id )
203 {
204   std::string uppercaseId = simgear::strutils::uppercase( id );
205   if( _requestedId == uppercaseId ) return;
206   _requestedId = uppercaseId;
207
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 );
213   } else  {
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 ) );
218     }
219   }
220 }
221
222 void MetarBridge::clearMetar()
223 {
224   string empty;
225   requestMetarForId( empty );
226 }
227
228 void MetarBridge::valueChanged(SGPropertyNode * node )
229 {
230   // check for raising edge of valid flag
231   if( NULL == node || false == node->getBoolValue() || false == _realWxEnabledNode->getBoolValue() )
232     return;
233
234   std::string responseId = simgear::strutils::uppercase(
235      _metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
236
237   // unrequested metar!?
238   if( responseId != _requestedId )
239     return;
240
241   if( NULL != _atisNode ) {
242     MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
243     _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
244   }
245 }
246
247 /* ------------- The CommRadio implementation ---------------------- */
248
249 class CommRadioImpl : public CommRadio, OutputProperties {
250
251 public:
252   CommRadioImpl( SGPropertyNode_ptr node );
253   virtual ~CommRadioImpl();
254
255   virtual void update( double dt );
256   virtual void init();
257   void bind();
258   void unbind();
259
260 private:
261   int                _num;
262   MetarBridgeRef     _metarBridge;
263   FrequencyFormatter _useFrequencyFormatter;
264   FrequencyFormatter _stbyFrequencyFormatter;
265   const SignalQualityComputerRef _signalQualityComputer; 
266
267   double _stationTTL;
268   double _frequency;
269   flightgear::CommStationRef _commStationForFrequency;
270
271   PropertyObject<bool>   _serviceable;
272   PropertyObject<bool>   _power_btn;
273   PropertyObject<bool>   _power_good;
274   PropertyObject<double> _volume_norm;
275   PropertyObject<string> _atis;
276
277
278 };
279
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) ),
291
292     _stationTTL(0.0),
293     _frequency(-1.0),
294     _commStationForFrequency(NULL),
295
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) )
301 {
302 }
303
304 CommRadioImpl::~CommRadioImpl()
305 {
306 }
307
308 void CommRadioImpl::bind()
309 {
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();
315 }
316
317 void CommRadioImpl::unbind()
318 {
319   _metarBridge->unbind();
320 }
321
322 void CommRadioImpl::init()
323 {
324 }
325
326 void CommRadioImpl::update( double dt )
327 {
328     if( dt < SGLimitsd::min() ) return;
329     _stationTTL -= dt;
330
331     // Ensure all output properties get written on exit of this method
332     OnExit onExit(this);
333
334     SGGeod position;
335     try { position = globals->get_aircraft_position(); }
336     catch( std::exception & ) { return; }
337
338     if( false == (_power_btn )) {
339         _stationTTL = 0.0;
340         return;
341     }
342
343
344     if( _frequency != _useFrequencyFormatter.getFrequency() ) {
345         _frequency = _useFrequencyFormatter.getFrequency();
346         _stationTTL = 0.0;
347     }
348
349     if( _stationTTL <= 0.0 ) {
350         _stationTTL = 30.0;
351
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 );
356
357     }
358
359     if( false == _commStationForFrequency.valid() ) return;
360
361     _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
362
363     SGGeodesy::inverse(position, _commStationForFrequency->geod(), 
364         _trueBearingTo_deg,
365         _trueBearingFrom_deg,
366         _trackDistance_m );
367
368     _heightAboveStation_ft = 
369          SGMiscd::max(0.0, position.getElevationFt() 
370            - _commStationForFrequency->airport()->elevation());
371     
372     _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
373     _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
374     _stationName = _commStationForFrequency->ident();
375     _airportId = _commStationForFrequency->airport()->getId();
376
377     switch( _commStationForFrequency->type() ) {
378       case FGPositioned::FREQ_ATIS:
379       case FGPositioned::FREQ_AWOS: {
380         if( _signalQuality_norm > 0.01 ) {
381           _metarBridge->requestMetarForId( _airportId );
382         } else {
383           _metarBridge->clearMetar();
384           _atis = "";
385         }
386       }
387       break;
388
389       default:
390         _metarBridge->clearMetar();
391         _atis = "";
392         break;
393     }
394
395 }
396
397 SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode )
398 {
399     return new CommRadioImpl( rootNode );
400 }
401
402 } // namespace Instrumentation
403