]> git.mxchange.org Git - flightgear.git/blob - src/Environment/environment_ctrl.cxx
Fix stray back-button in Qt launcher
[flightgear.git] / src / Environment / environment_ctrl.cxx
1 // environment_ctrl.cxx -- manager for natural environment information.
2 //
3 // Written by David Megginson, started February 2002.
4 // Partly rewritten by Torsten Dreyer, August 2010.
5 //
6 // Copyright (C) 2002  David Megginson - david@megginson.com
7 //
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16 // General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 //
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <algorithm>
28
29 #include <simgear/math/SGMath.hxx>
30 #include <Main/fg_props.hxx>
31 #include "environment_ctrl.hxx"
32 #include "environment.hxx"
33
34 namespace Environment {
35
36 /**
37  * @brief Describes an element of a LayerTable. A defined environment at a given altitude.
38 */
39 struct LayerTableBucket {
40     double altitude_ft;
41     FGEnvironment environment;
42     inline bool operator< (const LayerTableBucket &b) const {
43         return (altitude_ft < b.altitude_ft);
44     }
45     /** 
46     * @brief LessThan predicate for bucket pointers.
47     */
48     static bool lessThan(LayerTableBucket *a, LayerTableBucket *b) {
49         return (a->altitude_ft) < (b->altitude_ft);
50     }
51 };
52
53 //////////////////////////////////////////////////////////////////////////////
54
55 /**
56  * @brief Models a column of our atmosphere by stacking a number of environments above
57  *        each other
58  */
59 class LayerTable : public std::vector<LayerTableBucket *>, public SGPropertyChangeListener
60 {
61 public:
62     LayerTable( SGPropertyNode_ptr rootNode ) :
63       _rootNode(rootNode) {}
64
65     ~LayerTable();
66
67     /**
68      * @brief Read the environment column from properties relative to the given root node
69      * @param environment A template environment to copy values from, not given in the configuration
70      */
71     void read( FGEnvironment * parent = NULL );
72
73     /**
74      *@brief Interpolate and write environment values for a given altitude
75      *@param altitude_ft The altitude for the desired environment
76      *@environment the destination to write the resulting environment properties to
77      */
78     void interpolate(double altitude_ft, FGEnvironment * environment);
79
80     /**
81      *@brief Bind all environments properties to property nodes and initialize the listeners
82      */
83     void Bind();
84
85     /**
86      *@brief Unbind all environments properties from property nodes and deregister listeners
87      */
88     void Unbind();
89 private:
90     /**
91      * @brief Implementation of SGProertyChangeListener::valueChanged()
92      *        Takes care of consitent sea level pressure for the entire column
93      */
94     void valueChanged( SGPropertyNode * node );
95     SGPropertyNode_ptr _rootNode;
96 };
97
98 //////////////////////////////////////////////////////////////////////////////
99
100
101 /**
102  *@brief Implementation of the LayerIterpolateController
103  */
104 class LayerInterpolateControllerImplementation : public LayerInterpolateController
105 {
106 public:
107     LayerInterpolateControllerImplementation( SGPropertyNode_ptr rootNode );
108     
109     virtual void init ();
110     virtual void reinit ();
111     virtual void postinit();
112     virtual void bind();
113     virtual void unbind();
114     virtual void update (double delta_time_sec);
115
116 private:
117     SGPropertyNode_ptr _rootNode;
118     bool _enabled;
119     double _boundary_transition;
120     SGPropertyNode_ptr _altitude_n;
121     SGPropertyNode_ptr _altitude_agl_n;
122
123     LayerTable _boundary_table;
124     LayerTable _aloft_table;
125
126     FGEnvironment _environment;
127     simgear::TiedPropertyList _tiedProperties;
128 };
129
130 //////////////////////////////////////////////////////////////////////////////
131
132 LayerTable::~LayerTable() 
133 {
134     for( iterator it = begin(); it != end(); it++ )
135         delete (*it);
136 }
137
138 void LayerTable::read(FGEnvironment * parent )
139 {
140     double last_altitude_ft = 0.0;
141     double sort_required = false;
142     size_t i;
143
144     for (i = 0; i < (size_t)_rootNode->nChildren(); i++) {
145         const SGPropertyNode * child = _rootNode->getChild(i);
146         if ( child->getNameString() == "entry"
147          && child->getStringValue("elevation-ft", "")[0] != '\0'
148          && ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) )
149     {
150             LayerTableBucket * b;
151             if( i < size() ) {
152                 // recycle existing bucket
153                 b = at(i);
154             } else {
155                 // more nodes than buckets in table, add a new one
156                 b = new LayerTableBucket;
157                 push_back(b);
158             }
159             if (i == 0 && parent != NULL )
160                 b->environment = *parent;
161             if (i > 0)
162                 b->environment = at(i-1)->environment;
163             
164             b->environment.read(child);
165             b->altitude_ft = b->environment.get_elevation_ft();
166
167             // check, if altitudes are in ascending order
168             if( b->altitude_ft < last_altitude_ft )
169                 sort_required = true;
170             last_altitude_ft = b->altitude_ft;
171         }
172     }
173     // remove leftover buckets
174     while( size() > i ) {
175         LayerTableBucket * b = *(end() - 1);
176         delete b;
177         pop_back();
178     }
179
180     if( sort_required )
181         sort(begin(), end(), LayerTableBucket::lessThan);
182
183     // cleanup entries with (almost)same altitude
184     for( size_type n = 1; n < size(); n++ ) {
185         if( fabs(at(n)->altitude_ft - at(n-1)->altitude_ft ) < 1 ) {
186             SG_LOG( SG_ENVIRONMENT, SG_ALERT, "Removing duplicate altitude entry in environment config for altitude " << at(n)->altitude_ft );
187             erase( begin() + n );
188         }
189     }
190 }
191
192 void LayerTable::Bind()
193 {
194     // tie all environments to ~/entry[n]/xxx
195     // register this as a changelistener of ~/entry[n]/pressure-sea-level-inhg
196     for( unsigned i = 0; i < size(); i++ ) {
197         SGPropertyNode_ptr baseNode = _rootNode->getChild("entry", i, true );
198         at(i)->environment.Tie( baseNode );
199         baseNode->getNode( "pressure-sea-level-inhg", true )->addChangeListener( this );
200     }
201 }
202
203 void LayerTable::Unbind()
204 {
205     // untie all environments to ~/entry[n]/xxx
206     // deregister this as a changelistener of ~/entry[n]/pressure-sea-level-inhg
207     for( unsigned i = 0; i < size(); i++ ) {
208         SGPropertyNode_ptr baseNode = _rootNode->getChild("entry", i, true );
209         at(i)->environment.Untie();
210         baseNode->getNode( "pressure-sea-level-inhg", true )->removeChangeListener( this );
211     }
212 }
213
214 void LayerTable::valueChanged( SGPropertyNode * node ) 
215 {
216     // Make sure all environments in our column use the same sea level pressure
217     double value = node->getDoubleValue();
218     for( iterator it = begin(); it != end(); it++ )
219         (*it)->environment.set_pressure_sea_level_inhg( value );
220 }
221
222
223 void LayerTable::interpolate( double altitude_ft, FGEnvironment * result )
224 {
225     int length = size();
226     if (length == 0)
227         return;
228
229     // Boundary conditions
230     if ((length == 1) || (at(0)->altitude_ft >= altitude_ft)) {
231         *result = at(0)->environment; // below bottom of table
232         return;
233     } else if (at(length-1)->altitude_ft <= altitude_ft) {
234         *result = at(length-1)->environment; // above top of table
235         return;
236     } 
237
238     // Search the interpolation table
239     int layer;
240     for ( layer = 1; // can't be below bottom layer, handled above
241           layer < length && at(layer)->altitude_ft <= altitude_ft;
242           layer++);
243     FGEnvironment & env1 = (at(layer-1)->environment);
244     FGEnvironment & env2 = (at(layer)->environment);
245     // two layers of same altitude were sorted out in read_table
246     double fraction = ((altitude_ft - at(layer-1)->altitude_ft) /
247                       (at(layer)->altitude_ft - at(layer-1)->altitude_ft));
248     env1.interpolate(env2, fraction, result);
249 }
250
251 //////////////////////////////////////////////////////////////////////////////
252
253 LayerInterpolateControllerImplementation::LayerInterpolateControllerImplementation( SGPropertyNode_ptr rootNode ) :
254   _rootNode( rootNode ),
255   _enabled(true),
256   _boundary_transition(0.0),
257   _altitude_n( fgGetNode("/position/altitude-ft", true)),
258   _altitude_agl_n( fgGetNode("/position/altitude-agl-ft", true)),
259   _boundary_table( rootNode->getNode("boundary", true ) ),
260   _aloft_table( rootNode->getNode("aloft", true ) )
261 {
262 }
263
264 void LayerInterpolateControllerImplementation::init ()
265 {
266     _boundary_table.read();
267     // pass in a pointer to the environment of the last bondary layer as
268     // a starting point
269     _aloft_table.read(&(*(_boundary_table.end()-1))->environment);
270 }
271
272 void LayerInterpolateControllerImplementation::reinit ()
273 {
274     _boundary_table.Unbind();
275     _aloft_table.Unbind();
276     init();
277     postinit();
278 }
279
280 void LayerInterpolateControllerImplementation::postinit()
281 {
282     // we get here after 1. bind() and 2. init() was called by fg_init
283     _boundary_table.Bind();
284     _aloft_table.Bind();
285 }
286
287 void LayerInterpolateControllerImplementation::bind()
288 {
289     // don't bind the layer tables here, because they have not been read in yet.
290     _environment.Tie( _rootNode->getNode( "interpolated", true ) );
291     _tiedProperties.Tie( _rootNode->getNode("enabled", true), &_enabled );
292     _tiedProperties.Tie( _rootNode->getNode("boundary-transition-ft", true ), &_boundary_transition );
293 }
294
295 void LayerInterpolateControllerImplementation::unbind()
296 {
297     _boundary_table.Unbind();
298     _aloft_table.Unbind();
299     _tiedProperties.Untie();
300     _environment.Untie();
301 }
302
303 void LayerInterpolateControllerImplementation::update (double delta_time_sec)
304 {
305     if( !_enabled || delta_time_sec <= SGLimitsd::min() )
306         return;
307
308     double altitude_ft = _altitude_n->getDoubleValue();
309     double altitude_agl_ft = _altitude_agl_n->getDoubleValue();
310
311     // avoid div by zero later on and init with a default value if not given
312     if( _boundary_transition <= SGLimitsd::min() )
313         _boundary_transition = 500;
314
315     int length = _boundary_table.size();
316
317     if (length > 0) {
318         // If a boundary table is defined, get the top of the boundary layer
319         double boundary_limit = _boundary_table[length-1]->altitude_ft;
320         if (boundary_limit >= altitude_agl_ft) {
321             // If current altitude is below top of boundary layer, interpolate
322             // only in boundary layer
323             _boundary_table.interpolate(altitude_agl_ft, &_environment);
324             return;
325         } else if ((boundary_limit + _boundary_transition) >= altitude_agl_ft) {
326             // If current altitude is above top of boundary layer and within the 
327             // transition altitude, interpolate boundary and aloft layers
328             FGEnvironment env1, env2;
329             _boundary_table.interpolate( altitude_agl_ft, &env1);
330             _aloft_table.interpolate(altitude_ft, &env2);
331             double fraction = (altitude_agl_ft - boundary_limit) / _boundary_transition;
332             env1.interpolate(env2, fraction, &_environment);
333             return;
334         }
335     } 
336     // If no boundary layer is defined or altitude is above top boundary-layer plus boundary-transition
337     // altitude, use only the aloft table
338     _aloft_table.interpolate( altitude_ft, &_environment);
339 }
340
341 //////////////////////////////////////////////////////////////////////////////
342
343 LayerInterpolateController * LayerInterpolateController::createInstance( SGPropertyNode_ptr rootNode )
344 {
345     return new LayerInterpolateControllerImplementation( rootNode );
346 }
347
348 //////////////////////////////////////////////////////////////////////////////
349
350 } // namespace