]> git.mxchange.org Git - flightgear.git/blob - src/Environment/terrainsampler.cxx
#84: John Denker: Set correct file modes
[flightgear.git] / src / Environment / terrainsampler.cxx
1 // terrainsampler.cxx --
2 //
3 // Written by Torsten Dreyer, started July 2010
4 // Based on local weather implementation in nasal from 
5 // Thorsten Renk
6 //
7 // Copyright (C) 2010  Curtis Olson
8 //
9 // This program is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU General Public License as
11 // published by the Free Software Foundation; either version 2 of the
12 // License, or (at your option) any later version.
13 //
14 // This program is distributed in the hope that it will be useful, but
15 // WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17 // General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
22 //
23 #ifdef HAVE_CONFIG_H
24 #  include <config.h>
25 #endif
26
27 #include <Main/fg_props.hxx>
28 #include <simgear/math/sg_random.h>
29 #include <Scenery/scenery.hxx>
30 #include <deque>
31
32 #include "terrainsampler.hxx"
33 using simgear::PropertyList;
34
35 #include "tiedpropertylist.hxx"
36
37 namespace Environment {
38 /**
39  * @brief Class for presampling the terrain roughness
40  */
41 class AreaSampler : public SGSubsystem {
42 public:
43     AreaSampler( SGPropertyNode_ptr rootNode );
44     virtual ~AreaSampler();
45     void update( double dt );
46     void bind();
47     void unbind();
48     void init();
49     void reinit();
50
51     int getElevationHistogramStep() const { return _elevationHistogramStep; }
52     void setElevationHistograpStep( int value ) { 
53         _elevationHistogramStep = value > 0 ? value : 500;
54         _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
55     }
56
57     int getElevationHistogramMax() const { return _elevationHistogramMax; }
58     void setElevationHistograpMax( int value ) { 
59         _elevationHistogramMax = value > 0 ? value : 10000;
60         _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
61     }
62
63     int getElevationHistogramCount() const { return _elevationHistogramCount; }
64
65 private:
66     void analyse();
67
68     SGPropertyNode_ptr _rootNode;
69
70     bool _enabled;
71     bool _useAircraftPosition;
72     double _heading_deg;
73     double _speed_kt;
74     int _radius;
75     double _max_computation_time_norm;
76     int _max_samples;    // keep xx samples in queue for analysis
77     double _reuse_samples_norm;
78     double _recalc_distance_norm;
79     int _elevationHistogramMax;
80     int _elevationHistogramStep;
81     int _elevationHistogramCount;
82     SGGeod _inputPosition;
83
84     double _altOffset;
85     double _altMedian;
86     double _altMin;
87     double _altLayered;
88     double _altMean;
89     SGGeod _outputPosition;
90
91     SGPropertyNode_ptr _signalNode;
92     SGPropertyNode_ptr _positionLatitudeNode;
93     SGPropertyNode_ptr _positionLongitudeNode;
94
95     deque<double> _elevations;
96     TiedPropertyList _tiedProperties;
97 };
98
99 AreaSampler::AreaSampler( SGPropertyNode_ptr rootNode ) :
100     _rootNode(rootNode),
101     _enabled(true),
102     _useAircraftPosition(false),
103     _heading_deg(0.0),
104     _speed_kt(0.0),
105     _radius(40000.0),
106     _max_computation_time_norm(0.1),
107     _max_samples(1000),
108     _reuse_samples_norm(0.8),
109     _recalc_distance_norm(0.1),
110     _elevationHistogramMax(10000),
111     _elevationHistogramStep(500),
112     _elevationHistogramCount(_elevationHistogramMax/_elevationHistogramStep),
113     _altOffset(0),
114     _altMedian(0),
115     _altMin(0),
116     _altLayered(0),
117     _altMean(0),
118     _signalNode(rootNode->getNode("output/valid", true )),
119     _positionLatitudeNode(fgGetNode( "/position/latitude-deg", true )),
120     _positionLongitudeNode(fgGetNode( "/position/longitude-deg", true ))
121 {
122     _inputPosition.setElevationM( SG_MAX_ELEVATION_M );
123 }
124
125 AreaSampler::~AreaSampler()
126 {
127 }
128
129
130 void AreaSampler::bind()
131 {
132     _tiedProperties.setRoot( _rootNode );
133     _tiedProperties.Tie( "enabled", &_enabled );
134
135     _tiedProperties.setRoot( _rootNode->getNode( "input", true ) );
136     _tiedProperties.Tie( "use-aircraft-position", &_useAircraftPosition );
137     _tiedProperties.Tie( "latitude-deg", &_inputPosition, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg );
138     _tiedProperties.Tie( "longitude-deg", &_inputPosition, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg );
139     _tiedProperties.Tie( "heading-deg", &_heading_deg );
140     _tiedProperties.Tie( "speed-kt", &_speed_kt );
141     _tiedProperties.Tie( "radius-m", &_radius );
142     _tiedProperties.Tie( "max-computation-time-norm", &_max_computation_time_norm );
143     _tiedProperties.Tie( "max-samples", &_max_samples );
144     _tiedProperties.Tie( "reuse-samples-norm", &_reuse_samples_norm );
145     _tiedProperties.Tie( "recalc-distance-norm", &_recalc_distance_norm );
146     _tiedProperties.Tie( "elevation-histogram-max-ft", this, &AreaSampler::getElevationHistogramMax, &AreaSampler::setElevationHistograpMax );
147     _tiedProperties.Tie( "elevation-histogram-step-ft", this, &AreaSampler::getElevationHistogramStep, &AreaSampler::setElevationHistograpStep );
148     _tiedProperties.Tie( "elevation-histogram-count", this, &AreaSampler::getElevationHistogramCount );
149
150     _tiedProperties.setRoot( _rootNode->getNode( "output", true ) );
151     _tiedProperties.Tie( "alt-offset-ft", &_altOffset );
152     _tiedProperties.Tie( "alt-median-ft", &_altMedian );
153     _tiedProperties.Tie( "alt-min-ft", &_altMin );
154     _tiedProperties.Tie( "alt-layered-ft", &_altLayered );
155     _tiedProperties.Tie( "alt-mean-ft", &_altMean );
156     _tiedProperties.Tie( "longitude-deg", &_outputPosition, &SGGeod::getLongitudeDeg );
157     _tiedProperties.Tie( "latitude-deg", &_outputPosition, &SGGeod::getLatitudeDeg );
158
159 }
160
161 void AreaSampler::unbind()
162 {
163     _tiedProperties.Untie();
164 }
165
166 void AreaSampler::init()
167 {
168    _signalNode->setBoolValue(false);
169    _elevations.clear();
170    _altOffset = 0.0;
171    _altMedian = 0.0;
172    _altMin = 0.0;
173    _altLayered = 0.0;
174    _altMean = 0.0;
175 }
176
177 void AreaSampler::reinit()
178 {
179     init();
180 }
181
182 void AreaSampler::update( double dt )
183 {
184     // if not enabled or time has stalled, do nothing
185     if( !(_enabled && dt > SGLimitsd::min()) )
186         return;
187
188     // get the aircraft's position if requested
189     if( _useAircraftPosition && _speed_kt < 0.5 ) {
190         _inputPosition = SGGeod::fromDegM( 
191             _positionLongitudeNode->getDoubleValue(), 
192             _positionLatitudeNode->getDoubleValue(), 
193             SG_MAX_ELEVATION_M );
194     }
195
196     // need geocentric coordinates
197     SGGeoc center = SGGeoc::fromGeod( _inputPosition );
198
199     // if a speed is set, move the input position
200     if( _speed_kt >= 0.5 ) {
201         double distance_m = _speed_kt * dt * SG_NM_TO_METER;
202         center = center.advanceRadM( _heading_deg * SG_DEGREES_TO_RADIANS, distance_m );
203         _inputPosition = SGGeod::fromGeoc( center );
204     }
205
206     if( _signalNode->getBoolValue() ) {
207         // if we had finished the iteration and moved more than 10% of the radius
208         // of the sampling area, drop the oldest samples and continue sampling
209         if( SGGeoc::distanceM( center, SGGeoc::fromGeod(_outputPosition ) ) >= _recalc_distance_norm * _radius ) {
210             _elevations.resize( _max_samples * _reuse_samples_norm );
211             _signalNode->setBoolValue( false );
212         }
213     }
214
215     if( _signalNode->getBoolValue() )
216         return; // nothing to do.
217
218     FGScenery * scenery = globals->get_scenery();
219
220     SGTimeStamp start = SGTimeStamp::now();
221     while( (SGTimeStamp::now() - start).toSecs() < dt * _max_computation_time_norm ) {
222         // sample until we used up all our configured time
223         double distance = sg_random();
224         distance = _radius * (1-distance*distance);
225         double course = sg_random() * 2.0 * SG_PI;
226         SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, distance ));
227         double elevation_m = 0.0;
228
229         if (scenery->get_elevation_m( probe, elevation_m, NULL )) 
230             _elevations.push_front(elevation_m *= SG_METER_TO_FEET);
231         
232         if( _elevations.size() >= (deque<unsigned>::size_type)_max_samples ) {
233             // sampling complete? 
234             analyse();
235             _outputPosition = _inputPosition;
236             _signalNode->setBoolValue( true );
237             break;
238         }
239     }
240 }
241
242 void AreaSampler::analyse()
243 {
244     double sum;
245
246     vector<int> histogram(_elevationHistogramCount,0);
247     
248     for( deque<double>::size_type i = 0; i < _elevations.size(); i++ ) {
249         int idx = SGMisc<int>::clip( (int)(_elevations[i]/_elevationHistogramStep), 0, histogram.size()-1 );
250         histogram[idx]++;
251     }
252
253     _altMedian = 0.0;
254     sum = 0.0;
255     for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
256         sum += histogram[i];
257         if( sum > 0.5 * _elevations.size() ) {
258             _altMedian = i * _elevationHistogramStep;
259             break;
260         }
261     }
262
263     _altOffset = 0.0;
264     sum = 0.0;
265     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
266         sum += histogram[i];
267         if( sum > 0.3 * _elevations.size() ) {
268             _altOffset = i * _elevationHistogramStep;
269             break;
270         }
271     }
272
273    _altMean = 0.0;
274     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
275         _altMean += histogram[i] * i;
276     }
277     _altMean *= _elevationHistogramStep;
278     if( _elevations.size() != 0.0 ) _altMean /= _elevations.size();
279
280     _altMin = 0.0;
281     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
282         if( histogram[i] > 0 ) {
283             _altMin = i * _elevationHistogramStep;
284             break;
285         }
286     }
287
288     double alt_low_min = 0.0;
289     double n_max = 0.0;
290     sum = 0.0;
291     for(  vector<int>::size_type i = 0; i < histogram.size()-1; i++ ) {
292         sum += histogram[i];
293         if( histogram[i] > n_max ) n_max = histogram[i];
294         if( n_max > histogram[i+1] && sum > 0.3*_elevations.size()) {
295             alt_low_min = i * _elevationHistogramStep;
296             break;
297         }
298     }
299
300     _altLayered = 0.5 * (_altMin + _altOffset);
301
302 #if 0
303 append(alt_50_array, alt_med);
304 #endif
305 }
306
307 /* --------------------- End of AreaSampler implementation ------------- */
308
309 /* --------------------- TerrainSamplerImplementation -------------------------- */
310
311 class TerrainSamplerImplementation : public TerrainSampler
312 {
313 public:
314     TerrainSamplerImplementation ( SGPropertyNode_ptr rootNode );
315     virtual ~TerrainSamplerImplementation ();
316     
317     virtual void init ();
318     virtual void postinit();
319     virtual void reinit ();
320     virtual void bind();
321     virtual void unbind();
322     virtual void update (double delta_time_sec);
323 private:
324     inline string areaSubsystemName( unsigned i ) {
325       ostringstream name;
326       name <<  "area" << i;
327       return name.str();
328     }
329
330     SGPropertyNode_ptr _rootNode;
331     bool _enabled;
332     TiedPropertyList _tiedProperties;
333 };
334
335 TerrainSamplerImplementation::TerrainSamplerImplementation( SGPropertyNode_ptr rootNode ) :
336     _rootNode( rootNode ),
337     _enabled(true)
338 {
339 }
340
341 TerrainSamplerImplementation::~TerrainSamplerImplementation()
342 {
343 }
344
345 void TerrainSamplerImplementation::init()
346 {
347     PropertyList areaNodes = _rootNode->getChildren( "area" );
348     
349     for( PropertyList::size_type i = 0; i < areaNodes.size(); i++ )
350         set_subsystem( areaSubsystemName(i), new AreaSampler( areaNodes[i] ) );
351
352     SGSubsystemGroup::init();
353 }
354
355 void TerrainSamplerImplementation::postinit()
356 {
357     SGSubsystemGroup::bind();// 
358 }
359
360 void TerrainSamplerImplementation::reinit()
361 {
362     for( unsigned i = 0;; i++ ) {
363         string subsystemName = areaSubsystemName(i);
364         SGSubsystem * subsys = get_subsystem( subsystemName );
365         if( subsys == NULL )
366             break;
367         remove_subsystem( subsystemName );
368     }
369     
370     init();
371 }
372
373 void TerrainSamplerImplementation::bind()
374 {
375     SGSubsystemGroup::bind();
376     _tiedProperties.Tie( _rootNode->getNode("enabled",true), &_enabled );
377 }
378
379 void TerrainSamplerImplementation::unbind()
380 {
381     _tiedProperties.Untie();
382     SGSubsystemGroup::unbind();
383 }
384
385 void TerrainSamplerImplementation::update( double dt )
386 {
387     if( !(_enabled && dt > SGLimitsd::min()) )
388         return;
389     SGSubsystemGroup::update(dt);
390 }
391
392 /* ----------------------------------------------------------------------- */
393
394 /* implementation of the TerrainSampler factory to hide the implementation
395    details */
396 TerrainSampler * TerrainSampler::createInstance( SGPropertyNode_ptr rootNode )
397 {
398     return new TerrainSamplerImplementation( rootNode );
399 }
400
401 } // namespace
402