--- /dev/null
+// terrainsampler.cxx --\r
+//\r
+// Written by Torsten Dreyer, started July 2010\r
+// Based on local weather implementation in nasal from \r
+// Thorsten Renk\r
+//\r
+// Copyright (C) 2010 Curtis Olson\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License as\r
+// published by the Free Software Foundation; either version 2 of the\r
+// License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful, but\r
+// WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
+// General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\r
+//\r
+#ifdef HAVE_CONFIG_H\r
+# include <config.h>\r
+#endif\r
+\r
+#include <Main/fg_props.hxx>\r
+#include <simgear/math/sg_random.h>\r
+#include <Scenery/scenery.hxx>\r
+#include <deque>\r
+\r
+#include "terrainsampler.hxx"\r
+using simgear::PropertyList;\r
+\r
+#include "tiedpropertylist.hxx"\r
+\r
+namespace Environment {\r
+/**\r
+ * @brief Class for presampling the terrain roughness\r
+ */\r
+class AreaSampler : public SGSubsystem {\r
+public:\r
+ AreaSampler( SGPropertyNode_ptr rootNode );\r
+ virtual ~AreaSampler();\r
+ void update( double dt );\r
+ void bind();\r
+ void unbind();\r
+\r
+ double getOrientationDeg() const { return _orientation_rad * SG_RADIANS_TO_DEGREES; }\r
+ void setOrientationDeg( double value ) { _orientation_rad = value * SG_DEGREES_TO_RADIANS; }\r
+\r
+ int getElevationHistogramStep() const { return _elevationHistogramStep; }\r
+ void setElevationHistograpStep( int value ) { \r
+ _elevationHistogramStep = value > 0 ? value : 500;\r
+ _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;\r
+ }\r
+\r
+ int getElevationHistogramMax() const { return _elevationHistogramMax; }\r
+ void setElevationHistograpMax( int value ) { \r
+ _elevationHistogramMax = value > 0 ? value : 10000;\r
+ _elevationHistogramCount = _elevationHistogramMax / _elevationHistogramStep;\r
+ }\r
+\r
+ int getElevationHistogramCount() const { return _elevationHistogramCount; }\r
+\r
+private:\r
+ void analyse();\r
+\r
+ SGPropertyNode_ptr _rootNode;\r
+\r
+ bool _enabled;\r
+ bool _useAircraftPosition;\r
+ double _latitude_deg;\r
+ double _longitude_deg;\r
+ double _orientation_rad;\r
+ int _radius;\r
+ int _samples_per_frame;\r
+ int _max_samples; // keep xx samples in queue for analysis\r
+ int _analyze_every; // Run analysis every xx samples\r
+ int _elevationHistogramMax;\r
+ int _elevationHistogramStep;\r
+ int _elevationHistogramCount;\r
+\r
+ double _altOffset;\r
+ double _altMedian;\r
+ double _altMin;\r
+ double _altLayered;\r
+ double _altMean;\r
+\r
+ SGPropertyNode_ptr _positionLatitudeNode;\r
+ SGPropertyNode_ptr _positionLongitudeNode;\r
+\r
+ deque<double> elevations;\r
+ TiedPropertyList _tiedProperties;\r
+};\r
+\r
+AreaSampler::AreaSampler( SGPropertyNode_ptr rootNode ) :\r
+ _rootNode(rootNode),\r
+ _enabled(true),\r
+ _useAircraftPosition(false),\r
+ _latitude_deg(0.0),\r
+ _longitude_deg(0.0),\r
+ _orientation_rad(0.0),\r
+ _radius(40000.0),\r
+ _samples_per_frame(5),\r
+ _max_samples(1000),\r
+ _analyze_every(200),\r
+ _elevationHistogramMax(10000),\r
+ _elevationHistogramStep(500),\r
+ _elevationHistogramCount(_elevationHistogramMax/_elevationHistogramStep),\r
+ _altOffset(0),\r
+ _altMedian(0),\r
+ _altMin(0),\r
+ _altLayered(0),\r
+ _altMean(0)\r
+{\r
+ _positionLatitudeNode = fgGetNode( "/position/latitude-deg", true );\r
+ _positionLongitudeNode = fgGetNode( "/position/longitude-deg", true );\r
+}\r
+\r
+AreaSampler::~AreaSampler()\r
+{\r
+}\r
+\r
+\r
+void AreaSampler::bind()\r
+{\r
+ _tiedProperties.setRoot( _rootNode );\r
+ _tiedProperties.Tie( "enabled", &_enabled );\r
+\r
+ _tiedProperties.setRoot( _rootNode->getNode( "input", true ) );\r
+ _tiedProperties.Tie( "use-aircraft-position", &_useAircraftPosition );\r
+ _tiedProperties.Tie( "latitude-deg", &_latitude_deg );\r
+ _tiedProperties.Tie( "latitude-deg", &_latitude_deg );\r
+ _tiedProperties.Tie( "longitude-deg", &_longitude_deg );\r
+ _tiedProperties.Tie( "orientation-deg", this, &AreaSampler::getOrientationDeg, &AreaSampler::setOrientationDeg );\r
+ _tiedProperties.Tie( "radius-m", &_radius );\r
+ _tiedProperties.Tie( "max-samples-per-frame", &_samples_per_frame );\r
+ _tiedProperties.Tie( "max-samples", &_max_samples );\r
+ _tiedProperties.Tie( "analyse-every", &_analyze_every );\r
+ _tiedProperties.Tie( "elevation-histogram-max-ft", this, &AreaSampler::getElevationHistogramMax, &AreaSampler::setElevationHistograpMax );\r
+ _tiedProperties.Tie( "elevation-histogram-step-ft", this, &AreaSampler::getElevationHistogramStep, &AreaSampler::setElevationHistograpStep );\r
+ _tiedProperties.Tie( "elevation-histogram-count", this, &AreaSampler::getElevationHistogramCount );\r
+\r
+ _tiedProperties.setRoot( _rootNode->getNode( "output", true ) );\r
+ _tiedProperties.Tie( "alt-offset-ft", &_altOffset );\r
+ _tiedProperties.Tie( "alt-median-ft", &_altMedian );\r
+ _tiedProperties.Tie( "alt-min-ft", &_altMin );\r
+ _tiedProperties.Tie( "alt-layered-ft", &_altLayered );\r
+ _tiedProperties.Tie( "alt-mean-ft", &_altMean );\r
+}\r
+\r
+void AreaSampler::unbind()\r
+{\r
+ _tiedProperties.Untie();\r
+}\r
+\r
+void AreaSampler::update( double dt )\r
+{\r
+ if( !(_enabled && dt > SGLimitsd::min()) )\r
+ return;\r
+\r
+ if( _useAircraftPosition ) {\r
+ _longitude_deg = _positionLongitudeNode->getDoubleValue();\r
+ _latitude_deg = _positionLatitudeNode->getDoubleValue();\r
+ }\r
+\r
+ SGGeoc center = SGGeoc::fromGeod( SGGeod::fromDegM( _longitude_deg, _latitude_deg, SG_MAX_ELEVATION_M ) );\r
+\r
+ FGScenery * scenery = globals->get_scenery();\r
+ for( int i = 0; \r
+ i < _samples_per_frame; \r
+ i++ ) {\r
+\r
+ double distance = sg_random() * _radius;\r
+ double course = sg_random() * 2.0 * SG_PI;\r
+ SGGeod probe = SGGeod::fromGeoc(center.advanceRadM( course, distance ));\r
+ double elevation_m = 0.0;\r
+ if (scenery->get_elevation_m( probe, elevation_m, NULL ))\r
+ elevations.push_front(elevation_m *= SG_METER_TO_FEET);\r
+ \r
+ if( elevations.size() >= (deque<unsigned>::size_type)_max_samples ) {\r
+ analyse();\r
+ elevations.resize( _max_samples - _analyze_every );\r
+ }\r
+ }\r
+}\r
+\r
+void AreaSampler::analyse()\r
+{\r
+ double sum;\r
+\r
+ vector<int> histogram(_elevationHistogramCount,0);\r
+ \r
+ for( deque<double>::size_type i = 0; i < elevations.size(); i++ ) {\r
+ int idx = SGMisc<int>::clip( (int)(elevations[i]/_elevationHistogramStep), 0, histogram.size()-1 );\r
+ histogram[idx]++;\r
+ }\r
+\r
+ _altMedian = 0.0;\r
+ sum = 0.0;\r
+ for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {\r
+ sum += histogram[i];\r
+ if( sum > 0.5 * elevations.size() ) {\r
+ _altMedian = i * _elevationHistogramStep;\r
+ break;\r
+ }\r
+ }\r
+\r
+ _altOffset = 0.0;\r
+ sum = 0.0;\r
+ for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {\r
+ sum += histogram[i];\r
+ if( sum > 0.3 * elevations.size() ) {\r
+ _altOffset = i * _elevationHistogramStep;\r
+ break;\r
+ }\r
+ }\r
+\r
+ _altMean = 0.0;\r
+ for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {\r
+ _altMean += histogram[i] * i;\r
+ }\r
+ _altMean *= _elevationHistogramStep;\r
+ if( elevations.size() != 0.0 ) _altMean /= elevations.size();\r
+\r
+ _altMin = 0.0;\r
+ for( vector<int>::size_type i = 0; i < histogram.size(); i++ ) {\r
+ if( histogram[i] > 0 ) {\r
+ _altMin = i * _elevationHistogramStep;\r
+ break;\r
+ }\r
+ }\r
+\r
+ double alt_low_min = 0.0;\r
+ double n_max = 0.0;\r
+ sum = 0.0;\r
+ for( vector<int>::size_type i = 0; i < histogram.size()-1; i++ ) {\r
+ sum += histogram[i];\r
+ if( histogram[i] > n_max ) n_max = histogram[i];\r
+ if( n_max > histogram[i+1] && sum > 0.3*elevations.size()) {\r
+ alt_low_min = i * _elevationHistogramStep;\r
+ break;\r
+ }\r
+ }\r
+\r
+ _altLayered = 0.5 * (_altMin + _altOffset);\r
+\r
+#if 0\r
+ SG_LOG( SG_ALL, SG_ALERT, "TerrainPresampler - alalysis results:" <<\r
+ " total:" << elevations.size() <<\r
+ " mean:" << _altMean <<\r
+ " median:" << _altMedian <<\r
+ " min:" << _altMin <<\r
+ " alt_20:" << _altOffset );\r
+#endif\r
+#if 0\r
+append(alt_50_array, alt_med);\r
+#endif\r
+}\r
+\r
+/* --------------------- End of AreaSampler implementation ------------- */\r
+\r
+/* --------------------- TerrainSamplerImplementation -------------------------- */\r
+\r
+class TerrainSamplerImplementation : public TerrainSampler\r
+{\r
+public:\r
+ TerrainSamplerImplementation ( SGPropertyNode_ptr rootNode );\r
+ virtual ~TerrainSamplerImplementation ();\r
+ \r
+ virtual void init ();\r
+ virtual void reinit ();\r
+ virtual void bind();\r
+ virtual void unbind();\r
+ virtual void update (double delta_time_sec);\r
+private:\r
+ inline string areaSubsystemName( unsigned i ) {\r
+ ostringstream name;\r
+ name << "area" << i;\r
+ return name.str();\r
+ }\r
+\r
+ SGPropertyNode_ptr _rootNode;\r
+ bool _enabled;\r
+ TiedPropertyList _tiedProperties;\r
+};\r
+\r
+TerrainSamplerImplementation::TerrainSamplerImplementation( SGPropertyNode_ptr rootNode ) :\r
+ _rootNode( rootNode ),\r
+ _enabled(true)\r
+{\r
+}\r
+\r
+TerrainSamplerImplementation::~TerrainSamplerImplementation()\r
+{\r
+}\r
+\r
+void TerrainSamplerImplementation::init()\r
+{\r
+ PropertyList areaNodes = _rootNode->getChildren( "area" );\r
+ \r
+ for( PropertyList::size_type i = 0; i < areaNodes.size(); i++ )\r
+ set_subsystem( areaSubsystemName(i), new AreaSampler( areaNodes[i] ) );\r
+\r
+ SGSubsystemGroup::bind();// bind the subsystems before the get init()ed\r
+ SGSubsystemGroup::init();\r
+}\r
+\r
+void TerrainSamplerImplementation::reinit()\r
+{\r
+ for( unsigned i = 0;; i++ ) {\r
+ string subsystemName = areaSubsystemName(i);\r
+ SGSubsystem * subsys = get_subsystem( subsystemName );\r
+ if( subsys == NULL )\r
+ break;\r
+ remove_subsystem( subsystemName );\r
+ }\r
+ \r
+ init();\r
+}\r
+\r
+void TerrainSamplerImplementation::bind()\r
+{\r
+ SGSubsystemGroup::bind();\r
+ _tiedProperties.Tie( _rootNode->getNode("enabled",true), &_enabled );\r
+}\r
+\r
+void TerrainSamplerImplementation::unbind()\r
+{\r
+ _tiedProperties.Untie();\r
+ SGSubsystemGroup::unbind();\r
+}\r
+\r
+void TerrainSamplerImplementation::update( double dt )\r
+{\r
+ if( !(_enabled && dt > SGLimitsd::min()) )\r
+ return;\r
+ SGSubsystemGroup::update(dt);\r
+}\r
+\r
+/* ----------------------------------------------------------------------- */\r
+\r
+/* implementation of the TerrainSampler factory to hide the implementation\r
+ details */\r
+TerrainSampler::~TerrainSampler ()\r
+{\r
+}\r
+\r
+TerrainSampler * TerrainSampler::createInstance( SGPropertyNode_ptr rootNode )\r
+{\r
+ return new TerrainSamplerImplementation( rootNode );\r
+}\r
+\r
+} // namespace\r
+\r
--- /dev/null
+#ifndef __TIEDPROPERTYLIST_HXX
+#define __TIEDPROPERTYLIST_HXX
+#include <simgear/props/props.hxx>
+using simgear::PropertyList;
+
+// Maybe this goes into SimGear's props.hxx later?
+class TiedPropertyList : PropertyList {
+public:
+ TiedPropertyList() {}
+ TiedPropertyList( SGPropertyNode_ptr root ) { _root = root; }
+
+ void setRoot( SGPropertyNode_ptr root ) { _root = root; }
+ SGPropertyNode_ptr getRoot() const { return _root; }
+
+ template<typename T> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, const SGRawValue<T> &rawValue, bool useDefault = true ) {
+ bool success = node->tie( rawValue, useDefault );
+ if( success ) {
+ SG_LOG( SG_ALL, SG_INFO, "Tied " << node->getPath() );
+ push_back( node );
+ } else {
+#if PROPS_STANDALONE
+ cerr << "Failed to tie property " << node->getPath() << endl;
+#else
+ SG_LOG(SG_GENERAL, SG_WARN, "Failed to tie property " << node->getPath() );
+#endif
+ }
+ return node;
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, V * value, bool useDefault = true ) {
+ return Tie( node, SGRawValuePointer<V>(value), useDefault );
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( const char * relative_path, V * value, bool useDefault = true ) {
+ return Tie( _root->getNode(relative_path,true), SGRawValuePointer<V>(value), useDefault );
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, V (*getter)(), void (*setter)(V) = 0, bool useDefault = true ) {
+ return Tie(node, SGRawValueFunctions<V>(getter, setter), useDefault );
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( const char * relative_path, V (*getter)(), void (*setter)(V) = 0, bool useDefault = true ) {
+ return Tie(_root->getNode(relative_path, true), SGRawValueFunctions<V>(getter, setter), useDefault );
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, int index, V (*getter)(int), void (*setter)(int, V) = 0, bool useDefault = true) {
+ return Tie( node, SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault );
+ }
+
+ template <class V> SGPropertyNode_ptr Tie( const char * relative_path, int index, V (*getter)(int), void (*setter)(int, V) = 0, bool useDefault = true) {
+ return Tie( _root->getNode( relative_path, true ), SGRawValueFunctionsIndexed<V>(index, getter, setter), useDefault );
+ }
+
+ template <class T, class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, T * obj, V (T::*getter)() const, void (T::*setter)(V) = 0, bool useDefault = true) {
+ return Tie( node, SGRawValueMethods<T,V>(*obj, getter, setter), useDefault );
+ }
+
+ template <class T, class V> SGPropertyNode_ptr Tie( const char * relative_path, T * obj, V (T::*getter)() const, void (T::*setter)(V) = 0, bool useDefault = true) {
+ return Tie( _root->getNode( relative_path, true), SGRawValueMethods<T,V>(*obj, getter, setter), useDefault );
+ }
+
+ template <class T, class V> SGPropertyNode_ptr Tie( SGPropertyNode_ptr node, T * obj, int index, V (T::*getter)(int) const, void (T::*setter)(int, V) = 0, bool useDefault = true) {
+ return Tie( node, SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault);
+ }
+
+ template <class T, class V> SGPropertyNode_ptr Tie( const char * relative_path, T * obj, int index, V (T::*getter)(int) const, void (T::*setter)(int, V) = 0, bool useDefault = true) {
+ return Tie( _root->getNode( relative_path, true ), SGRawValueMethodsIndexed<T,V>(*obj, index, getter, setter), useDefault);
+ }
+
+ void Untie() {
+ while( size() > 0 ) {
+ SG_LOG( SG_ALL, SG_INFO, "untie of " << back()->getPath() );
+ back()->untie();
+ pop_back();
+ }
+ }
+private:
+ SGPropertyNode_ptr _root;
+};
+#endif