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