]> git.mxchange.org Git - flightgear.git/blob - src/Instrumentation/commradio.cxx
Fix wrong metar assignment in commradio
[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 <Airports/airport.hxx>
38 #include <Main/fg_props.hxx>
39 #include <Navaids/navlist.hxx>
40
41 #include "frequencyformatter.hxx"
42
43 namespace Instrumentation {
44
45 using simgear::PropertyObject;
46 using std::string;
47
48
49 SignalQualityComputer::~SignalQualityComputer()
50 {
51 }
52
53 class SimpleDistanceSquareSignalQualityComputer : public SignalQualityComputer
54 {
55 public:
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;
60 private:
61   double _rangeM;
62   double _rangeM2;
63 };
64
65 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGVec3d & sender, const SGVec3d & receiver ) const
66 {
67     return computeSignalQuality( dist( sender, receiver ) );
68 }
69
70 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( const SGGeod & sender, const SGGeod & receiver ) const
71 {
72     return computeSignalQuality( SGGeodesy::distanceM( sender, receiver ) );
73 }
74
75 double SimpleDistanceSquareSignalQualityComputer::computeSignalQuality( double distanceM ) const
76 {
77     return distanceM < _rangeM ? 1.0 : ( _rangeM2 / (distanceM*distanceM) );
78 }
79
80 class OnExitHandler {
81   public:
82     virtual void onExit() = 0;
83 };
84
85 class OnExit {
86   public:
87     OnExit( OnExitHandler * onExitHandler ) : _onExitHandler( onExitHandler ) {}
88     ~OnExit() { _onExitHandler->onExit(); }
89   private:
90     OnExitHandler * _onExitHandler;
91 };
92
93
94 class OutputProperties : public OnExitHandler {
95   public:
96     OutputProperties( SGPropertyNode_ptr rootNode ) : 
97       _rootNode(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),
104
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) )
114       {}
115
116 protected:
117     SGPropertyNode_ptr _rootNode;
118
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;
128
129 private:
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;
139
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;
150     }
151 };
152
153 /* ------------- The CommRadio implementation ---------------------- */
154
155 class MetarBridge : public SGReferenced, public SGPropertyChangeListener {
156 public:
157   MetarBridge();
158   ~MetarBridge();
159
160   void bind();
161   void unbind();
162   void requestMetarForId( std::string & id );
163   void clearMetar();
164   void setMetarPropertiesRoot( SGPropertyNode_ptr n ) { _metarPropertiesNode = n; }
165   void setAtisNode( SGPropertyNode * n ) { _atisNode = n; }
166
167 protected:
168   virtual void valueChanged(SGPropertyNode * );
169
170 private:
171   std::string _requestedId;
172   SGPropertyNode_ptr _metarPropertiesNode;
173   SGPropertyNode * _atisNode;
174   ATISEncoder _atisEncoder;
175 };
176 typedef SGSharedPtr<MetarBridge> MetarBridgeRef;
177
178 MetarBridge::MetarBridge() :
179   _atisNode(0)
180 {
181 }
182
183 MetarBridge::~MetarBridge()
184 {
185 }
186
187 void MetarBridge::bind()
188 {
189   _metarPropertiesNode->getNode( "valid", true )->addChangeListener( this );
190 }
191
192 void MetarBridge::unbind()
193 {
194   _metarPropertiesNode->getNode( "valid", true )->removeChangeListener( this );
195 }
196
197 void MetarBridge::requestMetarForId( std::string & id )
198 {
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 );
205 }
206
207 void MetarBridge::clearMetar()
208 {
209   string empty;
210   requestMetarForId( empty );
211 }
212
213 void MetarBridge::valueChanged(SGPropertyNode * node )
214 {
215   // check for raising edge of valid flag
216   if( NULL == node || false == node->getBoolValue() )
217     return;
218
219   std::string responseId = simgear::strutils::uppercase(
220      _metarPropertiesNode->getNode( "station-id", true )->getStringValue() );
221
222   // unrequested metar!?
223   if( responseId != _requestedId )
224     return;
225
226   if( NULL != _atisNode ) {
227     MetarPropertiesATISInformationProvider provider( _metarPropertiesNode );
228     _atisNode->setStringValue( _atisEncoder.encodeATIS( &provider ) );
229   }
230 }
231
232 /* ------------- The CommRadio implementation ---------------------- */
233
234 class CommRadioImpl : public CommRadio, OutputProperties {
235
236 public:
237   CommRadioImpl( SGPropertyNode_ptr node );
238   virtual ~CommRadioImpl();
239
240   virtual void update( double dt );
241   virtual void init();
242   void bind();
243   void unbind();
244
245 private:
246   int                _num;
247   MetarBridgeRef     _metarBridge;
248   FrequencyFormatter _useFrequencyFormatter;
249   FrequencyFormatter _stbyFrequencyFormatter;
250   const SignalQualityComputerRef _signalQualityComputer; 
251
252   double _stationTTL;
253   double _frequency;
254   flightgear::CommStationRef _commStationForFrequency;
255
256   PropertyObject<bool>   _serviceable;
257   PropertyObject<bool>   _power_btn;
258   PropertyObject<bool>   _power_good;
259   PropertyObject<double> _volume_norm;
260   PropertyObject<string> _atis;
261
262
263 };
264
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) ),
276
277     _stationTTL(0.0),
278     _frequency(-1.0),
279     _commStationForFrequency(NULL),
280
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) )
286 {
287 }
288
289 CommRadioImpl::~CommRadioImpl()
290 {
291 }
292
293 void CommRadioImpl::bind()
294 {
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();
300 }
301
302 void CommRadioImpl::unbind()
303 {
304   _metarBridge->unbind();
305 }
306
307 void CommRadioImpl::init()
308 {
309 }
310
311 void CommRadioImpl::update( double dt )
312 {
313     if( dt < SGLimitsd::min() ) return;
314     _stationTTL -= dt;
315
316     // Ensure all output properties get written on exit of this method
317     OnExit onExit(this);
318
319     SGGeod position;
320     try { position = globals->get_aircraft_position(); }
321     catch( std::exception & ) { return; }
322
323     if( false == (_power_btn )) {
324         _stationTTL = 0.0;
325         return;
326     }
327
328
329     if( _frequency != _useFrequencyFormatter.getFrequency() ) {
330         _frequency = _useFrequencyFormatter.getFrequency();
331         _stationTTL = 0.0;
332     }
333
334     if( _stationTTL <= 0.0 ) {
335         _stationTTL = 30.0;
336
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 );
341
342     }
343
344     if( false == _commStationForFrequency.valid() ) return;
345
346     _slantDistance_m = dist(_commStationForFrequency->cart(), SGVec3d::fromGeod(position));
347
348     SGGeodesy::inverse(position, _commStationForFrequency->geod(), 
349         _trueBearingTo_deg,
350         _trueBearingFrom_deg,
351         _trackDistance_m );
352
353     _heightAboveStation_ft = 
354          SGMiscd::max(0.0, position.getElevationFt() 
355            - _commStationForFrequency->airport()->elevation());
356     
357     _signalQuality_norm = _signalQualityComputer->computeSignalQuality( _slantDistance_m );
358     _stationType = _commStationForFrequency->nameForType( _commStationForFrequency->type() );
359     _stationName = _commStationForFrequency->ident();
360     _airportId = _commStationForFrequency->airport()->getId();
361
362     switch( _commStationForFrequency->type() ) {
363       case FGPositioned::FREQ_ATIS:
364       case FGPositioned::FREQ_AWOS: {
365         if( _signalQuality_norm > 0.01 ) {
366           _metarBridge->requestMetarForId( _airportId );
367         } else {
368           _metarBridge->clearMetar();
369           _atis = "";
370         }
371       }
372       break;
373
374       default:
375         _metarBridge->clearMetar();
376         _atis = "";
377         break;
378     }
379
380 }
381
382 SGSubsystem * CommRadio::createInstance( SGPropertyNode_ptr rootNode )
383 {
384     return new CommRadioImpl( rootNode );
385 }
386
387 } // namespace Instrumentation
388