]> git.mxchange.org Git - flightgear.git/blob - src/Environment/terrainsampler.cxx
460f0336dcc23841e21d38f326541e44060b158f
[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
49     double getOrientationDeg() const { return _orientation_rad * SG_RADIANS_TO_DEGREES; }
50     void setOrientationDeg( double value ) { _orientation_rad = value * SG_DEGREES_TO_RADIANS; }
51
52     int getElevationHistogramStep() const { return _elevationHistogramStep; }
53     void setElevationHistograpStep( int value ) { 
54         _elevationHistogramStep = value > 0 ? value : 500;
55         _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
56     }
57
58     int getElevationHistogramMax() const { return _elevationHistogramMax; }
59     void setElevationHistograpMax( int value ) { 
60         _elevationHistogramMax = value > 0 ? value : 10000;
61         _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
62     }
63
64     int getElevationHistogramCount() const { return _elevationHistogramCount; }
65
66 private:
67     void analyse();
68
69     SGPropertyNode_ptr _rootNode;
70
71     bool _enabled;
72     bool _useAircraftPosition;
73         double _latitude_deg;
74         double _longitude_deg;
75         double _orientation_rad;
76         int _radius;
77         int _samples_per_frame;
78     int _max_samples;    // keep xx samples in queue for analysis
79     int _analyze_every;  // Run analysis every xx samples
80     int _elevationHistogramMax;
81     int _elevationHistogramStep;
82     int _elevationHistogramCount;
83
84     double _altOffset;
85     double _altMedian;
86     double _altMin;
87     double _altLayered;
88     double _altMean;
89
90     SGPropertyNode_ptr _positionLatitudeNode;
91     SGPropertyNode_ptr _positionLongitudeNode;
92
93     deque<double> elevations;
94     TiedPropertyList _tiedProperties;
95 };
96
97 AreaSampler::AreaSampler( SGPropertyNode_ptr rootNode ) :
98     _rootNode(rootNode),
99     _enabled(true),
100     _useAircraftPosition(false),
101     _latitude_deg(0.0),
102         _longitude_deg(0.0),
103         _orientation_rad(0.0),
104         _radius(40000.0),
105         _samples_per_frame(5),
106     _max_samples(1000),
107     _analyze_every(200),
108     _elevationHistogramMax(10000),
109     _elevationHistogramStep(500),
110     _elevationHistogramCount(_elevationHistogramMax/_elevationHistogramStep),
111     _altOffset(0),
112     _altMedian(0),
113     _altMin(0),
114     _altLayered(0),
115     _altMean(0)
116 {
117     _positionLatitudeNode = fgGetNode( "/position/latitude-deg", true );
118     _positionLongitudeNode = fgGetNode( "/position/longitude-deg", true );
119 }
120
121 AreaSampler::~AreaSampler()
122 {
123 }
124
125
126 void AreaSampler::bind()
127 {
128     _tiedProperties.setRoot( _rootNode );
129     _tiedProperties.Tie( "enabled", &_enabled );
130
131     _tiedProperties.setRoot( _rootNode->getNode( "input", true ) );
132     _tiedProperties.Tie( "use-aircraft-position", &_useAircraftPosition );
133     _tiedProperties.Tie( "latitude-deg", &_latitude_deg );
134     _tiedProperties.Tie( "latitude-deg", &_latitude_deg );
135     _tiedProperties.Tie( "longitude-deg", &_longitude_deg );
136     _tiedProperties.Tie( "orientation-deg", this, &AreaSampler::getOrientationDeg, &AreaSampler::setOrientationDeg );
137     _tiedProperties.Tie( "radius-m", &_radius );
138     _tiedProperties.Tie( "max-samples-per-frame", &_samples_per_frame );
139     _tiedProperties.Tie( "max-samples", &_max_samples );
140     _tiedProperties.Tie( "analyse-every", &_analyze_every );
141     _tiedProperties.Tie( "elevation-histogram-max-ft", this, &AreaSampler::getElevationHistogramMax, &AreaSampler::setElevationHistograpMax );
142     _tiedProperties.Tie( "elevation-histogram-step-ft", this, &AreaSampler::getElevationHistogramStep, &AreaSampler::setElevationHistograpStep );
143     _tiedProperties.Tie( "elevation-histogram-count", this, &AreaSampler::getElevationHistogramCount );
144
145     _tiedProperties.setRoot( _rootNode->getNode( "output", true ) );
146     _tiedProperties.Tie( "alt-offset-ft", &_altOffset );
147     _tiedProperties.Tie( "alt-median-ft", &_altMedian );
148     _tiedProperties.Tie( "alt-min-ft", &_altMin );
149     _tiedProperties.Tie( "alt-layered-ft", &_altLayered );
150     _tiedProperties.Tie( "alt-mean-ft", &_altMean );
151 }
152
153 void AreaSampler::unbind()
154 {
155     _tiedProperties.Untie();
156 }
157
158 void AreaSampler::update( double dt )
159 {
160         if( !(_enabled && dt > SGLimitsd::min()) )
161         return;
162
163     if( _useAircraftPosition ) {
164         _longitude_deg = _positionLongitudeNode->getDoubleValue();
165         _latitude_deg = _positionLatitudeNode->getDoubleValue();
166     }
167
168     SGGeoc center = SGGeoc::fromGeod( SGGeod::fromDegM( _longitude_deg, _latitude_deg, SG_MAX_ELEVATION_M ) );
169
170     FGScenery * scenery = globals->get_scenery();
171         for( int i = 0; 
172                 i < _samples_per_frame; 
173                 i++ ) {
174
175                 double distance = sg_random() * _radius;
176                 double course = sg_random() * 2.0 * SG_PI;
177                 SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, distance ));
178                 double elevation_m = 0.0;
179             if (scenery->get_elevation_m( probe, elevation_m, NULL ))
180             elevations.push_front(elevation_m *= SG_METER_TO_FEET);
181         
182         if( elevations.size() >= (deque<unsigned>::size_type)_max_samples ) {
183             analyse();
184             elevations.resize( _max_samples - _analyze_every );
185         }
186         }
187 }
188
189 void AreaSampler::analyse()
190 {
191     double sum;
192
193         vector<int> histogram(_elevationHistogramCount,0);
194     
195     for( deque<double>::size_type i = 0; i < elevations.size(); i++ ) {
196         int idx = SGMisc<int>::clip( (int)(elevations[i]/_elevationHistogramStep), 0, histogram.size()-1 );
197         histogram[idx]++;
198     }
199
200     _altMedian = 0.0;
201     sum = 0.0;
202     for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
203         sum += histogram[i];
204         if( sum > 0.5 * elevations.size() ) {
205             _altMedian = i * _elevationHistogramStep;
206             break;
207         }
208     }
209
210     _altOffset = 0.0;
211     sum = 0.0;
212     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
213         sum += histogram[i];
214         if( sum > 0.3 * elevations.size() ) {
215             _altOffset = i * _elevationHistogramStep;
216             break;
217         }
218     }
219
220    _altMean = 0.0;
221     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
222         _altMean += histogram[i] * i;
223     }
224     _altMean *= _elevationHistogramStep;
225     if( elevations.size() != 0.0 ) _altMean /= elevations.size();
226
227     _altMin = 0.0;
228     for(  vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
229         if( histogram[i] > 0 ) {
230             _altMin = i * _elevationHistogramStep;
231             break;
232         }
233     }
234
235     double alt_low_min = 0.0;
236     double n_max = 0.0;
237     sum = 0.0;
238     for(  vector<int>::size_type i = 0; i < histogram.size()-1; i++ ) {
239         sum += histogram[i];
240         if( histogram[i] > n_max ) n_max = histogram[i];
241         if( n_max > histogram[i+1] && sum > 0.3*elevations.size()) {
242             alt_low_min = i * _elevationHistogramStep;
243             break;
244         }
245     }
246
247     _altLayered = 0.5 * (_altMin + _altOffset);
248
249 #if 0
250     SG_LOG( SG_ALL, SG_ALERT, "TerrainPresampler - alalysis results:" <<
251         " total:" << elevations.size() <<
252         " mean:" << _altMean <<
253         " median:" << _altMedian <<
254         " min:" << _altMin <<
255         " alt_20:" << _altOffset );
256 #endif
257 #if 0
258 append(alt_50_array, alt_med);
259 #endif
260 }
261
262 /* --------------------- End of AreaSampler implementation ------------- */
263
264 /* --------------------- TerrainSamplerImplementation -------------------------- */
265
266 class TerrainSamplerImplementation : public TerrainSampler
267 {
268 public:
269         TerrainSamplerImplementation ( SGPropertyNode_ptr rootNode );
270         virtual ~TerrainSamplerImplementation ();
271         
272         virtual void init ();
273         virtual void reinit ();
274     virtual void bind();
275     virtual void unbind();
276         virtual void update (double delta_time_sec);
277 private:
278     inline string areaSubsystemName( unsigned i ) {
279       ostringstream name;
280       name <<  "area" << i;
281       return name.str();
282     }
283
284         SGPropertyNode_ptr _rootNode;
285     bool _enabled;
286     TiedPropertyList _tiedProperties;
287 };
288
289 TerrainSamplerImplementation::TerrainSamplerImplementation( SGPropertyNode_ptr rootNode ) :
290     _rootNode( rootNode ),
291     _enabled(true)
292 {
293 }
294
295 TerrainSamplerImplementation::~TerrainSamplerImplementation()
296 {
297 }
298
299 void TerrainSamplerImplementation::init()
300 {
301     PropertyList areaNodes = _rootNode->getChildren( "area" );
302     
303     for( PropertyList::size_type i = 0; i < areaNodes.size(); i++ )
304         set_subsystem( areaSubsystemName(i), new AreaSampler( areaNodes[i] ) );
305
306     SGSubsystemGroup::bind();// bind the subsystems before the get init()ed
307     SGSubsystemGroup::init();
308 }
309
310 void TerrainSamplerImplementation::reinit()
311 {
312     for( unsigned i = 0;; i++ ) {
313         string subsystemName = areaSubsystemName(i);
314         SGSubsystem * subsys = get_subsystem( subsystemName );
315         if( subsys == NULL )
316             break;
317         remove_subsystem( subsystemName );
318     }
319     
320     init();
321 }
322
323 void TerrainSamplerImplementation::bind()
324 {
325     SGSubsystemGroup::bind();
326     _tiedProperties.Tie( _rootNode->getNode("enabled",true), &_enabled );
327 }
328
329 void TerrainSamplerImplementation::unbind()
330 {
331     _tiedProperties.Untie();
332     SGSubsystemGroup::unbind();
333 }
334
335 void TerrainSamplerImplementation::update( double dt )
336 {
337         if( !(_enabled && dt > SGLimitsd::min()) )
338                 return;
339     SGSubsystemGroup::update(dt);
340 }
341
342 /* ----------------------------------------------------------------------- */
343
344 /* implementation of the TerrainSampler factory to hide the implementation
345    details */
346 TerrainSampler::~TerrainSampler ()
347 {
348 }
349
350 TerrainSampler * TerrainSampler::createInstance( SGPropertyNode_ptr rootNode )
351 {
352         return new TerrainSamplerImplementation( rootNode );
353 }
354
355 } // namespace
356