FlightGear next
environment_ctrl.cxx
Go to the documentation of this file.
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
34namespace Environment {
35
42 inline bool operator< (const LayerTableBucket &b) const {
43 return (altitude_ft < b.altitude_ft);
44 }
45
49 return (a->altitude_ft) < (b->altitude_ft);
50 }
51};
52
54
59class LayerTable : public std::vector<LayerTableBucket *>, public SGPropertyChangeListener
60{
61public:
62 LayerTable( SGPropertyNode_ptr rootNode ) :
63 _rootNode(rootNode) {}
64
66
71 void read( FGEnvironment * parent = NULL );
72
78 void interpolate(double altitude_ft, FGEnvironment * environment);
79
83 void Bind();
84
88 void Unbind();
89private:
94 void valueChanged( SGPropertyNode * node );
95 SGPropertyNode_ptr _rootNode;
96};
97
99
100
105{
106public:
107 LayerInterpolateControllerImplementation( SGPropertyNode_ptr rootNode );
108
109 // Subsystem API.
110 void bind() override;
111 void init() override;
112 void postinit() override;
113 void reinit() override;
114 void unbind() override;
115 void update(double delta_time_sec) override;
116
117 // Subsystem identification.
118 static const char* staticSubsystemClassId() { return "layer-interpolate-controller"; }
119
120private:
121 SGPropertyNode_ptr _rootNode;
122 bool _enabled;
123 double _boundary_transition;
124 SGPropertyNode_ptr _altitude_n;
125 SGPropertyNode_ptr _altitude_agl_n;
126
127 LayerTable _boundary_table;
128 LayerTable _aloft_table;
129
130 FGEnvironment _environment;
131 simgear::TiedPropertyList _tiedProperties;
132};
133
135
137{
138 for( iterator it = begin(); it != end(); it++ )
139 delete (*it);
140}
141
143{
144 double last_altitude_ft = 0.0;
145 double sort_required = false;
146 size_t i;
147
148 for (i = 0; i < (size_t)_rootNode->nChildren(); i++) {
149 const SGPropertyNode * child = _rootNode->getChild(i);
150 if ( child->getNameString() == "entry"
151 && child->getStringValue("elevation-ft", "")[0] != '\0'
152 && ( child->getDoubleValue("elevation-ft") > 0.1 || i == 0 ) )
153 {
155 if( i < size() ) {
156 // recycle existing bucket
157 b = at(i);
158 } else {
159 // more nodes than buckets in table, add a new one
160 b = new LayerTableBucket;
161 push_back(b);
162 }
163 if (i == 0 && parent != NULL )
164 b->environment = *parent;
165 if (i > 0)
166 b->environment = at(i-1)->environment;
167
168 b->environment.read(child);
170
171 // check, if altitudes are in ascending order
172 if( b->altitude_ft < last_altitude_ft )
173 sort_required = true;
174 last_altitude_ft = b->altitude_ft;
175 }
176 }
177 // remove leftover buckets
178 while( size() > i ) {
179 LayerTableBucket * b = *(end() - 1);
180 delete b;
181 pop_back();
182 }
183
184 if( sort_required )
185 sort(begin(), end(), LayerTableBucket::lessThan);
186
187 // cleanup entries with (almost)same altitude
188 for( size_type n = 1; n < size(); n++ ) {
189 if( fabs(at(n)->altitude_ft - at(n-1)->altitude_ft ) < 1 ) {
190 SG_LOG( SG_ENVIRONMENT, SG_ALERT, "Removing duplicate altitude entry in environment config for altitude " << at(n)->altitude_ft );
191 erase( begin() + n );
192 }
193 }
194}
195
197{
198 // tie all environments to ~/entry[n]/xxx
199 // register this as a changelistener of ~/entry[n]/pressure-sea-level-inhg
200 // and ~/entry[n]/elevation-ft
201 for( unsigned i = 0; i < size(); i++ ) {
202 SGPropertyNode_ptr baseNode = _rootNode->getChild("entry", i, true );
203 at(i)->environment.Tie( baseNode );
204 baseNode->getNode( "pressure-sea-level-inhg", true )->addChangeListener( this );
205 baseNode->getNode("elevation-ft", true)->addChangeListener(this);
206 }
207}
208
210{
211 // untie all environments to ~/entry[n]/xxx
212 // deregister this as a changelistener of ~/entry[n]/pressure-sea-level-inhg
213 // and ~/entry[n]/elevation-ft
214 for( unsigned i = 0; i < size(); i++ ) {
215 SGPropertyNode_ptr baseNode = _rootNode->getChild("entry", i, true );
216 at(i)->environment.Untie();
217 baseNode->getNode( "pressure-sea-level-inhg", true )->removeChangeListener( this );
218 baseNode->getNode("elevation-ft", true)->removeChangeListener(this);
219 }
220}
221
222void LayerTable::valueChanged( SGPropertyNode * node )
223{
224 // - Make sure all environments in our column use the same sea level pressure
225 // - Synchronize layer elevations
226 if (node->getNameString() == "pressure-sea-level-inhg") {
227 double value = node->getDoubleValue();
228 for (iterator it = begin(); it != end(); it++) {
229 (*it)->environment.set_pressure_sea_level_inhg(value);
230 }
231 } else {
232 bool sort_required = false;
233 double last_altitude_ft = 0.0;
234 for (iterator it = begin(); it != end(); it++) {
235 (*it)->altitude_ft = (*it)->environment.get_elevation_ft();
236 if ((*it)->altitude_ft < last_altitude_ft)
237 sort_required = true;
238 last_altitude_ft = (*it)->altitude_ft;
239 }
240 if (sort_required)
241 sort(begin(), end(), LayerTableBucket::lessThan);
242 }
243}
244
245
246void LayerTable::interpolate( double altitude_ft, FGEnvironment * result )
247{
248 int length = size();
249 if (length == 0)
250 return;
251
252 // Boundary conditions
253 if ((length == 1) || (at(0)->altitude_ft >= altitude_ft)) {
254 *result = at(0)->environment; // below bottom of table
255 return;
256 } else if (at(length-1)->altitude_ft <= altitude_ft) {
257 *result = at(length-1)->environment; // above top of table
258 return;
259 }
260
261 // Search the interpolation table
262 int layer;
263 for ( layer = 1; // can't be below bottom layer, handled above
264 layer < length && at(layer)->altitude_ft <= altitude_ft;
265 layer++);
266 FGEnvironment & env1 = (at(layer-1)->environment);
267 FGEnvironment & env2 = (at(layer)->environment);
268 // two layers of same altitude were sorted out in read_table
269 double fraction = ((altitude_ft - at(layer-1)->altitude_ft) /
270 (at(layer)->altitude_ft - at(layer-1)->altitude_ft));
271 env1.interpolate(env2, fraction, result);
272}
273
275
277 _rootNode( rootNode ),
278 _enabled(true),
279 _boundary_transition(0.0),
280 _altitude_n( fgGetNode("/position/altitude-ft", true)),
281 _altitude_agl_n( fgGetNode("/position/altitude-agl-ft", true)),
282 _boundary_table( rootNode->getNode("boundary", true ) ),
283 _aloft_table( rootNode->getNode("aloft", true ) )
284{
285}
286
288{
289 _boundary_table.read();
290 // pass in a pointer to the environment of the last bondary layer as
291 // a starting point
292 _aloft_table.read(&(*(_boundary_table.end()-1))->environment);
293}
294
296{
297 _boundary_table.Unbind();
298 _aloft_table.Unbind();
299 init();
300 postinit();
301}
302
304{
305 // we get here after 1. bind() and 2. init() was called by fg_init
306 _boundary_table.Bind();
307 _aloft_table.Bind();
308}
309
311{
312 // don't bind the layer tables here, because they have not been read in yet.
313 _environment.Tie( _rootNode->getNode( "interpolated", true ) );
314 _tiedProperties.Tie( _rootNode->getNode("enabled", true), &_enabled );
315 _tiedProperties.Tie( _rootNode->getNode("boundary-transition-ft", true ), &_boundary_transition );
316}
317
319{
320 _boundary_table.Unbind();
321 _aloft_table.Unbind();
322 _tiedProperties.Untie();
323 _environment.Untie();
324}
325
327{
328 if( !_enabled || delta_time_sec <= SGLimitsd::min() )
329 return;
330
331 double altitude_ft = _altitude_n->getDoubleValue();
332 double altitude_agl_ft = _altitude_agl_n->getDoubleValue();
333
334 // avoid div by zero later on and init with a default value if not given
335 if( _boundary_transition <= SGLimitsd::min() )
336 _boundary_transition = 500;
337
338 int length = _boundary_table.size();
339
340 if (length > 0) {
341 // If a boundary table is defined, get the top of the boundary layer
342 double boundary_limit = _boundary_table[length-1]->altitude_ft;
343 if (boundary_limit >= altitude_agl_ft) {
344 // If current altitude is below top of boundary layer, interpolate
345 // only in boundary layer
346 _boundary_table.interpolate(altitude_agl_ft, &_environment);
347 return;
348 } else if ((boundary_limit + _boundary_transition) >= altitude_agl_ft) {
349 // If current altitude is above top of boundary layer and within the
350 // transition altitude, interpolate boundary and aloft layers
351 FGEnvironment env1, env2;
352 _boundary_table.interpolate( altitude_agl_ft, &env1);
353 _aloft_table.interpolate(altitude_ft, &env2);
354 double fraction = (altitude_agl_ft - boundary_limit) / _boundary_transition;
355 env1.interpolate(env2, fraction, &_environment);
356 return;
357 }
358 }
359 // If no boundary layer is defined or altitude is above top boundary-layer plus boundary-transition
360 // altitude, use only the aloft table
361 _aloft_table.interpolate( altitude_ft, &_environment);
362}
363
365
370
372
373// Register the subsystem.
374#if 0
375SGSubsystemMgr::Registrant<LayerInterpolateControllerImplementation> registrantLayerInterpolateControllerImplementation;
376#endif
377
378} // namespace
#define i(x)
Implementation of the LayerIterpolateController.
LayerInterpolateControllerImplementation(SGPropertyNode_ptr rootNode)
static LayerInterpolateController * createInstance(SGPropertyNode_ptr rootNode)
Models a column of our atmosphere by stacking a number of environments above each other.
void read(FGEnvironment *parent=NULL)
Read the environment column from properties relative to the given root node.
LayerTable(SGPropertyNode_ptr rootNode)
void Unbind()
Unbind all environments properties from property nodes and deregister listeners.
void interpolate(double altitude_ft, FGEnvironment *environment)
Interpolate and write environment values for a given altitude.
void Bind()
Bind all environments properties to property nodes and initialize the listeners.
Model the natural environment.
virtual void read(const SGPropertyNode *node)
FGEnvironment & interpolate(const FGEnvironment &env2, double fraction, FGEnvironment *result) const
virtual double get_elevation_ft() const
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27
Describes an element of a LayerTable.
static bool lessThan(LayerTableBucket *a, LayerTableBucket *b)
LessThan predicate for bucket pointers.
bool operator<(const LayerTableBucket &b) const