FlightGear next
terrainsampler.cxx
Go to the documentation of this file.
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.hxx>
29#include <Scenery/scenery.hxx>
30#include <deque>
31
32#include "terrainsampler.hxx"
33
34using simgear::PropertyList;
35using std::deque;
36using std::vector;
37using std::ostringstream;
38using std::string;
39
40#include <simgear/props/tiedpropertylist.hxx>
41
42namespace Environment {
43
47class AreaSampler : public SGSubsystem
48{
49public:
50 AreaSampler( SGPropertyNode_ptr rootNode );
51 virtual ~AreaSampler();
52
53 // Subsystem API.
54 void bind() override;
55 void init() override;
56 void reinit() override;
57 void unbind() override;
58 void update(double dt) override;
59
60 // Subsystem identification.
61 static const char* staticSubsystemClassId() { return "area"; }
62
63 int getElevationHistogramStep() const { return _elevationHistogramStep; }
64 void setElevationHistograpStep( int value ) {
65 _elevationHistogramStep = value > 0 ? value : 500;
66 _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
67 }
68
69 int getElevationHistogramMax() const { return _elevationHistogramMax; }
70 void setElevationHistograpMax( int value ) {
71 _elevationHistogramMax = value > 0 ? value : 10000;
72 _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;
73 }
74
75 int getElevationHistogramCount() const { return _elevationHistogramCount; }
76
77private:
78 void analyse();
79
80 SGPropertyNode_ptr _rootNode;
81
82 bool _enabled;
83 bool _useAircraftPosition;
84 double _heading_deg;
85 double _speed_kt;
86 int _radius;
87 double _max_computation_time_norm;
88 int _max_samples; // keep xx samples in queue for analysis
89 double _reuse_samples_norm;
90 double _recalc_distance_norm;
91 int _elevationHistogramMax;
92 int _elevationHistogramStep;
93 int _elevationHistogramCount;
94 SGGeod _inputPosition;
95
96 double _altOffset;
97 double _altMedian;
98 double _altMin;
99 double _altLayered;
100 double _altMean;
101 SGGeod _outputPosition;
102
103 SGPropertyNode_ptr _signalNode;
104 SGPropertyNode_ptr _positionLatitudeNode;
105 SGPropertyNode_ptr _positionLongitudeNode;
106
107 deque<double> _elevations;
108 simgear::TiedPropertyList _tiedProperties;
109};
110
111AreaSampler::AreaSampler( SGPropertyNode_ptr rootNode ) :
112 _rootNode(rootNode),
113 _enabled(true),
114 _useAircraftPosition(false),
115 _heading_deg(0.0),
116 _speed_kt(0.0),
117 _radius(40000.0),
118 _max_computation_time_norm(0.1),
119 _max_samples(1000),
120 _reuse_samples_norm(0.8),
121 _recalc_distance_norm(0.1),
122 _elevationHistogramMax(10000),
123 _elevationHistogramStep(500),
124 _elevationHistogramCount(_elevationHistogramMax/_elevationHistogramStep),
125 _altOffset(0),
126 _altMedian(0),
127 _altMin(0),
128 _altLayered(0),
129 _altMean(0),
130 _signalNode(rootNode->getNode("output/valid", true )),
131 _positionLatitudeNode(fgGetNode( "/position/latitude-deg", true )),
132 _positionLongitudeNode(fgGetNode( "/position/longitude-deg", true ))
133{
134 _inputPosition.setElevationM( SG_MAX_ELEVATION_M );
135}
136
140
141
143{
144 _tiedProperties.setRoot( _rootNode );
145 _tiedProperties.Tie( "enabled", &_enabled );
146
147 _tiedProperties.setRoot( _rootNode->getNode( "input", true ) );
148 _tiedProperties.Tie( "use-aircraft-position", &_useAircraftPosition );
149 _tiedProperties.Tie( "latitude-deg", &_inputPosition, &SGGeod::getLatitudeDeg, &SGGeod::setLatitudeDeg );
150 _tiedProperties.Tie( "longitude-deg", &_inputPosition, &SGGeod::getLongitudeDeg, &SGGeod::setLongitudeDeg );
151 _tiedProperties.Tie( "heading-deg", &_heading_deg );
152 _tiedProperties.Tie( "speed-kt", &_speed_kt );
153 _tiedProperties.Tie( "radius-m", &_radius );
154 _tiedProperties.Tie( "max-computation-time-norm", &_max_computation_time_norm );
155 _tiedProperties.Tie( "max-samples", &_max_samples );
156 _tiedProperties.Tie( "reuse-samples-norm", &_reuse_samples_norm );
157 _tiedProperties.Tie( "recalc-distance-norm", &_recalc_distance_norm );
158 _tiedProperties.Tie( "elevation-histogram-max-ft", this, &AreaSampler::getElevationHistogramMax, &AreaSampler::setElevationHistograpMax );
159 _tiedProperties.Tie( "elevation-histogram-step-ft", this, &AreaSampler::getElevationHistogramStep, &AreaSampler::setElevationHistograpStep );
160 _tiedProperties.Tie( "elevation-histogram-count", this, &AreaSampler::getElevationHistogramCount );
161
162 _tiedProperties.setRoot( _rootNode->getNode( "output", true ) );
163 _tiedProperties.Tie( "alt-offset-ft", &_altOffset );
164 _tiedProperties.Tie( "alt-median-ft", &_altMedian );
165 _tiedProperties.Tie( "alt-min-ft", &_altMin );
166 _tiedProperties.Tie( "alt-layered-ft", &_altLayered );
167 _tiedProperties.Tie( "alt-mean-ft", &_altMean );
168 _tiedProperties.Tie( "longitude-deg", &_outputPosition, &SGGeod::getLongitudeDeg );
169 _tiedProperties.Tie( "latitude-deg", &_outputPosition, &SGGeod::getLatitudeDeg );
170
171}
172
174{
175 _tiedProperties.Untie();
176}
177
179{
180 _signalNode->setBoolValue(false);
181 _elevations.clear();
182 _altOffset = 0.0;
183 _altMedian = 0.0;
184 _altMin = 0.0;
185 _altLayered = 0.0;
186 _altMean = 0.0;
187}
188
190{
191 init();
192}
193
194void AreaSampler::update( double dt )
195{
196 // if not enabled or time has stalled, do nothing
197 if( !(_enabled && dt > SGLimitsd::min()) )
198 return;
199
200 // get the aircraft's position if requested
201 if( _useAircraftPosition && _speed_kt < 0.5 ) {
202 _inputPosition = SGGeod::fromDegM(
203 _positionLongitudeNode->getDoubleValue(),
204 _positionLatitudeNode->getDoubleValue(),
205 SG_MAX_ELEVATION_M );
206 }
207
208 // need geocentric coordinates
209 SGGeoc center = SGGeoc::fromGeod( _inputPosition );
210
211 // if a speed is set, move the input position
212 if( _speed_kt >= 0.5 ) {
213 double distance_m = _speed_kt * dt * SG_NM_TO_METER;
214 center = center.advanceRadM( _heading_deg * SG_DEGREES_TO_RADIANS, distance_m );
215 _inputPosition = SGGeod::fromGeoc( center );
216 }
217
218 if( _signalNode->getBoolValue() ) {
219 // if we had finished the iteration and moved more than 10% of the radius
220 // of the sampling area, drop the oldest samples and continue sampling
221 if( SGGeoc::distanceM( center, SGGeoc::fromGeod(_outputPosition ) ) >= _recalc_distance_norm * _radius ) {
222 _elevations.resize( _max_samples * _reuse_samples_norm );
223 _signalNode->setBoolValue( false );
224 }
225 }
226
227 if( _signalNode->getBoolValue() )
228 return; // nothing to do.
229
230 FGScenery * scenery = globals->get_scenery();
231
232 SGTimeStamp start = SGTimeStamp::now();
233 while( (SGTimeStamp::now() - start).toSecs() < dt * _max_computation_time_norm ) {
234 // sample until we used up all our configured time
235 double distance = sg_random();
236 distance = _radius * (1-distance*distance);
237 double course = sg_random() * 2.0 * SG_PI;
238 SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, distance ));
239 double elevation_m = 0.0;
240
241 if (scenery->get_elevation_m( probe, elevation_m, NULL ))
242 _elevations.push_front(elevation_m *= SG_METER_TO_FEET);
243
244 if( _elevations.size() >= (deque<unsigned>::size_type)_max_samples ) {
245 // sampling complete?
246 analyse();
247 _outputPosition = _inputPosition;
248 _signalNode->setBoolValue( true );
249 break;
250 }
251 }
252}
253
254void AreaSampler::analyse()
255{
256 double sum;
257
258 vector<int> histogram(_elevationHistogramCount,0);
259
260 for( deque<double>::size_type i = 0; i < _elevations.size(); i++ ) {
261 int idx = SGMisc<int>::clip( (int)(_elevations[i]/_elevationHistogramStep), 0, histogram.size()-1 );
262 histogram[idx]++;
263 }
264
265 _altMedian = 0.0;
266 sum = 0.0;
267 for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
268 sum += histogram[i];
269 if( sum > 0.5 * _elevations.size() ) {
270 _altMedian = i * _elevationHistogramStep;
271 break;
272 }
273 }
274
275 _altOffset = 0.0;
276 sum = 0.0;
277 for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
278 sum += histogram[i];
279 if( sum > 0.3 * _elevations.size() ) {
280 _altOffset = i * _elevationHistogramStep;
281 break;
282 }
283 }
284
285 _altMean = 0.0;
286 for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
287 _altMean += histogram[i] * i;
288 }
289 _altMean *= _elevationHistogramStep;
290 if( _elevations.size() != 0.0 ) _altMean /= _elevations.size();
291
292 _altMin = 0.0;
293 for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {
294 if( histogram[i] > 0 ) {
295 _altMin = i * _elevationHistogramStep;
296 break;
297 }
298 }
299
300/*
301 double alt_low_min = 0.0;
302 double n_max = 0.0;
303 sum = 0.0;
304 for( vector<int>::size_type i = 0; i < histogram.size()-1; i++ ) {
305 sum += histogram[i];
306 if( histogram[i] > n_max ) n_max = histogram[i];
307 if( n_max > histogram[i+1] && sum > 0.3*_elevations.size()) {
308 alt_low_min = i * _elevationHistogramStep;
309 break;
310 }
311 }
312*/
313 _altLayered = 0.5 * (_altMin + _altOffset);
314
315#if 0
316append(alt_50_array, alt_med);
317#endif
318}
319
320
321// Register the subsystem.
322#if 0
323SGSubsystemMgr::Registrant<AreaSampler> registrantAreaSampler;
324#endif
325
326/* --------------------- End of AreaSampler implementation ------------- */
327
328/* --------------------- TerrainSamplerImplementation -------------------------- */
329
331{
332public:
333 TerrainSamplerImplementation ( SGPropertyNode_ptr rootNode );
335
336 // Subsystem API.
337 void bind() override;
338 InitStatus incrementalInit() override;
339 void init() override;
340 void postinit() override;
341 void reinit() override;
342 void unbind() override;
343 void update(double delta_time_sec) override;
344
345 // Subsystem identification.
346 static const char* staticSubsystemClassId() { return "terrain-sampler"; }
347
348private:
349 inline string areaSubsystemName( unsigned i ) {
350 ostringstream name;
351 name << "area" << i;
352 return name.str();
353 }
354
355 SGPropertyNode_ptr _rootNode;
356 bool _enabled;
357 simgear::TiedPropertyList _tiedProperties;
358};
359
361 _rootNode( rootNode ),
362 _enabled(true)
363{
364}
365
369
371{
372 init();
373 return INIT_DONE;
374}
375
377{
378 PropertyList areaNodes = _rootNode->getChildren( "area" );
379
380 for( PropertyList::size_type i = 0; i < areaNodes.size(); i++ )
381 set_subsystem( areaSubsystemName(i), new AreaSampler( areaNodes[i] ) );
382
383 SGSubsystemGroup::init();
384}
385
389
391{
392 for( unsigned i = 0;; i++ ) {
393 string subsystemName = areaSubsystemName(i);
394 SGSubsystem * subsys = get_subsystem( subsystemName );
395 if( subsys == NULL )
396 break;
397 remove_subsystem( subsystemName );
398 subsys->unbind();
399 delete subsys;
400 }
401
402 init();
403}
404
406{
407 SGSubsystemGroup::bind();
408 _tiedProperties.Tie( _rootNode->getNode("enabled",true), &_enabled );
409}
410
412{
413 _tiedProperties.Untie();
414 SGSubsystemGroup::unbind();
415}
416
418{
419 if( !(_enabled && dt > SGLimitsd::min()) )
420 return;
421 SGSubsystemGroup::update(dt);
422}
423
424/* ----------------------------------------------------------------------- */
425
426/* implementation of the TerrainSampler factory to hide the implementation
427 details */
428TerrainSampler * TerrainSampler::createInstance( SGPropertyNode_ptr rootNode )
429{
430 return new TerrainSamplerImplementation( rootNode );
431}
432
433} // namespace
434
#define i(x)
Class for presampling the terrain roughness.
void setElevationHistograpMax(int value)
static const char * staticSubsystemClassId()
AreaSampler(SGPropertyNode_ptr rootNode)
int getElevationHistogramCount() const
void setElevationHistograpStep(int value)
int getElevationHistogramStep() const
void update(double dt) override
void update(double delta_time_sec) override
TerrainSamplerImplementation(SGPropertyNode_ptr rootNode)
static TerrainSampler * createInstance(SGPropertyNode_ptr rootNode)
bool get_elevation_m(const SGGeod &geod, double &alt, const simgear::BVHMaterial **material, const osg::Node *butNotFrom=0)
Compute the elevation of the scenery at geodetic latitude lat, geodetic longitude lon and not higher ...
Definition scenery.cxx:527
const char * name
FGGlobals * globals
Definition globals.cxx:142
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
Definition proptest.cpp:27