From 179a7993334d84f0a7f1b9447ef03f18599f5ba7 Mon Sep 17 00:00:00 2001 From: torsten Date: Fri, 27 Nov 2009 15:12:34 +0000 Subject: [PATCH 01/29] - fix the element which was no longer working due to an invalid property root - allow multiple elements within an aircraft. All autopilot live in an individual FGXMLAutopilot subsystem which run within a subsystem group now. --- src/Autopilot/xmlauto.cxx | 121 +++++++++++++++++++++++++++----------- src/Autopilot/xmlauto.hxx | 14 ++++- src/Main/fg_init.cxx | 2 +- 3 files changed, 98 insertions(+), 39 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index 6d5428557..8a91a091c 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -69,7 +69,7 @@ void FGXMLAutoInput::parse( SGPropertyNode_ptr node, double aValue, double aOffs SGPropertyNode * n; if( (n = node->getChild("condition")) != NULL ) { - _condition = sgReadCondition(node, n); + _condition = sgReadCondition(fgGetNode("/"), n); } if( (n = node->getChild( "scale" )) != NULL ) { @@ -200,7 +200,7 @@ void FGXMLAutoComponent::parseNode(SGPropertyNode* aNode) debug = child->getBoolValue(); } else if ( cname == "enable" ) { if( (prop = child->getChild("condition")) != NULL ) { - _condition = sgReadCondition(child, prop); + _condition = sgReadCondition(fgGetNode("/"), prop); } else { if ( (prop = child->getChild( "prop" )) != NULL ) { enable_prop = fgGetNode( prop->getStringValue(), true ); @@ -793,6 +793,83 @@ void FGDigitalFilter::update(double dt) } } +FGXMLAutopilotGroup::FGXMLAutopilotGroup() +{ +} + +void FGXMLAutopilotGroup::reinit() +{ + for( vector::size_type i = 0; i < _autopilotNames.size(); i++ ) { + FGXMLAutopilot * ap = (FGXMLAutopilot*)get_subsystem( _autopilotNames[i] ); + if( ap == NULL ) continue; // ? + remove_subsystem( _autopilotNames[i] ); + delete ap; + } + _autopilotNames.clear(); + init(); +} + +void FGXMLAutopilotGroup::init() +{ + vector autopilotNodes = fgGetNode( "/sim/systems", true )->getChildren("autopilot"); + if( autopilotNodes.size() == 0 ) { + SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration specified for this model!"); + return; + } + + for( vector::size_type i = 0; i < autopilotNodes.size(); i++ ) { + SGPropertyNode_ptr pathNode = autopilotNodes[i]->getNode( "path" ); + if( pathNode == NULL ) { + SG_LOG( SG_ALL, SG_WARN, "No autopilot configuration file specified for this autopilot!"); + continue; + } + + string apName; + SGPropertyNode_ptr nameNode = autopilotNodes[i]->getNode( "name" ); + if( nameNode != NULL ) { + apName = nameNode->getStringValue(); + } else { + std::ostringstream buf; + buf << "unnamed_autopilot_" << i; + apName = buf.str(); + } + + if( get_subsystem( apName.c_str() ) != NULL ) { + SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" ); + continue; + } + + SGPath config( globals->get_fg_root() ); + config.append( pathNode->getStringValue() ); + + SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() ); + // FGXMLAutopilot + FGXMLAutopilot * ap = new FGXMLAutopilot; + try { + SGPropertyNode_ptr root = new SGPropertyNode(); + readProperties( config.str(), root ); + + + if ( ! ap->build( root ) ) { + SG_LOG( SG_ALL, SG_ALERT, + "Detected an internal inconsistency in the autopilot configuration." << endl << " See earlier errors for details." ); + delete ap; + continue; + } + } catch (const sg_exception& e) { + SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: " + << config.str() << ":" << e.getMessage() ); + delete ap; + continue; + } + + SG_LOG( SG_ALL, SG_INFO, "adding autopilot subsystem " << apName ); + set_subsystem( apName, ap ); + _autopilotNames.push_back( apName ); + } + + SGSubsystemGroup::init(); +} FGXMLAutopilot::FGXMLAutopilot() { } @@ -802,38 +879,11 @@ FGXMLAutopilot::~FGXMLAutopilot() { } -void FGXMLAutopilot::init() { - config_props = fgGetNode( "/autopilot/new-config", true ); - - SGPropertyNode *path_n = fgGetNode("/sim/systems/autopilot/path"); - - if ( path_n ) { - SGPath config( globals->get_fg_root() ); - config.append( path_n->getStringValue() ); - - SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " - << config.str() ); - try { - readProperties( config.str(), config_props ); - - if ( ! build() ) { - SG_LOG( SG_ALL, SG_ALERT, - "Detected an internal inconsistency in the autopilot"); - SG_LOG( SG_ALL, SG_ALERT, - " configuration. See earlier errors for" ); - SG_LOG( SG_ALL, SG_ALERT, - " details."); - exit(-1); - } - } catch (const sg_exception& e) { - SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: " - << config.str() << ":" << e.getMessage() ); - } - - } else { - SG_LOG( SG_ALL, SG_WARN, - "No autopilot configuration specified for this model!"); - } +/* read all /sim/systems/autopilot[n]/path properties, try to read the file specified therein + * and configure/add the digital filters specified in that file + */ +void FGXMLAutopilot::init() +{ } @@ -849,7 +899,7 @@ void FGXMLAutopilot::bind() { void FGXMLAutopilot::unbind() { } -bool FGXMLAutopilot::build() { +bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { SGPropertyNode *node; int i; @@ -858,6 +908,7 @@ bool FGXMLAutopilot::build() { node = config_props->getChild(i); string name = node->getName(); // cout << name << endl; + SG_LOG( SG_ALL, SG_INFO, "adding autopilot component " << name ); if ( name == "pid-controller" ) { components.push_back( new FGPIDController( node ) ); } else if ( name == "pi-simple-controller" ) { diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index 3f68fb20c..549244c32 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -357,6 +357,16 @@ public: * */ +class FGXMLAutopilotGroup : public SGSubsystemGroup +{ +public: + FGXMLAutopilotGroup(); + void init(); + void reinit(); +private: + std::vector _autopilotNames; +}; + class FGXMLAutopilot : public SGSubsystem { @@ -371,16 +381,14 @@ public: void unbind(); void update( double dt ); - bool build(); + bool build( SGPropertyNode_ptr ); protected: - typedef std::vector > comp_list; private: bool serviceable; - SGPropertyNode_ptr config_props; comp_list components; }; diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 2e8713a17..24f8726a3 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -1537,7 +1537,7 @@ bool fgInitSubsystems() { // Initialize the XML Autopilot subsystem. //////////////////////////////////////////////////////////////////// - globals->add_subsystem( "xml-autopilot", new FGXMLAutopilot ); + globals->add_subsystem( "xml-autopilot", new FGXMLAutopilotGroup ); globals->add_subsystem( "route-manager", new FGRouteMgr ); globals->add_subsystem( "autobrake", new FGAutoBrake ); From 506f6894e44c76df153779a6dca4762b8bf15d41 Mon Sep 17 00:00:00 2001 From: torsten Date: Tue, 15 Dec 2009 21:27:46 +0000 Subject: [PATCH 02/29] - introduce some typedefs for SGSharedPtr<> - add a element to input and output of each filter to support periodical values like headings. See README.digitalfilters for details. --- src/Autopilot/xmlauto.cxx | 54 +++++++++++++++++++++++++++++++++++---- src/Autopilot/xmlauto.hxx | 31 ++++++++++++++++------ 2 files changed, 72 insertions(+), 13 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index 8a91a091c..8d07a66bd 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -40,14 +40,42 @@ using std::cout; using std::endl; +FGPeriodicalValue::FGPeriodicalValue( SGPropertyNode_ptr root ) +{ + SGPropertyNode_ptr minNode = root->getChild( "min" ); + SGPropertyNode_ptr maxNode = root->getChild( "max" ); + if( minNode == NULL || maxNode == NULL ) { + SG_LOG(SG_AUTOPILOT, SG_ALERT, "periodical defined, but no and/or tag. Period ignored." ); + } else { + minPeriod = new FGXMLAutoInput( minNode ); + maxPeriod = new FGXMLAutoInput( maxNode ); + } +} + +double FGPeriodicalValue::normalize( double value ) +{ + if( !(minPeriod && maxPeriod )) return value; + + double p1 = minPeriod->get_value(); + double p2 = maxPeriod->get_value(); + + double min = std::min(p1,p2); + double max = std::max(p1,p2); + double phase = fabs(max - min); + + if( phase > SGLimitsd::min() ) { + while( value < min ) value += phase; + while( value > max ) value -= phase; + } else { + value = min; // phase is zero + } + + return value; +} + FGXMLAutoInput::FGXMLAutoInput( SGPropertyNode_ptr node, double value, double offset, double scale) : value(0.0), abs(false), - property(NULL), - offset(NULL), - scale(NULL), - min(NULL), - max(NULL), _condition(NULL) { parse( node, value, offset, scale ); @@ -62,6 +90,7 @@ void FGXMLAutoInput::parse( SGPropertyNode_ptr node, double aValue, double aOffs scale = NULL; min = NULL; max = NULL; + periodical = NULL; if( node == NULL ) return; @@ -92,6 +121,10 @@ void FGXMLAutoInput::parse( SGPropertyNode_ptr node, double aValue, double aOffs abs = n->getBoolValue(); } + if( (n = node->getChild( "period" )) != NULL ) { + periodical = new FGPeriodicalValue( n ); + } + SGPropertyNode *valueNode = node->getChild( "value" ); if ( valueNode != NULL ) { value = valueNode->getDoubleValue(); @@ -161,6 +194,10 @@ double FGXMLAutoInput::get_value() if( value > m ) value = m; } + + if( periodical ) { + value = periodical->normalize( value ); + } return abs ? fabs(value) : value; } @@ -246,6 +283,8 @@ void FGXMLAutoComponent::parseNode(SGPropertyNode* aNode) umaxInput.push_back( new FGXMLAutoInput( child ) ); } else if ( cname == "u_max" ) { umaxInput.push_back( new FGXMLAutoInput( child ) ); + } else if ( cname == "period" ) { + periodical = new FGPeriodicalValue( child ); } else { SG_LOG(SG_AUTOPILOT, SG_ALERT, "malformed autopilot definition - unrecognized node:" << cname << " in section " << name); @@ -314,6 +353,11 @@ void FGXMLAutoComponent::do_feedback_if_disabled() double FGXMLAutoComponent::clamp( double value ) { + //If this is a periodical value, normalize it into our domain + // before clamping + if( periodical ) + value = periodical->normalize( value ); + // clamp, if either min or max is defined if( uminInput.size() + umaxInput.size() > 0 ) { double d = umaxInput.get_value( 0.0 ); diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index 549244c32..efdf2f3ce 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -34,16 +34,28 @@ #include #include +typedef SGSharedPtr FGXMLAutoInput_ptr; +typedef SGSharedPtr FGPeriodicalValue_ptr; + +class FGPeriodicalValue : public SGReferenced { +private: + FGXMLAutoInput_ptr minPeriod; // The minimum value of the period + FGXMLAutoInput_ptr maxPeriod; // The maximum value of the period +public: + FGPeriodicalValue( SGPropertyNode_ptr node ); + double normalize( double value ); +}; class FGXMLAutoInput : public SGReferenced { private: double value; // The value as a constant or initializer for the property bool abs; // return absolute value SGPropertyNode_ptr property; // The name of the property containing the value - SGSharedPtr offset; // A fixed offset, defaults to zero - SGSharedPtr scale; // A constant scaling factor defaults to one - SGSharedPtr min; // A minimum clip defaults to no clipping - SGSharedPtr max; // A maximum clip defaults to no clipping + FGXMLAutoInput_ptr offset; // A fixed offset, defaults to zero + FGXMLAutoInput_ptr scale; // A constant scaling factor defaults to one + FGXMLAutoInput_ptr min; // A minimum clip defaults to no clipping + FGXMLAutoInput_ptr max; // A maximum clip defaults to no clipping + FGPeriodicalValue_ptr periodical; // SGSharedPtr _condition; public: @@ -71,9 +83,9 @@ public: }; -class FGXMLAutoInputList : public std::vector > { +class FGXMLAutoInputList : public std::vector { public: - FGXMLAutoInput * get_active() { + FGXMLAutoInput_ptr get_active() { for (iterator it = begin(); it != end(); ++it) { if( (*it)->is_enabled() ) return *it; @@ -82,7 +94,7 @@ class FGXMLAutoInputList : public std::vector > { } double get_value( double def = 0.0 ) { - FGXMLAutoInput * input = get_active(); + FGXMLAutoInput_ptr input = get_active(); return input == NULL ? def : input->get_value(); } @@ -147,6 +159,7 @@ protected: FGXMLAutoInputList referenceInput; FGXMLAutoInputList uminInput; FGXMLAutoInputList umaxInput; + FGPeriodicalValue_ptr periodical; // debug flag bool debug; bool enabled; @@ -221,6 +234,8 @@ public: bool isPropertyEnabled(); }; +typedef SGSharedPtr FGXMLAutoComponent_ptr; + /** * Roy Ovesen's PID controller @@ -384,7 +399,7 @@ public: bool build( SGPropertyNode_ptr ); protected: - typedef std::vector > comp_list; + typedef std::vector comp_list; private: From 2b3e07cc5f451aa5b2e915f78c31ddd0acf041cf Mon Sep 17 00:00:00 2001 From: torsten Date: Tue, 15 Dec 2009 21:28:30 +0000 Subject: [PATCH 03/29] document the element --- docs-mini/README.digitalfilters | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/docs-mini/README.digitalfilters b/docs-mini/README.digitalfilters index ad7a26e26..dce9a80dd 100644 --- a/docs-mini/README.digitalfilters +++ b/docs-mini/README.digitalfilters @@ -27,6 +27,10 @@ The complete XML syntax for a InputValue is infinity -infinity false + + -180.0 + -180.0 + The enclosing element is the element defined in each filter, like , , @@ -124,7 +128,7 @@ other computations are completed. OutputValue ============================================================================== -Each filter drives one to many output properties. No scaling of offsetting is implemented +Each filter drives one to many output properties. No scaling or offsetting is implemented for the output value, these should be done in the filter itself. The output properties are defined in the element by adding elements within the element. For just a single output property, the element @@ -167,6 +171,8 @@ Example: min or max value defaults to 0 (zero). Note: and may also occour within a element. and may be used as a substitude for the corresponding u_xxx element. + Complex Define a periodical input or output value. The phase width is defined by the + child elements and which are of type InputValue Example: Limit the pilot's body temperature to a constant minimum of 36 and a maximum defined in /pilots/max-body-temperature-degc, initialized to 40.0 @@ -183,6 +189,17 @@ Implicit definition of the minimum value of 0 (zero) and defining a maximum of 1 100.0 +This defines the input or output as a periodic value with a phase width of 360, like +the compass rose. Any value reaching the filter's input or leaving the filter at the +output will be transformed to fit into the given range by adding or substracting one phase +width of 360. Values of -270, 90 or 450 applied to this periodical element will allways +result in +90. A value of 630, 270 or -90 will be normalized to -90 in the given example. + + -180.0 + 180.0 + + + Complex Define a condition to enable or disable the filter. For disabled filters, no output computations are performed. Only enabled filters fill the output properties. The default for undefined From 50adab32c15ec54e4f557cf85f567bae4fda3e4e Mon Sep 17 00:00:00 2001 From: torsten Date: Fri, 18 Dec 2009 11:43:14 +0000 Subject: [PATCH 04/29] Ingore unhandled top level elements instead of not loading the autopilot. This (re-)enables usage of at top level and --- src/Autopilot/xmlauto.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index 8d07a66bd..28bfdaffd 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -961,10 +961,10 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { components.push_back( new FGPredictor( node ) ); } else if ( name == "filter" ) { components.push_back( new FGDigitalFilter( node ) ); - } else { - SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: " - << name ); - return false; +// } else { +// SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: " +// << name ); +// return false; } } From 5cad5aa7da2476ca8323a61f81dea59676dca085 Mon Sep 17 00:00:00 2001 From: jmt Date: Sat, 26 Dec 2009 12:31:07 +0000 Subject: [PATCH 05/29] Autopilot: clean up the helpers code (which drives the various /internal/) properties. Fixes various heading modes to work in all aircraft. --- src/Autopilot/xmlauto.cxx | 72 +++++++++++++++------------------------ src/Autopilot/xmlauto.hxx | 9 ++++- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index 28bfdaffd..ff149a99c 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -928,6 +928,8 @@ FGXMLAutopilot::~FGXMLAutopilot() { */ void FGXMLAutopilot::init() { + latNode = fgGetNode("/position/latitude-deg"); + lonNode = fgGetNode("/position/longitude-deg"); } @@ -975,7 +977,7 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { /* * Update helper values */ -static void update_helper( double dt ) { +void FGXMLAutopilot::update_helper( double dt ) { // Estimate speed in 5,10 seconds static SGPropertyNode_ptr vel = fgGetNode( "/velocities/airspeed-kt", true ); static SGPropertyNode_ptr lookahead5 @@ -1002,32 +1004,20 @@ static void update_helper( double dt ) { v_last = v; } - // Calculate heading bug error normalized to +/- 180.0 (based on - // DG indicated heading) + // Calculate heading bug error normalized to +/- 180.0 static SGPropertyNode_ptr bug = fgGetNode( "/autopilot/settings/heading-bug-deg", true ); - static SGPropertyNode_ptr ind_hdg - = fgGetNode( "/instrumentation/heading-indicator/indicated-heading-deg", - true ); - static SGPropertyNode_ptr ind_bug_error - = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true ); - - double diff = bug->getDoubleValue() - ind_hdg->getDoubleValue(); - if ( diff < -180.0 ) { diff += 360.0; } - if ( diff > 180.0 ) { diff -= 360.0; } - ind_bug_error->setDoubleValue( diff ); - - // Calculate heading bug error normalized to +/- 180.0 (based on - // actual/nodrift magnetic-heading, i.e. a DG slaved to magnetic - // compass.) static SGPropertyNode_ptr mag_hdg = fgGetNode( "/orientation/heading-magnetic-deg", true ); + static SGPropertyNode_ptr bug_error + = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true ); + + double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue(); + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + bug_error->setDoubleValue( diff ); + static SGPropertyNode_ptr fdm_bug_error = fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true ); - - diff = bug->getDoubleValue() - mag_hdg->getDoubleValue(); - if ( diff < -180.0 ) { diff += 360.0; } - if ( diff > 180.0 ) { diff -= 360.0; } fdm_bug_error->setDoubleValue( diff ); // Calculate true heading error normalized to +/- 180.0 @@ -1035,14 +1025,11 @@ static void update_helper( double dt ) { = fgGetNode( "/autopilot/settings/true-heading-deg", true ); static SGPropertyNode_ptr true_hdg = fgGetNode( "/orientation/heading-deg", true ); - static SGPropertyNode_ptr true_track - = fgGetNode( "/instrumentation/gps/indicated-track-true-deg", true ); static SGPropertyNode_ptr true_error = fgGetNode( "/autopilot/internal/true-heading-error-deg", true ); diff = target_true->getDoubleValue() - true_hdg->getDoubleValue(); - if ( diff < -180.0 ) { diff += 360.0; } - if ( diff > 180.0 ) { diff -= 360.0; } + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); true_error->setDoubleValue( diff ); // Calculate nav1 target heading error normalized to +/- 180.0 @@ -1054,25 +1041,25 @@ static void update_helper( double dt ) { = fgGetNode( "/autopilot/internal/nav1-track-error-deg", true ); diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue(); - if ( diff < -180.0 ) { diff += 360.0; } - if ( diff > 180.0 ) { diff -= 360.0; } + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); true_nav1->setDoubleValue( diff ); - diff = target_nav1->getDoubleValue() - true_track->getDoubleValue(); - if ( diff < -180.0 ) { diff += 360.0; } - if ( diff > 180.0 ) { diff -= 360.0; } + // Calculate true groundtrack + SGGeod currentPosition(SGGeod::fromDeg( + lonNode->getDoubleValue(), latNode->getDoubleValue())); + double true_track = SGGeodesy::courseDeg(lastPosition, currentPosition); + lastPosition = currentPosition; + diff = target_nav1->getDoubleValue() - true_track; + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); true_track_nav1->setDoubleValue( diff ); // Calculate nav1 selected course error normalized to +/- 180.0 - // (based on DG indicated heading) static SGPropertyNode_ptr nav1_course_error = fgGetNode( "/autopilot/internal/nav1-course-error", true ); static SGPropertyNode_ptr nav1_selected_course = fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true ); - diff = nav1_selected_course->getDoubleValue() - ind_hdg->getDoubleValue(); -// if ( diff < -180.0 ) { diff += 360.0; } -// if ( diff > 180.0 ) { diff -= 360.0; } + diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue(); SG_NORMALIZE_RANGE( diff, -180.0, 180.0 ); nav1_course_error->setDoubleValue( diff ); @@ -1088,23 +1075,20 @@ static void update_helper( double dt ) { // Calculate static port pressure rate in [inhg/s]. // Used to determine vertical speed. static SGPropertyNode_ptr static_pressure - = fgGetNode( "/systems/static[0]/pressure-inhg", true ); + = fgGetNode( "/systems/static[0]/pressure-inhg", true ); static SGPropertyNode_ptr pressure_rate - = fgGetNode( "/autopilot/internal/pressure-rate", true ); + = fgGetNode( "/autopilot/internal/pressure-rate", true ); static double last_static_pressure = 0.0; if ( dt > 0.0 ) { - double current_static_pressure = static_pressure->getDoubleValue(); + double current_static_pressure = static_pressure->getDoubleValue(); + double current_pressure_rate = + ( current_static_pressure - last_static_pressure ) / dt; - double current_pressure_rate = - ( current_static_pressure - last_static_pressure ) / dt; - - pressure_rate->setDoubleValue(current_pressure_rate); - - last_static_pressure = current_static_pressure; + pressure_rate->setDoubleValue(current_pressure_rate); + last_static_pressure = current_static_pressure; } - } diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index efdf2f3ce..4b122caef 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -402,9 +402,16 @@ protected: typedef std::vector comp_list; private: - + /** + * Update helper values, especially the /autopilot/internal properties + */ + void update_helper( double dt ); + bool serviceable; comp_list components; + + SGPropertyNode_ptr latNode, lonNode; + SGGeod lastPosition; }; From 95e2d62d94586e694c5241fcc46ad7a37173dce1 Mon Sep 17 00:00:00 2001 From: torsten Date: Tue, 29 Dec 2009 15:34:02 +0000 Subject: [PATCH 06/29] bugfix: don't call the update_helper() for every instance of FGXMLAutopilot. Compute the helper properties only once per FGXMLAutopilotGroup's update() method. Only one AutopilotGroup shall be instantiated, but many Autopilots may exist. --- src/Autopilot/xmlauto.cxx | 223 +++++++++++++++++--------------------- src/Autopilot/xmlauto.hxx | 35 ++++-- 2 files changed, 126 insertions(+), 132 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index ff149a99c..090d9fbb7 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -837,10 +837,105 @@ void FGDigitalFilter::update(double dt) } } -FGXMLAutopilotGroup::FGXMLAutopilotGroup() +FGXMLAutopilotGroup::FGXMLAutopilotGroup() : + SGSubsystemGroup(), + average(0.0), // average/filtered prediction + v_last(0.0), // last velocity + last_static_pressure(0.0), + vel(fgGetNode( "/velocities/airspeed-kt", true )), + // Estimate speed in 5,10 seconds + lookahead5(fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true )), + lookahead10(fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true )), + bug(fgGetNode( "/autopilot/settings/heading-bug-deg", true )), + mag_hdg(fgGetNode( "/orientation/heading-magnetic-deg", true )), + bug_error(fgGetNode( "/autopilot/internal/heading-bug-error-deg", true )), + fdm_bug_error(fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true )), + target_true(fgGetNode( "/autopilot/settings/true-heading-deg", true )), + true_hdg(fgGetNode( "/orientation/heading-deg", true )), + true_error(fgGetNode( "/autopilot/internal/true-heading-error-deg", true )), + target_nav1(fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true )), + true_nav1(fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true )), + true_track_nav1(fgGetNode( "/autopilot/internal/nav1-track-error-deg", true )), + nav1_course_error(fgGetNode( "/autopilot/internal/nav1-course-error", true )), + nav1_selected_course(fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true )), + vs_fps(fgGetNode( "/velocities/vertical-speed-fps", true )), + vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )), + static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )), + pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )), + latNode(fgGetNode("/position/latitude-deg")), + lonNode(fgGetNode("/position/longitude-deg")) { } +void FGXMLAutopilotGroup::update( double dt ) +{ + // update all configured autopilots + SGSubsystemGroup::update( dt ); + + // update helper values + double v = vel->getDoubleValue(); + double a = 0.0; + if ( dt > 0.0 ) { + a = (v - v_last) / dt; + + if ( dt < 1.0 ) { + average = (1.0 - dt) * average + dt * a; + } else { + average = a; + } + + lookahead5->setDoubleValue( v + average * 5.0 ); + lookahead10->setDoubleValue( v + average * 10.0 ); + v_last = v; + } + + // Calculate heading bug error normalized to +/- 180.0 + double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue(); + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + bug_error->setDoubleValue( diff ); + + fdm_bug_error->setDoubleValue( diff ); + + // Calculate true heading error normalized to +/- 180.0 + diff = target_true->getDoubleValue() - true_hdg->getDoubleValue(); + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + true_error->setDoubleValue( diff ); + + // Calculate nav1 target heading error normalized to +/- 180.0 + diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue(); + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + true_nav1->setDoubleValue( diff ); + + // Calculate true groundtrack + SGGeod currentPosition(SGGeod::fromDeg( + lonNode->getDoubleValue(), latNode->getDoubleValue())); + double true_track = SGGeodesy::courseDeg(lastPosition, currentPosition); + lastPosition = currentPosition; + diff = target_nav1->getDoubleValue() - true_track; + SG_NORMALIZE_RANGE(diff, -180.0, 180.0); + true_track_nav1->setDoubleValue( diff ); + + // Calculate nav1 selected course error normalized to +/- 180.0 + diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue(); + SG_NORMALIZE_RANGE( diff, -180.0, 180.0 ); + nav1_course_error->setDoubleValue( diff ); + + // Calculate vertical speed in fpm + vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 ); + + + // Calculate static port pressure rate in [inhg/s]. + // Used to determine vertical speed. + if ( dt > 0.0 ) { + double current_static_pressure = static_pressure->getDoubleValue(); + double current_pressure_rate = + ( current_static_pressure - last_static_pressure ) / dt; + + pressure_rate->setDoubleValue(current_pressure_rate); + last_static_pressure = current_static_pressure; + } +} + void FGXMLAutopilotGroup::reinit() { for( vector::size_type i = 0; i < _autopilotNames.size(); i++ ) { @@ -928,8 +1023,6 @@ FGXMLAutopilot::~FGXMLAutopilot() { */ void FGXMLAutopilot::init() { - latNode = fgGetNode("/position/latitude-deg"); - lonNode = fgGetNode("/position/longitude-deg"); } @@ -973,132 +1066,12 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { return true; } - -/* - * Update helper values - */ -void FGXMLAutopilot::update_helper( double dt ) { - // Estimate speed in 5,10 seconds - static SGPropertyNode_ptr vel = fgGetNode( "/velocities/airspeed-kt", true ); - static SGPropertyNode_ptr lookahead5 - = fgGetNode( "/autopilot/internal/lookahead-5-sec-airspeed-kt", true ); - static SGPropertyNode_ptr lookahead10 - = fgGetNode( "/autopilot/internal/lookahead-10-sec-airspeed-kt", true ); - - static double average = 0.0; // average/filtered prediction - static double v_last = 0.0; // last velocity - - double v = vel->getDoubleValue(); - double a = 0.0; - if ( dt > 0.0 ) { - a = (v - v_last) / dt; - - if ( dt < 1.0 ) { - average = (1.0 - dt) * average + dt * a; - } else { - average = a; - } - - lookahead5->setDoubleValue( v + average * 5.0 ); - lookahead10->setDoubleValue( v + average * 10.0 ); - v_last = v; - } - - // Calculate heading bug error normalized to +/- 180.0 - static SGPropertyNode_ptr bug - = fgGetNode( "/autopilot/settings/heading-bug-deg", true ); - static SGPropertyNode_ptr mag_hdg - = fgGetNode( "/orientation/heading-magnetic-deg", true ); - static SGPropertyNode_ptr bug_error - = fgGetNode( "/autopilot/internal/heading-bug-error-deg", true ); - - double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue(); - SG_NORMALIZE_RANGE(diff, -180.0, 180.0); - bug_error->setDoubleValue( diff ); - - static SGPropertyNode_ptr fdm_bug_error - = fgGetNode( "/autopilot/internal/fdm-heading-bug-error-deg", true ); - fdm_bug_error->setDoubleValue( diff ); - - // Calculate true heading error normalized to +/- 180.0 - static SGPropertyNode_ptr target_true - = fgGetNode( "/autopilot/settings/true-heading-deg", true ); - static SGPropertyNode_ptr true_hdg - = fgGetNode( "/orientation/heading-deg", true ); - static SGPropertyNode_ptr true_error - = fgGetNode( "/autopilot/internal/true-heading-error-deg", true ); - - diff = target_true->getDoubleValue() - true_hdg->getDoubleValue(); - SG_NORMALIZE_RANGE(diff, -180.0, 180.0); - true_error->setDoubleValue( diff ); - - // Calculate nav1 target heading error normalized to +/- 180.0 - static SGPropertyNode_ptr target_nav1 - = fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true ); - static SGPropertyNode_ptr true_nav1 - = fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true ); - static SGPropertyNode_ptr true_track_nav1 - = fgGetNode( "/autopilot/internal/nav1-track-error-deg", true ); - - diff = target_nav1->getDoubleValue() - true_hdg->getDoubleValue(); - SG_NORMALIZE_RANGE(diff, -180.0, 180.0); - true_nav1->setDoubleValue( diff ); - - // Calculate true groundtrack - SGGeod currentPosition(SGGeod::fromDeg( - lonNode->getDoubleValue(), latNode->getDoubleValue())); - double true_track = SGGeodesy::courseDeg(lastPosition, currentPosition); - lastPosition = currentPosition; - diff = target_nav1->getDoubleValue() - true_track; - SG_NORMALIZE_RANGE(diff, -180.0, 180.0); - true_track_nav1->setDoubleValue( diff ); - - // Calculate nav1 selected course error normalized to +/- 180.0 - static SGPropertyNode_ptr nav1_course_error - = fgGetNode( "/autopilot/internal/nav1-course-error", true ); - static SGPropertyNode_ptr nav1_selected_course - = fgGetNode( "/instrumentation/nav[0]/radials/selected-deg", true ); - - diff = nav1_selected_course->getDoubleValue() - mag_hdg->getDoubleValue(); - SG_NORMALIZE_RANGE( diff, -180.0, 180.0 ); - nav1_course_error->setDoubleValue( diff ); - - // Calculate vertical speed in fpm - static SGPropertyNode_ptr vs_fps - = fgGetNode( "/velocities/vertical-speed-fps", true ); - static SGPropertyNode_ptr vs_fpm - = fgGetNode( "/autopilot/internal/vert-speed-fpm", true ); - - vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 ); - - - // Calculate static port pressure rate in [inhg/s]. - // Used to determine vertical speed. - static SGPropertyNode_ptr static_pressure - = fgGetNode( "/systems/static[0]/pressure-inhg", true ); - static SGPropertyNode_ptr pressure_rate - = fgGetNode( "/autopilot/internal/pressure-rate", true ); - - static double last_static_pressure = 0.0; - - if ( dt > 0.0 ) { - double current_static_pressure = static_pressure->getDoubleValue(); - double current_pressure_rate = - ( current_static_pressure - last_static_pressure ) / dt; - - pressure_rate->setDoubleValue(current_pressure_rate); - last_static_pressure = current_static_pressure; - } -} - - /* * Update the list of autopilot components */ -void FGXMLAutopilot::update( double dt ) { - update_helper( dt ); - +void FGXMLAutopilot::update( double dt ) +{ unsigned int i; for ( i = 0; i < components.size(); ++i ) { components[i]->update( dt ); diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index 4b122caef..a29545759 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -378,8 +378,36 @@ public: FGXMLAutopilotGroup(); void init(); void reinit(); + void update( double dt ); private: std::vector _autopilotNames; + + double average; + double v_last; + double last_static_pressure; + + SGPropertyNode_ptr vel; + SGPropertyNode_ptr lookahead5; + SGPropertyNode_ptr lookahead10; + SGPropertyNode_ptr bug; + SGPropertyNode_ptr mag_hdg; + SGPropertyNode_ptr bug_error; + SGPropertyNode_ptr fdm_bug_error; + SGPropertyNode_ptr target_true; + SGPropertyNode_ptr true_hdg; + SGPropertyNode_ptr true_error; + SGPropertyNode_ptr target_nav1; + SGPropertyNode_ptr true_nav1; + SGPropertyNode_ptr true_track_nav1; + SGPropertyNode_ptr nav1_course_error; + SGPropertyNode_ptr nav1_selected_course; + SGPropertyNode_ptr vs_fps; + SGPropertyNode_ptr vs_fpm; + SGPropertyNode_ptr static_pressure; + SGPropertyNode_ptr pressure_rate; + + SGPropertyNode_ptr latNode, lonNode; + SGGeod lastPosition; }; class FGXMLAutopilot : public SGSubsystem @@ -402,16 +430,9 @@ protected: typedef std::vector comp_list; private: - /** - * Update helper values, especially the /autopilot/internal properties - */ - void update_helper( double dt ); - bool serviceable; comp_list components; - SGPropertyNode_ptr latNode, lonNode; - SGGeod lastPosition; }; From 8114d1d8998f48e61e2bd4a73d8223961ad4dc17 Mon Sep 17 00:00:00 2001 From: durk Date: Wed, 30 Dec 2009 14:11:16 +0000 Subject: [PATCH 07/29] Conditional compilation of ATCDCL module. Use --disable-atcdcl to try building flightgear without ATCDCL code. --- configure.ac | 19 ++++++++++++++++++- src/AIModel/AIAircraft.cxx | 9 --------- src/ATC/trafficcontrol.cxx | 8 ++++---- src/ATC/trafficcontrol.hxx | 1 - src/Airports/apt_loader.cxx | 23 +++++++++++++++++++---- src/Airports/groundnetwork.cxx | 7 +++++++ src/Main/Makefile.am | 9 ++++++++- src/Main/fg_init.cxx | 21 +++++++++++++++++---- src/Makefile.am | 9 ++++++++- 9 files changed, 81 insertions(+), 25 deletions(-) diff --git a/configure.ac b/configure.ac index 6ae6d7438..1c1fa9613 100644 --- a/configure.ac +++ b/configure.ac @@ -158,6 +158,17 @@ else fi AM_CONDITIONAL(ENABLE_SP_FDM, test "x$enable_sp_fdms" != "xno") +# Specify whether we want to compile ATCDCL. +# default to with_atcdcl=yes +AC_ARG_ENABLE(atcdcl, [ --enable-atcdcl Compile and link the depricated atc/ai module], [enable_atcdcl="$enableval"] ) +if test "x$enable_atcdcl" != "xno"; then + AC_DEFINE([ENABLE_ATCDCL], 1, [Define to include old ATC/AI module]) +else + AC_DEFINE([ENABLE_ATCDCL], 0, [Define to include old ATC/AI module]) +fi +AM_CONDITIONAL(ENABLE_ATCDCL, test "x$enable_atcdcl" != "xno") + + dnl EXPERIMENTAL generic event driven input device # defaults to no @@ -202,6 +213,7 @@ fi AC_CHECK_HEADER(pthread.h) AM_CONDITIONAL(WITH_THREADS, test "x$with_threads" = "xyes") + dnl Used by JSBSim to conditionally compile in fgfs interface code AC_DEFINE([FGFS], 1, [Define so that JSBSim compiles in 'library' mode]) @@ -283,7 +295,7 @@ AC_SEARCH_LIBS(clock_gettime, rt) base_LIBS="$LIBS" dnl Check for SDL or glut if enabled. -AC_ARG_ENABLE(osgviewer, [ --enable-osgviewer Configure to use osgViewer(default)], [enable_osgviewer="$enableval"]) +AC_ARG_ENABLE(osgviewer, [ --enable-osgviewer Configure to use osgViewer(default)], [enable_osgviewer="$enableval"]) AC_ARG_ENABLE(sdl, [ --enable-sdl Configure to use SDL], [enable_sdl="$enableval"]) AC_ARG_ENABLE(glut, [ --enable-glut Configure to use GLUT], [enable_glut="$enableval"]) AM_CONDITIONAL(USE_SDL, test "x$enable_sdl" = "xyes") @@ -880,3 +892,8 @@ else echo "Include special purpose flight models: no" fi +if test "x$enable_atcdcl" != "xno"; then + echo "Build depricated ATC/AI module: yes" +else + echo "Build depricated ATC/AI module: no" +fi diff --git a/src/AIModel/AIAircraft.cxx b/src/AIModel/AIAircraft.cxx index 68d7f46c4..eb8469fde 100644 --- a/src/AIModel/AIAircraft.cxx +++ b/src/AIModel/AIAircraft.cxx @@ -447,15 +447,6 @@ void FGAIAircraft::announcePositionToController() { case 4: //Take off tower controller if (trafficRef->getDepartureAirport()->getDynamics()) { controller = trafficRef->getDepartureAirport()->getDynamics()->getTowerController(); - //if (trafficRef->getDepartureAirport()->getId() == "EHAM") { - //string trns = trafficRef->getCallSign() + " at runway " + fp->getRunway() + - // ". Ready for departure. " + trafficRef->getFlightType() + " to " + - // trafficRef->getArrivalAirport()->getId(); - //fgSetString("/sim/messages/atc", trns.c_str()); - // if (controller == 0) { - //cerr << "Error in assigning controller at " << trafficRef->getDepartureAirport()->getId() << endl; - //} - //} } else { cerr << "Error: Could not find Dynamics at airport : " << trafficRef->getDepartureAirport()->getId() << endl; } diff --git a/src/ATC/trafficcontrol.cxx b/src/ATC/trafficcontrol.cxx index 57f718812..a04b6fc81 100644 --- a/src/ATC/trafficcontrol.cxx +++ b/src/ATC/trafficcontrol.cxx @@ -264,7 +264,7 @@ bool FGTrafficRecord::isOpposing (FGGroundNetwork *net, FGTrafficRecord &other, if (other.intentions.size()) { for (intVecIterator j = other.intentions.begin(); j != other.intentions.end(); j++) - { + { // cerr << "Current segment 1 " << (*i) << endl; if ((*i) > 0) { if ((opp = net->findSegment(*i)->opposite())) @@ -402,7 +402,7 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m // Assign SID, if necessery (TODO) case MSG_PERMIT_ENGINE_START: taxiFreqStr = formatATCFrequency3_2(taxiFreq); - + heading = rec->getAircraft()->getTrafficRef()->getCourse(); fltType = rec->getAircraft()->getTrafficRef()->getFlightType(); rwyClass= rec->getAircraft()->GetFlightPlan()->getRunwayClassFromTrafficType(fltType); @@ -453,10 +453,8 @@ void FGATCController::transmit(FGTrafficRecord *rec, AtcMsgId msgId, AtcMsgDir m // Display ATC message only when one of the radios is tuned // the relevant frequency. // Note that distance attenuation is currently not yet implemented - //cerr << "Transmitting " << text << " at " << stationFreq; if ((onBoardRadioFreqI0 == stationFreq) || (onBoardRadioFreqI1 == stationFreq)) { fgSetString("/sim/messages/atc", text.c_str()); - //cerr << "Printing Message: " << endl; } } @@ -466,6 +464,8 @@ string FGATCController::formatATCFrequency3_2(int freq) { return string(buffer); } +// TODO: Set transponder codes according to real-world routes. +// The current version just returns a random string of four octal numbers. string FGATCController::genTransponderCode(string fltRules) { if (fltRules == "VFR") { return string("1200"); diff --git a/src/ATC/trafficcontrol.hxx b/src/ATC/trafficcontrol.hxx index 7ca8e88bf..ef984efca 100644 --- a/src/ATC/trafficcontrol.hxx +++ b/src/ATC/trafficcontrol.hxx @@ -45,7 +45,6 @@ typedef vector::iterator intVecIterator; class FGAIFlightPlan; // forward reference class FGGroundNetwork; // forward reference -//class FGAISchedule; // forward reference class FGAIAircraft; // forward reference /************************************************************************************** diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index af9319ae3..3dfcdf059 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -45,7 +45,9 @@ #include "simple.hxx" #include "runways.hxx" #include "pavement.hxx" -#include +#if ENABLE_ATCDCL +# include +#endif #include @@ -75,7 +77,12 @@ public: last_apt_type("") {} + +#ifdef ENABLE_ATCDCL void parseAPT(const string &aptdb_file, FGCommList *comm_list) +#else + void parseAPT(const string &aptdb_file) +#endif { sg_gzifstream in( aptdb_file ); @@ -461,7 +468,7 @@ private: pvt->addNode(pos, num == 113); } } - +#if ENABLE_ATCDCL void parseATISLine(FGCommList *comm_list, const vector& token) { if ( rwy_count <= 0 ) { @@ -505,19 +512,27 @@ private: << " type: " << a.type ); #endif } +#endif }; + // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. bool fgAirportDBLoad( const string &aptdb_file, +#if ENABLE_ATCDCL FGCommList *comm_list, const std::string &metar_file ) +#else + const std::string &metar_file ) +#endif { APTLoader ld; - +#if ENABLE_ATCDCL ld.parseAPT(aptdb_file, comm_list); - +#else + ld.parseAPT(aptdb_file); +#endif // // Load the metar.dat file and update apt db with stations that // have metar data. diff --git a/src/Airports/groundnetwork.cxx b/src/Airports/groundnetwork.cxx index 8bdd947fc..7600033ce 100644 --- a/src/Airports/groundnetwork.cxx +++ b/src/Airports/groundnetwork.cxx @@ -508,6 +508,13 @@ void FGGroundNetwork::signOff(int id) { void FGGroundNetwork::update(int id, double lat, double lon, double heading, double speed, double alt, double dt) { + // Check whether aircraft are on hold due to a preceding pushback. If so, make sure to + // Transmit air-to-ground "Ready to taxi request: + // Transmit ground to air approval / hold + // Transmit confirmation ... + // Probably use a status mechanism similar to the Engine start procedure in the startup controller. + + TrafficVectorIterator i = activeTraffic.begin(); // Search search if the current id has an entry // This might be faster using a map instead of a vector, but let's start by taking a safe route diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 0994d859a..996c63e11 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -9,6 +9,13 @@ else SP_FDM_LIBS = endif +if ENABLE_ATCDCL +ATCDCL_LIBS = $(top_builddir)/src/ATCDCL/libATCDCL.a +else +ATCDCL_LIBS = +endif + + if WITH_THREADS THREAD_LIBS = -lsgthreads $(thread_LIBS) else @@ -82,7 +89,7 @@ fgfs_SOURCES = bootstrap.cxx fgfs_LDADD = \ libMain.a \ $(top_builddir)/src/Aircraft/libAircraft.a \ - $(top_builddir)/src/ATCDCL/libATCDCL.a \ + $(ATCDCL_LIBS) \ $(top_builddir)/src/Cockpit/libCockpit.a \ $(top_builddir)/src/Cockpit/built_in/libBuilt_in.a \ $(top_builddir)/src/FDM/libFlight.a \ diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 26a7987f4..ded6765fc 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -73,8 +73,13 @@ #include #include -#include -#include + +#if ENABLE_ATCDCL +# include +# include +# include "ATCDCL/commlist.hxx" +#endif + #include #include #include @@ -82,7 +87,8 @@ #include #include #include -#ifdef ENABLE_SP_FDM + +#if ENABLE_SP_FDM #include #include #include @@ -130,7 +136,7 @@ #include "renderer.hxx" #include "viewmgr.hxx" #include "main.hxx" -#include "ATCDCL/commlist.hxx" + #ifdef __APPLE__ # include @@ -973,9 +979,16 @@ fgInitNav () // Initialise the frequency search map BEFORE reading // the airport database: + + + +#if ENABLE_ATCDCL current_commlist = new FGCommList; current_commlist->init( globals->get_fg_root() ); fgAirportDBLoad( aptdb.str(), current_commlist, p_metar.str() ); +#else + fgAirportDBLoad( aptdb.str(), p_metar.str() ); +#endif FGNavList *navlist = new FGNavList; FGNavList *loclist = new FGNavList; diff --git a/src/Makefile.am b/src/Makefile.am index 178419eb0..39b081ac1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,9 +1,16 @@ +if ENABLE_ATCDCL +ATCDCL_DIR = ATCDCL +else +ATCDCL_DIR = +endif + + SUBDIRS = \ Include \ Aircraft \ Airports \ ATC \ - ATCDCL \ + $(ATCDCL_DIR) \ Autopilot \ Cockpit \ Environment \ From 690395354f739395f54df14e7378bcb273cfe8ff Mon Sep 17 00:00:00 2001 From: torsten Date: Sat, 2 Jan 2010 18:29:01 +0000 Subject: [PATCH 08/29] provide a property /orientation/track The track is computed between two subsequent settings of the geodetic position and represents the true track. Also the _set_Latitude() and _set_Longitude() methods were removed. Use _set_Geodetic_Position(lat,lon) instead. --- src/FDM/SP/ACMS.cxx | 5 ++--- src/FDM/SP/MagicCarpet.cxx | 3 +-- src/FDM/UFO.cxx | 3 +-- src/FDM/flight.cxx | 6 ++++++ src/FDM/flight.hxx | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 7 deletions(-) diff --git a/src/FDM/SP/ACMS.cxx b/src/FDM/SP/ACMS.cxx index ad36f28cf..6fd219bc9 100644 --- a/src/FDM/SP/ACMS.cxx +++ b/src/FDM/SP/ACMS.cxx @@ -88,15 +88,14 @@ void FGACMS::update( double dt ) { _set_V_calibrated_kts( kts ); _set_V_ground_speed( kts ); - SGGeod pos = SGGeod::fromDegM(get_Longitude(), get_Latitude(), get_Altitude()); + SGGeod pos = getPosition(); // update (lon/lat) position SGGeod pos2; double az2; geo_direct_wgs_84 ( pos, heading * SGD_RADIANS_TO_DEGREES, dist, pos2, &az2 ); - _set_Longitude( pos2.getLongitudeRad() ); - _set_Latitude( pos2.getLatitudeRad() ); + _set_Geodetic_Position( pos2.getLatitudeRad(), pos2.getLongitudeRad(), pos.getElevationFt() ); double sl_radius, lat_geoc; sgGeodToGeoc( get_Latitude(), get_Altitude(), &sl_radius, &lat_geoc ); diff --git a/src/FDM/SP/MagicCarpet.cxx b/src/FDM/SP/MagicCarpet.cxx index 99c937433..44bffd0ef 100644 --- a/src/FDM/SP/MagicCarpet.cxx +++ b/src/FDM/SP/MagicCarpet.cxx @@ -93,8 +93,7 @@ void FGMagicCarpet::update( double dt ) { get_Psi() * SGD_RADIANS_TO_DEGREES, dist, &lat2, &lon2, &az2 ); - _set_Longitude( lon2 * SGD_DEGREES_TO_RADIANS ); - _set_Latitude( lat2 * SGD_DEGREES_TO_RADIANS ); + _set_Geodetic_Position( lat2 * SGD_DEGREES_TO_RADIANS, lon2 * SGD_DEGREES_TO_RADIANS ); } // cout << "lon error = " << fabs(end.x()*SGD_RADIANS_TO_DEGREES - lon2) diff --git a/src/FDM/UFO.cxx b/src/FDM/UFO.cxx index e8a37fc7b..93928f67d 100644 --- a/src/FDM/UFO.cxx +++ b/src/FDM/UFO.cxx @@ -162,8 +162,7 @@ void FGUFO::update( double dt ) { get_Psi() * SGD_RADIANS_TO_DEGREES, dist, &lat2, &lon2, &az2 ); - _set_Longitude( lon2 * SGD_DEGREES_TO_RADIANS ); - _set_Latitude( lat2 * SGD_DEGREES_TO_RADIANS ); + _set_Geodetic_Position( lat2 * SGD_DEGREES_TO_RADIANS, lon2 * SGD_DEGREES_TO_RADIANS ); } // cout << "lon error = " << fabs(end.x()*SGD_RADIANS_TO_DEGREES - lon2) diff --git a/src/FDM/flight.cxx b/src/FDM/flight.cxx index bc5b3059a..c9956d8cb 100644 --- a/src/FDM/flight.cxx +++ b/src/FDM/flight.cxx @@ -278,6 +278,8 @@ FGInterface::bind () &FGInterface::get_Psi_deg, &FGInterface::set_Psi_deg); fgSetArchivable("/orientation/heading-deg"); + fgTie("/orientation/track-deg", this, + &FGInterface::get_Track); // Body-axis "euler rates" (rotation speed, but in a funny // representation). @@ -404,6 +406,7 @@ FGInterface::unbind () fgUntie("/orientation/roll-deg"); fgUntie("/orientation/pitch-deg"); fgUntie("/orientation/heading-deg"); + fgUntie("/orientation/track-deg"); fgUntie("/orientation/roll-rate-degps"); fgUntie("/orientation/pitch-rate-degps"); fgUntie("/orientation/yaw-rate-degps"); @@ -442,6 +445,7 @@ FGInterface::update (double dt) void FGInterface::_updatePositionM(const SGVec3d& cartPos) { + TrackComputer tracker( track, geodetic_position_v ); cartesian_position_v = cartPos; geodetic_position_v = SGGeod::fromCart(cartesian_position_v); geocentric_position_v = SGGeoc::fromCart(cartesian_position_v); @@ -452,6 +456,7 @@ void FGInterface::_updatePositionM(const SGVec3d& cartPos) void FGInterface::_updatePosition(const SGGeod& geod) { + TrackComputer tracker( track, geodetic_position_v ); geodetic_position_v = geod; cartesian_position_v = SGVec3d::fromGeod(geodetic_position_v); geocentric_position_v = SGGeoc::fromCart(cartesian_position_v); @@ -463,6 +468,7 @@ void FGInterface::_updatePosition(const SGGeod& geod) void FGInterface::_updatePosition(const SGGeoc& geoc) { + TrackComputer tracker( track, geodetic_position_v ); geocentric_position_v = geoc; cartesian_position_v = SGVec3d::fromGeoc(geocentric_position_v); geodetic_position_v = SGGeod::fromCart(cartesian_position_v); diff --git a/src/FDM/flight.hxx b/src/FDM/flight.hxx index ba9551456..222df3b54 100644 --- a/src/FDM/flight.hxx +++ b/src/FDM/flight.hxx @@ -88,6 +88,34 @@ using std::list; using std::vector; using std::string; +/** + * A little helper class to update the track if + * the position has changed. In the constructor, + * create a copy of the current position and store + * references to the position object and the track + * variable to update. + * The destructor, called at TrackComputer's end of + * life/visibility, computes the track if the + * position has changed. + */ +class TrackComputer { +public: + inline TrackComputer( double & track, const SGGeod & position ) : + _track( track ), + _position( position ), + _prevPosition( position ) { + } + + inline ~TrackComputer() { + if( _prevPosition == _position ) return; + _track = SGGeodesy::courseDeg( _prevPosition, _position ); + } +private: + double & _track; + const SGGeod & _position; + const SGGeod _prevPosition; +}; + // This is based heavily on LaRCsim/ls_generic.h class FGInterface : public SGSubsystem { @@ -157,6 +185,7 @@ private: double runway_altitude; double climb_rate; // in feet per second double altitude_agl; + double track; double daux[16]; // auxilliary doubles float faux[16]; // auxilliary floats @@ -265,19 +294,27 @@ public: geocentric_position_v.setLongitudeRad(lon); geocentric_position_v.setRadiusFt(rad); } +/* Don't call _set_L[at|ong]itude() directly, use _set_Geodetic_Position() instead. + These methods can't update the track. + * inline void _set_Latitude(double lat) { geodetic_position_v.setLatitudeRad(lat); } inline void _set_Longitude(double lon) { geodetic_position_v.setLongitudeRad(lon); } +*/ inline void _set_Altitude(double altitude) { geodetic_position_v.setElevationFt(altitude); } inline void _set_Altitude_AGL(double agl) { altitude_agl = agl; } + inline void _set_Geodetic_Position( double lat, double lon ) { + _set_Geodetic_Position( lat, lon, geodetic_position_v.getElevationFt()); + } inline void _set_Geodetic_Position( double lat, double lon, double alt ) { + TrackComputer tracker( track, geodetic_position_v ); geodetic_position_v.setLatitudeRad(lat); geodetic_position_v.setLongitudeRad(lon); geodetic_position_v.setElevationFt(alt); @@ -541,6 +578,7 @@ public: return geodetic_position_v.getElevationFt(); } inline double get_Altitude_AGL(void) const { return altitude_agl; } + inline double get_Track(void) const { return track; } inline double get_Latitude_deg () const { return geodetic_position_v.getLatitudeDeg(); From 78d8e7edfd290dba4f22ab27e8d14d0c3d093959 Mon Sep 17 00:00:00 2001 From: torsten Date: Sun, 3 Jan 2010 09:49:28 +0000 Subject: [PATCH 09/29] now that there is a true track property, the magnetic track is nice to have, too. --- src/Main/fg_props.cxx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Main/fg_props.cxx b/src/Main/fg_props.cxx index 066f0262b..dfce13c13 100644 --- a/src/Main/fg_props.cxx +++ b/src/Main/fg_props.cxx @@ -349,6 +349,18 @@ getHeadingMag () return magheading; } +/** + * Return the current track in degrees. + */ +static double +getTrackMag () +{ + double magtrack; + magtrack = current_aircraft.fdm_state->get_Track() - getMagVar(); + if (magtrack < 0) magtrack += 360; + return magtrack; +} + static long getWarp () { @@ -508,6 +520,7 @@ FGProperties::bind () // Orientation fgTie("/orientation/heading-magnetic-deg", getHeadingMag); + fgTie("/orientation/track-magnetic-deg", getTrackMag); fgTie("/environment/magnetic-variation-deg", getMagVar); fgTie("/environment/magnetic-dip-deg", getMagDip); @@ -537,6 +550,7 @@ FGProperties::unbind () // Orientation fgUntie("/orientation/heading-magnetic-deg"); + fgUntie("/orientation/track-magnetic-deg"); // Environment fgUntie("/environment/magnetic-variation-deg"); From 168af9dc1e86b78fe714754253258f9c304b3d09 Mon Sep 17 00:00:00 2001 From: torsten Date: Sun, 3 Jan 2010 10:13:19 +0000 Subject: [PATCH 10/29] - added a differential filter - use /orientation/track-deg instead of computing our own track - some cosmetic changes --- src/Autopilot/xmlauto.cxx | 144 ++++++++++++++++++++------------------ src/Autopilot/xmlauto.hxx | 6 +- 2 files changed, 77 insertions(+), 73 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index 090d9fbb7..d37164a72 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -65,7 +65,7 @@ double FGPeriodicalValue::normalize( double value ) if( phase > SGLimitsd::min() ) { while( value < min ) value += phase; - while( value > max ) value -= phase; + while( value >= max ) value -= phase; } else { value = min; // phase is zero } @@ -729,6 +729,10 @@ bool FGDigitalFilter::parseNodeHook(const string& aName, SGPropertyNode* aNode) filterType = gain; } else if (val == "reciprocal") { filterType = reciprocal; + } else if (val == "differential") { + filterType = differential; + // use a constant of two samples for current and previous input value + samplesInput.push_back( new FGXMLAutoInput(NULL, 2.0 ) ); } } else if (aName == "filter-time" ) { TfInput.push_back( new FGXMLAutoInput( aNode, 1.0 ) ); @@ -768,72 +772,79 @@ void FGDigitalFilter::update(double dt) do_feedback(); } - if ( enabled && dt > 0.0 ) { - /* - * Exponential filter - * - * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1] - * - */ - if( debug ) cout << "Updating " << get_name() - << " dt " << dt << endl; + if ( !enabled || dt < SGLimitsd::min() ) + return; - if (filterType == exponential) - { - double alpha = 1 / ((TfInput.get_value()/dt) + 1); - output.push_front(alpha * input[0] + - (1 - alpha) * output[0]); - } - else if (filterType == doubleExponential) - { - double alpha = 1 / ((TfInput.get_value()/dt) + 1); - output.push_front(alpha * alpha * input[0] + - 2 * (1 - alpha) * output[0] - - (1 - alpha) * (1 - alpha) * output[1]); - } - else if (filterType == movingAverage) - { - output.push_front(output[0] + - (input[0] - input.back()) / samplesInput.get_value()); - } - else if (filterType == noiseSpike) - { - double maxChange = rateOfChangeInput.get_value() * dt; + /* + * Exponential filter + * + * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1] + * + */ + if( debug ) cout << "Updating " << get_name() + << " dt " << dt << endl; - if ((output[0] - input[0]) > maxChange) - { - output.push_front(output[0] - maxChange); - } - else if ((output[0] - input[0]) < -maxChange) - { - output.push_front(output[0] + maxChange); - } - else if (fabs(input[0] - output[0]) <= maxChange) - { - output.push_front(input[0]); - } - } - else if (filterType == gain) - { - output[0] = gainInput.get_value() * input[0]; - } - else if (filterType == reciprocal) - { - if (input[0] != 0.0) { - output[0] = gainInput.get_value() / input[0]; - } - } + if (filterType == exponential) + { + double alpha = 1 / ((TfInput.get_value()/dt) + 1); + output.push_front(alpha * input[0] + + (1 - alpha) * output[0]); + } + else if (filterType == doubleExponential) + { + double alpha = 1 / ((TfInput.get_value()/dt) + 1); + output.push_front(alpha * alpha * input[0] + + 2 * (1 - alpha) * output[0] - + (1 - alpha) * (1 - alpha) * output[1]); + } + else if (filterType == movingAverage) + { + output.push_front(output[0] + + (input[0] - input.back()) / samplesInput.get_value()); + } + else if (filterType == noiseSpike) + { + double maxChange = rateOfChangeInput.get_value() * dt; - output[0] = clamp(output[0]) ; - set_output_value( output[0] ); - - output.resize(2); - - if (debug) + if ((output[0] - input[0]) > maxChange) { - cout << "input:" << input[0] - << "\toutput:" << output[0] << endl; + output.push_front(output[0] - maxChange); } + else if ((output[0] - input[0]) < -maxChange) + { + output.push_front(output[0] + maxChange); + } + else if (fabs(input[0] - output[0]) <= maxChange) + { + output.push_front(input[0]); + } + } + else if (filterType == gain) + { + output[0] = gainInput.get_value() * input[0]; + } + else if (filterType == reciprocal) + { + if (input[0] != 0.0) { + output[0] = gainInput.get_value() / input[0]; + } + } + else if (filterType == differential) + { + if( dt > SGLimitsd::min() ) { + output[0] = (input[0]-input[1]) * TfInput.get_value() / dt; + } + } + + output[0] = clamp(output[0]) ; + set_output_value( output[0] ); + + output.resize(2); + + if (debug) + { + cout << "input:" << input[0] + << "\toutput:" << output[0] << endl; } } @@ -862,8 +873,7 @@ FGXMLAutopilotGroup::FGXMLAutopilotGroup() : vs_fpm(fgGetNode( "/autopilot/internal/vert-speed-fpm", true )), static_pressure(fgGetNode( "/systems/static[0]/pressure-inhg", true )), pressure_rate(fgGetNode( "/autopilot/internal/pressure-rate", true )), - latNode(fgGetNode("/position/latitude-deg")), - lonNode(fgGetNode("/position/longitude-deg")) + track(fgGetNode( "/orientation/track-deg", true )) { } @@ -907,11 +917,7 @@ void FGXMLAutopilotGroup::update( double dt ) true_nav1->setDoubleValue( diff ); // Calculate true groundtrack - SGGeod currentPosition(SGGeod::fromDeg( - lonNode->getDoubleValue(), latNode->getDoubleValue())); - double true_track = SGGeodesy::courseDeg(lastPosition, currentPosition); - lastPosition = currentPosition; - diff = target_nav1->getDoubleValue() - true_track; + diff = target_nav1->getDoubleValue() - track->getDoubleValue(); SG_NORMALIZE_RANGE(diff, -180.0, 180.0); true_track_nav1->setDoubleValue( diff ); diff --git a/src/Autopilot/xmlauto.hxx b/src/Autopilot/xmlauto.hxx index a29545759..6d7d3f2aa 100644 --- a/src/Autopilot/xmlauto.hxx +++ b/src/Autopilot/xmlauto.hxx @@ -354,7 +354,7 @@ private: std::deque output; std::deque input; enum filterTypes { exponential, doubleExponential, movingAverage, - noiseSpike, gain, reciprocal, none }; + noiseSpike, gain, reciprocal, differential, none }; filterTypes filterType; protected: @@ -405,9 +405,7 @@ private: SGPropertyNode_ptr vs_fpm; SGPropertyNode_ptr static_pressure; SGPropertyNode_ptr pressure_rate; - - SGPropertyNode_ptr latNode, lonNode; - SGGeod lastPosition; + SGPropertyNode_ptr track; }; class FGXMLAutopilot : public SGSubsystem From d8ae90801acd1ea051d2f46dc434b649ea53ba2f Mon Sep 17 00:00:00 2001 From: durk Date: Tue, 5 Jan 2010 20:04:54 +0000 Subject: [PATCH 11/29] Some additional changes to ensure that FlightGear at least compiles after configuring with --disable-atcdcl. Some substitution code is added in ATC/atcutils.cxx and ATC/atcutils.hxx. Note that the new code doesn't run properly yet. Instead, it is just meant to identify which parts need replacement. Getting that to work will be the next step. --- src/ATC/Makefile.am | 1 + src/ATC/atcutils.cxx | 372 ++++++++++++++++++++++++++++ src/ATC/atcutils.hxx | 185 ++++++++++++++ src/Airports/apt_loader.cxx | 24 +- src/Airports/apt_loader.hxx | 2 +- src/Instrumentation/KLN89/kln89.cxx | 14 +- src/Main/fg_init.cxx | 11 +- 7 files changed, 586 insertions(+), 23 deletions(-) create mode 100644 src/ATC/atcutils.cxx create mode 100644 src/ATC/atcutils.hxx diff --git a/src/ATC/Makefile.am b/src/ATC/Makefile.am index 32e4ced92..35756c958 100644 --- a/src/ATC/Makefile.am +++ b/src/ATC/Makefile.am @@ -1,6 +1,7 @@ noinst_LIBRARIES = libATC.a libATC_a_SOURCES = \ + atcutils.cxx atcutils.hxx \ trafficcontrol.cxx trafficcontrol.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/ATC/atcutils.cxx b/src/ATC/atcutils.cxx new file mode 100644 index 000000000..12747c480 --- /dev/null +++ b/src/ATC/atcutils.cxx @@ -0,0 +1,372 @@ +// commlist.cxx -- comm frequency lookup class +// +// Written by David Luff and Alexander Kappes, started Jan 2003. +// Based on navlist.cxx by Curtis Olson, started April 2000. +// +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + +#ifdef HAVE_CONFIG_H +# include +#endif + + + +#include +#include +#include +#include +#include +#include +#include + +#include "atcutils.hxx" + +#if !ENABLE_ATCDCL + + +FGCommList *current_commlist; + + +// Constructor +FGCommList::FGCommList( void ) { + sg_srandom_time(); +} + + +// Destructor +FGCommList::~FGCommList( void ) { +} + + +// load the navaids and build the map +bool FGCommList::init( const SGPath& path ) { +/* + SGPath temp = path; + commlist_freq.erase(commlist_freq.begin(), commlist_freq.end()); + commlist_bck.erase(commlist_bck.begin(), commlist_bck.end()); + temp.append( "ATC/default.atis" ); + LoadComms(temp); + temp = path; + temp.append( "ATC/default.tower" ); + LoadComms(temp); + temp = path; + temp.append( "ATC/default.ground" ); + LoadComms(temp); + temp = path; + temp.append( "ATC/default.approach" ); + LoadComms(temp); + return true;*/ +} + + +bool FGCommList::LoadComms(const SGPath& path) { +/* + sg_gzifstream fin( path.str() ); + if ( !fin.is_open() ) { + SG_LOG( SG_GENERAL, SG_ALERT, "Cannot open file: " << path.str() ); + exit(-1); + } + + // read in each line of the file + fin >> skipcomment; + + while ( !fin.eof() ) { + ATCData a; + fin >> a; + if(a.type == INVALID) { + SG_LOG(SG_GENERAL, SG_DEBUG, "WARNING - INVALID type found in " << path.str() << '\n'); + } else { + // Push all stations onto frequency map + commlist_freq[a.freq].push_back(a); + + // Push non-atis stations onto bucket map as well + // In fact, push all stations onto bucket map for now so FGATCMgr::GetFrequency() works. + //if(a.type != ATIS and/or AWOS?) { + // get bucket number + SGBucket bucket(a.geod); + int bucknum = bucket.gen_index(); + commlist_bck[bucknum].push_back(a); + //} + } + + fin >> skipcomment; + } + + fin.close(); + return true; +*/ +} + + +// query the database for the specified frequency, lon and lat are in +// degrees, elev is in meters +// If no atc_type is specified, it returns true if any non-invalid type is found +// If atc_type is specifed, returns true only if the specified type is found +bool FGCommList::FindByFreq(const SGGeod& aPos, double freq, + ATCData* ad, atc_type tp ) +{ +/* + comm_list_type stations; + stations = commlist_freq[kHz10(freq)]; + comm_list_iterator current = stations.begin(); + comm_list_iterator last = stations.end(); + + // double az1, az2, s; + SGVec3d aircraft = SGVec3d::fromGeod(aPos); + const double orig_max_d = 1e100; + double max_d = orig_max_d; + double d; + // TODO - at the moment this loop returns the first match found in range + // We want to return the closest match in the event of a frequency conflict + for ( ; current != last ; ++current ) { + d = distSqr(aircraft, current->cart); + + //cout << " dist = " << sqrt(d) + // << " range = " << current->range * SG_NM_TO_METER << endl; + + // TODO - match up to twice the published range so we can model + // reduced signal strength + // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. + if ( d < (current->range * SG_NM_TO_METER + * current->range * SG_NM_TO_METER ) ) { + //cout << "matched = " << current->ident << endl; + if((tp == INVALID) || (tp == (*current).type)) { + if(d < max_d) { + max_d = d; + *ad = *current; + } + } + } + } + + if(max_d < orig_max_d) { + return true; + } else { + return false; + } +*/ +} + +int FGCommList::FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp) +{ +/* + // number of relevant stations found within range + int found = 0; + stations->erase(stations->begin(), stations->end()); + + // get bucket number for plane position + SGBucket buck(aPos); + + // get neigboring buckets + int bx = (int)( range*SG_NM_TO_METER / buck.get_width_m() / 2) + 1; + int by = (int)( range*SG_NM_TO_METER / buck.get_height_m() / 2 ) + 1; + + // loop over bucket range + for ( int i=-bx; i<=bx; i++) { + for ( int j=-by; j<=by; j++) { + buck = sgBucketOffset(aPos.getLongitudeDeg(), aPos.getLatitudeDeg(), i, j); + long int bucket = buck.gen_index(); + comm_map_const_iterator Fstations = commlist_bck.find(bucket); + if (Fstations == commlist_bck.end()) continue; + comm_list_const_iterator current = Fstations->second.begin(); + comm_list_const_iterator last = Fstations->second.end(); + + + // double az1, az2, s; + SGVec3d aircraft = SGVec3d::fromGeod(aPos); + double d; + for(; current != last; ++current) { + if((current->type == tp) || (tp == INVALID)) { + d = distSqr(aircraft, current->cart); + // NOTE The below is squared since we match to distance3Dsquared (above) to avoid a sqrt. + if ( d < (current->range * SG_NM_TO_METER + * current->range * SG_NM_TO_METER ) ) { + stations->push_back(*current); + ++found; + } + } + } + } + } + return found; +*/ +} + + +// Returns the distance in meters to the closest station of a given type, +// with the details written into ATCData& ad. If no type is specifed simply +// returns the distance to the closest station of any type. +// Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). +// Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if +// say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result +// and giving up after 1000. +double FGCommList::FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp, double max_range) { +/* + int num_stations = 0; + int range = 10; + comm_list_type stations; + comm_list_iterator itr; + double distance = -9999.0; + + while(num_stations == 0) { + num_stations = FindByPos(aPos, range, &stations, tp); + if(num_stations) { + double closest = max_range * SG_NM_TO_METER; + double tmp; + for(itr = stations.begin(); itr != stations.end(); ++itr) { + ATCData ad2 = *itr; + const FGAirport *a = fgFindAirportID(ad2.ident); + if (a) { + tmp = dclGetHorizontalSeparation(ad2.geod, aPos); + if(tmp <= closest) { + closest = tmp; + distance = tmp; + ad = *itr; + } + } + } + //cout << "Closest station is " << ad.ident << " at a range of " << distance << " meters\n"; + return(distance); + } + if(range > max_range) { + break; + } + range *= 10; + } + return(-9999.0); +*/ +} + + +// Find by Airport code. +// This is basically a wrapper for a call to the airport database to get the airport +// position followed by a call to FindByPos(...) +bool FGCommList::FindByCode( const string& ICAO, ATCData& ad, atc_type tp ) { +/* + const FGAirport *a = fgFindAirportID( ICAO); + if ( a) { + comm_list_type stations; + int found = FindByPos(a->geod(), 10.0, &stations, tp); + if(found) { + comm_list_iterator itr = stations.begin(); + while(itr != stations.end()) { + if(((*itr).ident == ICAO) && ((*itr).type == tp)) { + ad = *itr; + //cout << "FindByCode returns " << ICAO + // << " type: " << tp + // << " freq: " << itr->freq + // << endl; + return true; + } + ++itr; + } + } + } + return false; +*/ +} + +// TODO - this function should move somewhere else eventually! +// Return an appropriate sequence number for an ATIS transmission. +// Return sequence number + 2600 if sequence is unchanged since +// last time. +int FGCommList::GetAtisSequence( const string& apt_id, + const double tstamp, const int interval, const int special) +{ +/* + atis_transmission_type tran; + + if(atislog.find(apt_id) == atislog.end()) { // New station + tran.tstamp = tstamp - interval; +// Random number between 0 and 25 inclusive, i.e. 26 equiprobable outcomes: + tran.sequence = int(sg_random() * LTRS); + atislog[apt_id] = tran; + //cout << "New ATIS station: " << apt_id << " seq-1: " + // << tran.sequence << endl; + } + +// calculate the appropriate identifier and update the log + tran = atislog[apt_id]; + + int delta = int((tstamp - tran.tstamp) / interval); + tran.tstamp += delta * interval; + if (special && !delta) delta++; // a "special" ATIS update is required + tran.sequence = (tran.sequence + delta) % LTRS; + atislog[apt_id] = tran; + //if (delta) cout << "New ATIS sequence: " << tran.sequence + // << " Delta: " << delta << endl; + return(tran.sequence + (delta ? 0 : LTRS*1000)); +*/ +} +/***************************************************************************** + * FGKln89AlignedProjection + ****************************************************************************/ + +FGKln89AlignedProjection::FGKln89AlignedProjection() { + _origin.setLatitudeRad(0); + _origin.setLongitudeRad(0); + _origin.setElevationM(0); + _correction_factor = cos(_origin.getLatitudeRad()); +} + +FGKln89AlignedProjection::FGKln89AlignedProjection(const SGGeod& centre, double heading) { + _origin = centre; + _theta = heading * SG_DEGREES_TO_RADIANS; + _correction_factor = cos(_origin.getLatitudeRad()); +} + +FGKln89AlignedProjection::~FGKln89AlignedProjection() { +} + +void FGKln89AlignedProjection::Init(const SGGeod& centre, double heading) { + _origin = centre; + _theta = heading * SG_DEGREES_TO_RADIANS; + _correction_factor = cos(_origin.getLatitudeRad()); +} + +SGVec3d FGKln89AlignedProjection::ConvertToLocal(const SGGeod& pt) { + // convert from lat/lon to orthogonal + double delta_lat = pt.getLatitudeRad() - _origin.getLatitudeRad(); + double delta_lon = pt.getLongitudeRad() - _origin.getLongitudeRad(); + double y = sin(delta_lat) * SG_EQUATORIAL_RADIUS_M; + double x = sin(delta_lon) * SG_EQUATORIAL_RADIUS_M * _correction_factor; + + // Align + if(_theta != 0.0) { + double xbar = x; + x = x*cos(_theta) - y*sin(_theta); + y = (xbar*sin(_theta)) + (y*cos(_theta)); + } + + return SGVec3d(x, y, pt.getElevationM()); +} + +SGGeod FGKln89AlignedProjection::ConvertFromLocal(const SGVec3d& pt) { + // de-align + double thi = _theta * -1.0; + double x = pt.x()*cos(thi) - pt.y()*sin(thi); + double y = (pt.x()*sin(thi)) + (pt.y()*cos(thi)); + + // convert from orthogonal to lat/lon + double delta_lat = asin(y / SG_EQUATORIAL_RADIUS_M); + double delta_lon = asin(x / SG_EQUATORIAL_RADIUS_M) / _correction_factor; + + return SGGeod::fromRadM(_origin.getLongitudeRad()+delta_lon, _origin.getLatitudeRad()+delta_lat, pt.z()); +} + +#endif // #ENABLE_ATCDCL \ No newline at end of file diff --git a/src/ATC/atcutils.hxx b/src/ATC/atcutils.hxx new file mode 100644 index 000000000..e26d204b0 --- /dev/null +++ b/src/ATC/atcutils.hxx @@ -0,0 +1,185 @@ +// atcutils.hxx +// +// This file contains a collection of classes from David Luff's +// AI/ATC code that are shared by non-AI parts of FlightGear. +// more specifcially, it contains implementations of FGCommList and +// FGATCAlign +// +// Written by David Luff and Alexander Kappes, started Jan 2003. +// Based on navlist.hxx by Curtis Olson, started April 2000. +// +// Copyright (C) 2000 Curtis L. Olson - http://www.flightgear.org/~curt +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +/***************************************************************** +* +* FGCommList is used to store communication frequency information +* for the ATC and AI subsystems. Two maps are maintained - one +* searchable by location and one searchable by frequency. The +* data structure returned from the search is the ATCData struct +* defined in ATC.hxx, containing location, frequency, name, range +* and type of the returned station. +* +******************************************************************/ + +#ifndef _FG_ATCUTILS_HXX +#define _FG_ATCUTILS_HXX + + + +#include + +#include +#include +#include + +//#include "ATC.hxx" +//#include "atis.hxx" + +#if !ENABLE_ATCDCL + +class SGPath; +class ATCData; + +// Possible types of ATC type that the radios may be tuned to. +// INVALID implies not tuned in to anything. +enum atc_type { + AWOS, + ATIS, + GROUND, + TOWER, + APPROACH, + DEPARTURE, + ENROUTE, + INVALID /* must be last element; see ATC_NUM_TYPES */ +}; + + +// A list of ATC stations +typedef std::list < ATCData > comm_list_type; +typedef comm_list_type::iterator comm_list_iterator; +typedef comm_list_type::const_iterator comm_list_const_iterator; + +// A map of ATC station lists +typedef std::map < int, comm_list_type > comm_map_type; +typedef comm_map_type::iterator comm_map_iterator; +typedef comm_map_type::const_iterator comm_map_const_iterator; + + +class FGCommList { + +public: + + FGCommList(); + ~FGCommList(); + + // load all comm frequencies and build the map + bool init( const SGPath& path ); + + // query the database for the specified frequency, lon and lat are + // If no atc_type is specified, it returns true if any non-invalid type is found. + // If atc_type is specifed, returns true only if the specified type is found. + // Returns the station closest to the supplied position. + // The data found is written into the passed-in ATCData structure. + bool FindByFreq(const SGGeod& aPos, double freq, ATCData* ad, atc_type tp = INVALID ); + + // query the database by location, lon and lat are in degrees, elev is in meters, range is in nautical miles. + // Returns the number of stations of the specified atc_type tp that are in range of the position defined by + // lon, lat and elev, and pushes them into stations. + // If no atc_type is specifed, returns the number of all stations in range, and pushes them into stations + // ** stations is erased before use ** + int FindByPos(const SGGeod& aPos, double range, comm_list_type* stations, atc_type tp = INVALID ); + + // Returns the distance in meters to the closest station of a given type, + // with the details written into ATCData& ad. If no type is specifed simply + // returns the distance to the closest station of any type. + // Returns -9999 if no stations found within max_range in nautical miles (default 100 miles). + // Note that the search algorithm starts at 10 miles and multiplies by 10 thereafter, so if + // say 300 miles is specifed 10, then 100, then 1000 will be searched, breaking at first result + // and giving up after 1000. + // !!!Be warned that searching anything over 100 miles will pause the sim unacceptably!!! + // (The ability to search longer ranges should be used during init only). + double FindClosest(const SGGeod& aPos, ATCData& ad, atc_type tp = INVALID, double max_range = 100.0 ); + + // Find by Airport code. + bool FindByCode( const std::string& ICAO, ATCData& ad, atc_type tp = INVALID ); + + // Return the sequence letter for an ATIS transmission given transmission time and airport id + // This maybe should get moved somewhere else!! + int GetAtisSequence( const std::string& apt_id, const double tstamp, + const int interval, const int flush=0); + + // Comm stations mapped by frequency + //comm_map_type commlist_freq; + + // Comm stations mapped by bucket + //comm_map_type commlist_bck; + + // Load comms from a specified path (which must include the filename) +private: + + bool LoadComms(const SGPath& path); + +//----------- This stuff is left over from atislist.[ch]xx and maybe should move somewhere else + // Add structure and map for storing a log of atis transmissions + // made in this session of FlightGear. This allows the callsign + // to be allocated correctly wrt time. + //typedef struct { + // double tstamp; + // int sequence; + //} atis_transmission_type; + + //typedef std::map < std::string, atis_transmission_type > atis_log_type; + //typedef atis_log_type::iterator atis_log_iterator; + //typedef atis_log_type::const_iterator atis_log_const_iterator; + + //atis_log_type atislog; +//----------------------------------------------------------------------------------------------- + +}; + + +extern FGCommList *current_commlist; + +// FGATCAlignedProjection - a class to project an area local to a runway onto an orthogonal co-ordinate system +// with the origin at the threshold and the runway aligned with the y axis. +class FGKln89AlignedProjection { + +public: + FGKln89AlignedProjection(); + FGKln89AlignedProjection(const SGGeod& centre, double heading); + ~FGKln89AlignedProjection(); + + void Init(const SGGeod& centre, double heading); + + // Convert a lat/lon co-ordinate (degrees) to the local projection (meters) + SGVec3d ConvertToLocal(const SGGeod& pt); + + // Convert a local projection co-ordinate (meters) to lat/lon (degrees) + SGGeod ConvertFromLocal(const SGVec3d& pt); + +private: + SGGeod _origin; // lat/lon of local area origin (the threshold) + double _theta; // the rotation angle for alignment in radians + double _correction_factor; // Reduction in surface distance per degree of longitude due to latitude. Saves having to do a cos() every call. + +}; + +#endif // #if ENABLE_ATCDCL + +#endif // _FG_ATCUTILS_HXX + + diff --git a/src/Airports/apt_loader.cxx b/src/Airports/apt_loader.cxx index 3dfcdf059..8984214d9 100644 --- a/src/Airports/apt_loader.cxx +++ b/src/Airports/apt_loader.cxx @@ -47,6 +47,8 @@ #include "pavement.hxx" #if ENABLE_ATCDCL # include +#else + #include #endif #include @@ -78,11 +80,8 @@ public: {} -#ifdef ENABLE_ATCDCL + void parseAPT(const string &aptdb_file, FGCommList *comm_list) -#else - void parseAPT(const string &aptdb_file) -#endif { sg_gzifstream in( aptdb_file ); @@ -162,7 +161,9 @@ public: } else if ( line_id == 0 ) { // ?? } else if ( line_id == 50 ) { + parseATISLine(comm_list, simgear::strutils::split(line)); + } else if ( line_id >= 51 && line_id <= 56 ) { // other frequency entries (ignore) } else if ( line_id == 110 ) { @@ -468,7 +469,7 @@ private: pvt->addNode(pos, num == 113); } } -#if ENABLE_ATCDCL + void parseATISLine(FGCommList *comm_list, const vector& token) { if ( rwy_count <= 0 ) { @@ -485,6 +486,7 @@ private: // 50 11770 AWOS 3 // This code parallels code found in "operator>>" in ATC.hxx; // FIXME: unify the code. +#if ENABLE_ATCDCL ATCData a; a.geod = SGGeod::fromDegFt(rwy_lon_accum / (double)rwy_count, rwy_lat_accum / (double)rwy_count, last_apt_elev); @@ -503,6 +505,8 @@ private: SGBucket bucket(a.geod); int bucknum = bucket.gen_index(); comm_list->commlist_bck[bucknum].push_back(a); +#else +#endif #if 0 SG_LOG( SG_GENERAL, SG_ALERT, "Loaded ATIS/AWOS for airport: " << a.ident @@ -512,7 +516,7 @@ private: << " type: " << a.type ); #endif } -#endif + }; @@ -520,19 +524,11 @@ private: // metar file is used to mark the airports as having metar available // or not. bool fgAirportDBLoad( const string &aptdb_file, -#if ENABLE_ATCDCL FGCommList *comm_list, const std::string &metar_file ) -#else - const std::string &metar_file ) -#endif { APTLoader ld; -#if ENABLE_ATCDCL ld.parseAPT(aptdb_file, comm_list); -#else - ld.parseAPT(aptdb_file); -#endif // // Load the metar.dat file and update apt db with stations that // have metar data. diff --git a/src/Airports/apt_loader.hxx b/src/Airports/apt_loader.hxx index 54596e8d8..7e2405638 100644 --- a/src/Airports/apt_loader.hxx +++ b/src/Airports/apt_loader.hxx @@ -35,8 +35,8 @@ class FGCommList; // Load the airport data base from the specified aptdb file. The // metar file is used to mark the airports as having metar available // or not. + bool fgAirportDBLoad( const std::string &aptdb_file, FGCommList *comm_list, const std::string &metar_file ); - #endif // _FG_APT_LOADER_HXX diff --git a/src/Instrumentation/KLN89/kln89.cxx b/src/Instrumentation/KLN89/kln89.cxx index c028e17ae..0f7345f32 100644 --- a/src/Instrumentation/KLN89/kln89.cxx +++ b/src/Instrumentation/KLN89/kln89.cxx @@ -43,7 +43,12 @@ #include "kln89_symbols.hxx" #include +#if ENABLE_ATCDCL #include +#else +#include "kln89_align.hxx" +#endif + #include
#include #include @@ -676,12 +681,17 @@ void KLN89::DrawMap(bool draw_avs) { double mapScaleMeters = _mapScale * (_mapScaleUnits == 0 ? SG_NM_TO_METER : 1000); // TODO - use an aligned projection when either DTK or TK up! +#if ENABLE_ATCDCL FGATCAlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading); - +#else + FGKln89AlignedProjection mapProj(SGGeod::fromRad(_gpsLon, _gpsLat), _mapHeading); +#endif double meter_per_pix = (_mapOrientation == 0 ? mapScaleMeters / 20.0f : mapScaleMeters / 29.0f); - SGGeod bottomLeft = mapProj.ConvertFromLocal(SGVec3d(gps_max(-57.0 * meter_per_pix, -50000), gps_max((_mapOrientation == 0 ? -20.0 * meter_per_pix : -11.0 * meter_per_pix), -25000), 0.0)); SGGeod topRight = mapProj.ConvertFromLocal(SGVec3d(gps_min(54.0 * meter_per_pix, 50000), gps_min((_mapOrientation == 0 ? 20.0 * meter_per_pix : 29.0 * meter_per_pix), 25000), 0.0)); + + + // Draw Airport labels first (but not one's that are waypoints) // Draw Airports first (but not one's that are waypoints) diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index ded6765fc..3a8a078c1 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -78,6 +78,8 @@ # include # include # include "ATCDCL/commlist.hxx" +#else +# include "ATC/atcutils.hxx" #endif #include @@ -982,13 +984,9 @@ fgInitNav () -#if ENABLE_ATCDCL current_commlist = new FGCommList; current_commlist->init( globals->get_fg_root() ); fgAirportDBLoad( aptdb.str(), current_commlist, p_metar.str() ); -#else - fgAirportDBLoad( aptdb.str(), p_metar.str() ); -#endif FGNavList *navlist = new FGNavList; FGNavList *loclist = new FGNavList; @@ -1623,10 +1621,11 @@ bool fgInitSubsystems() { // Initialise the ATC Manager //////////////////////////////////////////////////////////////////// +#if ENABLE_ATCDCL SG_LOG(SG_GENERAL, SG_INFO, " ATC Manager"); globals->set_ATC_mgr(new FGATCMgr); globals->get_ATC_mgr()->init(); - + //////////////////////////////////////////////////////////////////// // Initialise the AI Manager //////////////////////////////////////////////////////////////////// @@ -1634,7 +1633,7 @@ bool fgInitSubsystems() { SG_LOG(SG_GENERAL, SG_INFO, " AI Manager"); globals->set_AI_mgr(new FGAIMgr); globals->get_AI_mgr()->init(); - +#endif //////////////////////////////////////////////////////////////////// // Initialise the AI Model Manager //////////////////////////////////////////////////////////////////// From 16dd34839fc4b8eb419a8681fe0bdf51f46b20cd Mon Sep 17 00:00:00 2001 From: durk Date: Sun, 17 Jan 2010 07:48:04 +0000 Subject: [PATCH 12/29] Replacement code for ATCDCL functions are in ATC/atcutils.[ch]xx. Thanks to Alex Buzin for reporting. --- src/Instrumentation/KLN89/kln89.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/KLN89/kln89.cxx b/src/Instrumentation/KLN89/kln89.cxx index 0f7345f32..7b08ba735 100644 --- a/src/Instrumentation/KLN89/kln89.cxx +++ b/src/Instrumentation/KLN89/kln89.cxx @@ -46,7 +46,7 @@ #if ENABLE_ATCDCL #include #else -#include "kln89_align.hxx" +#include "ATC/atcutils.hxx" #endif #include
From 546d3e265d7d3678c89c9125326a18487e43f5a7 Mon Sep 17 00:00:00 2001 From: durk Date: Sun, 17 Jan 2010 07:55:19 +0000 Subject: [PATCH 13/29] And this time, fix it more properly. :-) --- src/Instrumentation/KLN89/kln89.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Instrumentation/KLN89/kln89.cxx b/src/Instrumentation/KLN89/kln89.cxx index 7b08ba735..b074e2295 100644 --- a/src/Instrumentation/KLN89/kln89.cxx +++ b/src/Instrumentation/KLN89/kln89.cxx @@ -46,7 +46,7 @@ #if ENABLE_ATCDCL #include #else -#include "ATC/atcutils.hxx" +#include #endif #include
From 5aea857dd66354a453dbc95934d8b76719038f32 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Tue, 26 Jan 2010 11:06:37 +0100 Subject: [PATCH 14/29] initialize track variable in constructors --- src/FDM/flight.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/FDM/flight.cxx b/src/FDM/flight.cxx index c9956d8cb..10ce9948a 100644 --- a/src/FDM/flight.cxx +++ b/src/FDM/flight.cxx @@ -120,6 +120,7 @@ FGInterface::_setup () runway_altitude=0; climb_rate=0; altitude_agl=0; + track=0; } void From 49e477f6a744100f7f855a1a728a4a511cb72da7 Mon Sep 17 00:00:00 2001 From: jmt Date: Thu, 28 Jan 2010 09:42:57 +0000 Subject: [PATCH 15/29] Bugfix: ensure navradio nav-loc and has-gs properties clear to false when no valid station is tuned / radio is u/s. --- src/Instrumentation/navradio.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Instrumentation/navradio.cxx b/src/Instrumentation/navradio.cxx index 7a8215f3e..3e403d9ca 100644 --- a/src/Instrumentation/navradio.cxx +++ b/src/Instrumentation/navradio.cxx @@ -368,6 +368,8 @@ void FGNavRadio::clearOutputs() gs_deflection_deg_node->setDoubleValue(0.0); gs_deflection_norm_node->setDoubleValue(0.0); gs_inrange_node->setBoolValue( false ); + loc_node->setBoolValue( false ); + has_gs_node->setBoolValue(false); to_flag_node->setBoolValue( false ); from_flag_node->setBoolValue( false ); @@ -892,7 +894,9 @@ void FGNavRadio::search() _gs = NULL; _dme = NULL; nav_id_node->setStringValue(""); - + loc_node->setBoolValue(false); + has_gs_node->setBoolValue(false); + _sgr->remove( nav_fx_name ); _sgr->remove( dme_fx_name ); } From 73334cf61b097f1647c4bd0969f40db6eb5296e9 Mon Sep 17 00:00:00 2001 From: jmt Date: Wed, 10 Feb 2010 19:28:35 +0000 Subject: [PATCH 16/29] Bugfix #35 / denker #20F: Guard against invalid font names in panel XML files, and make font name comparisons case-insensitive so that 'helvetica', 'Helvetica' or 'HELVETICA' work as expected. --- src/Cockpit/panel.cxx | 12 +++++++++++- src/GUI/new_gui.cxx | 45 +++++++++++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 18 deletions(-) diff --git a/src/Cockpit/panel.cxx b/src/Cockpit/panel.cxx index e2caa87f0..be2f12570 100644 --- a/src/Cockpit/panel.cxx +++ b/src/Cockpit/panel.cxx @@ -1109,7 +1109,12 @@ FGTextLayer::draw (osg::State& state) transform(); FGFontCache *fc = globals->get_fontcache(); - text_renderer.setFont(fc->getTexFont(_font_name.c_str())); + fntFont* font = fc->getTexFont(_font_name.c_str()); + if (!font) { + return; // don't crash on missing fonts + } + + text_renderer.setFont(font); text_renderer.setPointSize(_pointSize); text_renderer.begin(); @@ -1170,6 +1175,11 @@ void FGTextLayer::setFontName(const string &name) { _font_name = name + ".txf"; + FGFontCache *fc = globals->get_fontcache(); + fntFont* font = fc->getTexFont(_font_name.c_str()); + if (!font) { + SG_LOG(SG_GENERAL, SG_WARN, "unable to find font:" << name); + } } diff --git a/src/GUI/new_gui.cxx b/src/GUI/new_gui.cxx index ff9c7ad3c..b17c0b021 100644 --- a/src/GUI/new_gui.cxx +++ b/src/GUI/new_gui.cxx @@ -14,6 +14,8 @@ #include #include +#include + #include
#include "menubar.hxx" @@ -468,24 +470,29 @@ inline bool FGFontCache::FntParamsLess::operator()(const FntParams& f1, struct FGFontCache::fnt * FGFontCache::getfnt(const char *name, float size, float slant) { - string fontName(name); + string fontName = boost::to_lower_copy(string(name)); FntParams fntParams(fontName, size, slant); PuFontMap::iterator i = _puFonts.find(fntParams); - if (i != _puFonts.end()) + if (i != _puFonts.end()) { + // found in the puFonts map, all done return i->second; + } + // fntTexFont s are all preloaded into the _texFonts map TexFontMap::iterator texi = _texFonts.find(fontName); - fntTexFont* texfont = 0; - puFont* pufont = 0; + fntTexFont* texfont = NULL; + puFont* pufont = NULL; if (texi != _texFonts.end()) { texfont = texi->second; } else { + // check the built-in PUI fonts (in guifonts array) const GuiFont* guifont = std::find_if(&guifonts[0], guifontsEnd, GuiFont::Predicate(name)); if (guifont != guifontsEnd) { pufont = guifont->font; } } + fnt* f = new fnt; if (pufont) { f->pufont = pufont; @@ -527,16 +534,18 @@ FGFontCache::get(SGPropertyNode *node) void FGFontCache::init() { - if (!_initialized) { - char *envp = ::getenv("FG_FONTS"); - if (envp != NULL) { - _path.set(envp); - } else { - _path.set(globals->get_fg_root()); - _path.append("Fonts"); - } - _initialized = true; + if (_initialized) { + return; } + + char *envp = ::getenv("FG_FONTS"); + if (envp != NULL) { + _path.set(envp); + } else { + _path.set(globals->get_fg_root()); + _path.append("Fonts"); + } + _initialized = true; } SGPath @@ -552,7 +561,7 @@ FGFontCache::getfntpath(const char *name) path = SGPath(_path); path.append("Helvetica.txf"); - + SG_LOG(SG_GENERAL, SG_WARN, "Unknown font name '" << name << "', defaulting to Helvetica"); return path; } @@ -569,9 +578,11 @@ bool FGFontCache::initializeFonts() path.append(dirEntry->d_name); if (path.extension() == fontext) { fntTexFont* f = new fntTexFont; - if (f->load((char *)path.c_str())) - _texFonts[string(dirEntry->d_name)] = f; - else + if (f->load((char *)path.c_str())) { + // convert font names in the map to lowercase for matching + string fontName = boost::to_lower_copy(string(dirEntry->d_name)); + _texFonts[fontName] = f; + } else delete f; } } From 26361f2f5aea7d6e7c621235bdbf71e76c87997a Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 15 Feb 2010 23:34:53 +0000 Subject: [PATCH 17/29] Bugfix: stop dialogs jumping around when re-layout occurs - make positions persistent. --- src/GUI/dialog.cxx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/GUI/dialog.cxx b/src/GUI/dialog.cxx index 95fe5f04c..e20a62b93 100644 --- a/src/GUI/dialog.cxx +++ b/src/GUI/dialog.cxx @@ -295,8 +295,16 @@ int fgPopup::checkHit(int button, int updown, int x, int y) getFirstChild()->setSize(w, h); // dialog background puFrame } } else { - setPosition(x + _dlgX - _startX, y + _dlgY - _startY); - } + int posX = x + _dlgX - _startX, + posY = y + _dlgY - _startY; + setPosition(posX, posY); + + GUIInfo *info = (GUIInfo *)getUserData(); + if (info && info->node) { + info->node->setIntValue("x", posX); + info->node->setIntValue("y", posY); + } + } // re-positioning } else if (_dragging) { fgSetMouseCursor(_start_cursor); From 4f99561f6340a1056f507e91122daafe44e289d1 Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 15 Feb 2010 23:57:56 +0000 Subject: [PATCH 18/29] Quiet: reduce log-level of various things from INFO to DEBUG or BULK, and tune the categories/level of some specific messages. Commit split by timoore in order to apply most of it to maint branch. --- src/Cockpit/hud.cxx | 6 +++--- src/Cockpit/hud_card.cxx | 2 +- src/Cockpit/hud_gaug.cxx | 2 +- src/Cockpit/hud_labl.cxx | 2 +- src/Cockpit/hud_ladr.cxx | 2 +- src/Cockpit/hud_rwy.cxx | 2 +- src/Cockpit/hud_tbi.cxx | 2 +- src/Cockpit/panel_io.cxx | 15 +++------------ src/GUI/menubar.cxx | 10 +++++----- src/Main/fg_commands.cxx | 4 ++-- 10 files changed, 19 insertions(+), 28 deletions(-) diff --git a/src/Cockpit/hud.cxx b/src/Cockpit/hud.cxx index aae7bf22e..b06a1917b 100644 --- a/src/Cockpit/hud.cxx +++ b/src/Cockpit/hud.cxx @@ -182,7 +182,7 @@ int readHud( istream &input ) } - SG_LOG(SG_INPUT, SG_INFO, "Read properties for " << + SG_LOG(SG_INPUT, SG_DEBUG, "Read properties for " << root.getStringValue("name")); if (!root.getNode("depreciated")) @@ -191,7 +191,7 @@ int readHud( istream &input ) HUD_deque.erase( HUD_deque.begin(), HUD_deque.end()); - SG_LOG(SG_INPUT, SG_INFO, "Reading Hud instruments"); + SG_LOG(SG_INPUT, SG_DEBUG, "Reading Hud instruments"); const SGPropertyNode * instrument_group = root.getChild("instruments"); int nInstruments = instrument_group->nChildren(); @@ -203,7 +203,7 @@ int readHud( istream &input ) SGPath path( globals->get_fg_root() ); path.append(node->getStringValue("path")); - SG_LOG(SG_INPUT, SG_INFO, "Reading Instrument " + SG_LOG(SG_INPUT, SG_DEBUG, "Reading Instrument " << node->getName() << " from " << path.str()); diff --git a/src/Cockpit/hud_card.cxx b/src/Cockpit/hud_card.cxx index 858c3716f..746e9317a 100644 --- a/src/Cockpit/hud_card.cxx +++ b/src/Cockpit/hud_card.cxx @@ -56,7 +56,7 @@ hud_card::hud_card(const SGPropertyNode *node) : Maj_div(node->getIntValue("major_divs")), // FIXME dup Min_div(node->getIntValue("minor_divs")) // FIXME dup { - SG_LOG(SG_INPUT, SG_INFO, "Done reading dial/tape instrument " + SG_LOG(SG_INPUT, SG_BULK, "Done reading dial/tape instrument " << node->getStringValue("name", "[unnamed]")); set_data_source(get_func(node->getStringValue("loadfn"))); diff --git a/src/Cockpit/hud_gaug.cxx b/src/Cockpit/hud_gaug.cxx index 04c2018ed..7a4d5ce6f 100644 --- a/src/Cockpit/hud_gaug.cxx +++ b/src/Cockpit/hud_gaug.cxx @@ -26,7 +26,7 @@ gauge_instr::gauge_instr(const SGPropertyNode *node) : 0, /* hud.cxx: static int dp_shoing = 0; */ // FIXME node->getBoolValue("working", true)) { - SG_LOG(SG_INPUT, SG_INFO, "Done reading gauge instrument " + SG_LOG(SG_INPUT, SG_BULK, "Done reading gauge instrument " << node->getStringValue("name", "[unnamed]")); set_data_source(get_func(node->getStringValue("loadfn"))); diff --git a/src/Cockpit/hud_labl.cxx b/src/Cockpit/hud_labl.cxx index d19fed056..95d69142a 100644 --- a/src/Cockpit/hud_labl.cxx +++ b/src/Cockpit/hud_labl.cxx @@ -33,7 +33,7 @@ instr_label::instr_label(const SGPropertyNode *node) : lon_node(fgGetNode("/position/longitude-string", true)), lat_node(fgGetNode("/position/latitude-string", true)) { - SG_LOG(SG_INPUT, SG_INFO, "Done reading instr_label instrument " + SG_LOG(SG_INPUT, SG_BULK, "Done reading instr_label instrument " << node->getStringValue("name", "[unnamed]")); set_data_source(get_func(node->getStringValue("data_source"))); diff --git a/src/Cockpit/hud_ladr.cxx b/src/Cockpit/hud_ladr.cxx index 38def8d9d..34b58d0eb 100644 --- a/src/Cockpit/hud_ladr.cxx +++ b/src/Cockpit/hud_ladr.cxx @@ -51,7 +51,7 @@ HudLadder::HudLadder(const SGPropertyNode *node) : if (fgGetBool("/sim/hud/enable3d", true) && HUD_style == 1) factor = 640.0 / 55.0; - SG_LOG(SG_INPUT, SG_INFO, "Done reading HudLadder instrument" + SG_LOG(SG_INPUT, SG_BULK, "Done reading HudLadder instrument" << node->getStringValue("name", "[unnamed]")); if (!width_units) diff --git a/src/Cockpit/hud_rwy.cxx b/src/Cockpit/hud_rwy.cxx index 2bc15bcd1..0f7866619 100644 --- a/src/Cockpit/hud_rwy.cxx +++ b/src/Cockpit/hud_rwy.cxx @@ -60,7 +60,7 @@ runway_instr::runway_instr(const SGPropertyNode *node) : drawIA(arrowScale > 0 ? true : false), drawIAAlways(arrowScale > 0 ? node->getBoolValue("arrow_always") : false) { - SG_LOG(SG_INPUT, SG_INFO, "Done reading runway instrument " + SG_LOG(SG_INPUT, SG_BULK, "Done reading runway instrument " << node->getStringValue("name", "[unnamed]")); view[0] = 0; diff --git a/src/Cockpit/hud_tbi.cxx b/src/Cockpit/hud_tbi.cxx index 0c96340d1..c5fd670c6 100644 --- a/src/Cockpit/hud_tbi.cxx +++ b/src/Cockpit/hud_tbi.cxx @@ -32,7 +32,7 @@ fgTBI_instr::fgTBI_instr(const SGPropertyNode *node) : rad(node->getFloatValue("rad")) { - SG_LOG(SG_INPUT, SG_INFO, "Done reading TBI instrument" + SG_LOG(SG_INPUT, SG_BULK, "Done reading TBI instrument" << node->getStringValue("name", "[unnamed]")); } diff --git a/src/Cockpit/panel_io.cxx b/src/Cockpit/panel_io.cxx index fdb6b2686..7ef58b6e1 100644 --- a/src/Cockpit/panel_io.cxx +++ b/src/Cockpit/panel_io.cxx @@ -684,7 +684,6 @@ readPanel (const SGPropertyNode * root) if (bgTexture.empty()) bgTexture = "FOO"; panel->setBackground(FGTextureManager::createTexture(bgTexture.c_str())); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << bgTexture ); // // Get multibackground if any... @@ -692,49 +691,41 @@ readPanel (const SGPropertyNode * root) string mbgTexture = root->getStringValue("multibackground[0]"); if (!mbgTexture.empty()) { panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 0); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[1]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 1); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[2]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 2); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[3]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 3); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[4]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 4); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[5]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 5); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[6]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 6); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); mbgTexture = root->getStringValue("multibackground[7]"); if (mbgTexture.empty()) mbgTexture = "FOO"; panel->setMultiBackground(FGTextureManager::createTexture(mbgTexture.c_str()), 7); - SG_LOG( SG_COCKPIT, SG_INFO, "Set background texture to " << mbgTexture ); } @@ -743,7 +734,7 @@ readPanel (const SGPropertyNode * root) // // Create each instrument. // - SG_LOG( SG_COCKPIT, SG_INFO, "Reading panel instruments" ); + SG_LOG( SG_COCKPIT, SG_DEBUG, "Reading panel instruments" ); const SGPropertyNode * instrument_group = root->getChild("instruments"); if (instrument_group != 0) { int nInstruments = instrument_group->nChildren(); @@ -799,12 +790,12 @@ readPanel (const SGPropertyNode * root) SG_LOG( SG_COCKPIT, SG_WARN, "Unknown special instrument found" ); } } else { - SG_LOG( SG_COCKPIT, SG_INFO, "Skipping " << node->getName() + SG_LOG( SG_COCKPIT, SG_WARN, "Skipping " << node->getName() << " in instruments section" ); } } } - SG_LOG( SG_COCKPIT, SG_INFO, "Done reading panel instruments" ); + SG_LOG( SG_COCKPIT, SG_BULK, "Done reading panel instruments" ); // diff --git a/src/GUI/menubar.cxx b/src/GUI/menubar.cxx index 3cd03bbcc..d34f0fe3a 100644 --- a/src/GUI/menubar.cxx +++ b/src/GUI/menubar.cxx @@ -258,7 +258,7 @@ FGMenuBar::destroy_menubar () // Delete all the character arrays // we were forced to keep around for // plib. - SG_LOG(SG_GENERAL, SG_INFO, "Deleting char arrays"); + SG_LOG(SG_GENERAL, SG_BULK, "Deleting char arrays"); for (i = 0; i < _char_arrays.size(); i++) { for (int j = 0; _char_arrays[i][j] != 0; j++) free(_char_arrays[i][j]); // added with strdup @@ -268,20 +268,20 @@ FGMenuBar::destroy_menubar () // Delete all the callback arrays // we were forced to keep around for // plib. - SG_LOG(SG_GENERAL, SG_INFO, "Deleting callback arrays"); + SG_LOG(SG_GENERAL, SG_BULK, "Deleting callback arrays"); for (i = 0; i < _callback_arrays.size(); i++) delete[] _callback_arrays[i]; // Delete all those bindings - SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings"); + SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings"); map >::iterator it; for (it = _bindings.begin(); it != _bindings.end(); it++) { - SG_LOG(SG_GENERAL, SG_INFO, "Deleting bindings for " << it->first); + SG_LOG(SG_GENERAL, SG_BULK, "Deleting bindings for " << it->first); for ( i = 0; i < it->second.size(); i++ ) delete it->second[i]; } - SG_LOG(SG_GENERAL, SG_INFO, "Done."); + SG_LOG(SG_GENERAL, SG_BULK, "Done."); } void diff --git a/src/Main/fg_commands.cxx b/src/Main/fg_commands.cxx index c8b938473..70ceb23b5 100644 --- a/src/Main/fg_commands.cxx +++ b/src/Main/fg_commands.cxx @@ -1596,9 +1596,9 @@ static struct { void fgInitCommands () { - SG_LOG(SG_GENERAL, SG_INFO, "Initializing basic built-in commands:"); + SG_LOG(SG_GENERAL, SG_BULK, "Initializing basic built-in commands:"); for (int i = 0; built_ins[i].name != 0; i++) { - SG_LOG(SG_GENERAL, SG_INFO, " " << built_ins[i].name); + SG_LOG(SG_GENERAL, SG_BULK, " " << built_ins[i].name); globals->get_commands()->addCommand(built_ins[i].name, built_ins[i].command); } From 301f3c5bd63572d0f681a86af95dd026c04139c6 Mon Sep 17 00:00:00 2001 From: jmt Date: Mon, 15 Feb 2010 23:57:56 +0000 Subject: [PATCH 19/29] Quiet: reduce log-level of various things from INFO to DEBUG or BULK, and tune the categories/level of some specific messages. Part of original commit that only applys to master branch. --- src/Autopilot/xmlauto.cxx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/Autopilot/xmlauto.cxx b/src/Autopilot/xmlauto.cxx index d37164a72..119083f66 100644 --- a/src/Autopilot/xmlauto.cxx +++ b/src/Autopilot/xmlauto.cxx @@ -1002,13 +1002,13 @@ void FGXMLAutopilotGroup::init() continue; } } catch (const sg_exception& e) { - SG_LOG( SG_ALL, SG_ALERT, "Failed to load autopilot configuration: " + SG_LOG( SG_AUTOPILOT, SG_ALERT, "Failed to load autopilot configuration: " << config.str() << ":" << e.getMessage() ); delete ap; continue; } - SG_LOG( SG_ALL, SG_INFO, "adding autopilot subsystem " << apName ); + SG_LOG( SG_AUTOPILOT, SG_INFO, "adding autopilot subsystem " << apName ); set_subsystem( apName, ap ); _autopilotNames.push_back( apName ); } @@ -1053,7 +1053,7 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { node = config_props->getChild(i); string name = node->getName(); // cout << name << endl; - SG_LOG( SG_ALL, SG_INFO, "adding autopilot component " << name ); + SG_LOG( SG_AUTOPILOT, SG_BULK, "adding autopilot component " << name ); if ( name == "pid-controller" ) { components.push_back( new FGPIDController( node ) ); } else if ( name == "pi-simple-controller" ) { @@ -1062,9 +1062,8 @@ bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) { components.push_back( new FGPredictor( node ) ); } else if ( name == "filter" ) { components.push_back( new FGDigitalFilter( node ) ); -// } else { -// SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: " -// << name ); + } else { + SG_LOG( SG_AUTOPILOT, SG_WARN, "Unknown top level autopilot section: " << name ); // return false; } } From 7df30059deb669048f5c973e8420885d26b9403c Mon Sep 17 00:00:00 2001 From: curt Date: Tue, 16 Feb 2010 20:26:26 +0000 Subject: [PATCH 20/29] Fix a couple "make dist" glitches. --- Makefile.am | 3 --- utils/Makefile.am | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/Makefile.am b/Makefile.am index 07fabe3e7..03063ab0d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -8,8 +8,6 @@ SUBDIRS = \ EXTRA_DIST = \ acinclude.m4 \ autogen.sh \ - FlightGear.dsp \ - FlightGear.dsw \ projects \ README \ README.OpenAL \ @@ -19,7 +17,6 @@ EXTRA_DIST = \ Thanks dist-hook: - (cd $(top_srcdir); $(HOME)/Projects/FlightGear/admin/am2dsp.pl) (cd $(top_srcdir); tar --exclude docs-mini/CVS --exclude hints/CVS \ -cf - docs-mini ) | (cd $(distdir); tar xvf -) rm -rf `find $(distdir)/projects -name CVS` diff --git a/utils/Makefile.am b/utils/Makefile.am index 1f183fec5..fc0f16d47 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -1,4 +1,4 @@ -DIST_SUBDIRS = GPSsmooth TerraSync Modeller js_server fgadmin xmlgrep propmerge +DIST_SUBDIRS = GPSsmooth TerraSync Modeller js_server fgadmin xmlgrep propmerge fgviewer SUBDIRS = GPSsmooth TerraSync Modeller js_server propmerge fgviewer From cb2c800434500fa3f32edd81afd75295ea94b33f Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 18 Feb 2010 16:07:12 +0000 Subject: [PATCH 21/29] Add data/Scenery/Airports/ to the data distribution. --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 03063ab0d..15ba35f43 100644 --- a/Makefile.am +++ b/Makefile.am @@ -97,6 +97,7 @@ data-tar: data/preferences.xml \ data/Protocol \ data/README \ + data/Scenery/Airports \ data/Scenery/Objects \ data/Scenery/Terrain \ data/Shaders \ From 4468d785b5f44795b7a840de45e51e167d73142d Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 21 Feb 2010 20:44:17 +0000 Subject: [PATCH 22/29] GPS / route-manager: add new custom widget to display the waypoints list. Supports various new editing features, including dragging to re-order, and +/- keys to adjust the target altitude for a waypoint. Also displays some additional information, and will display *even* more once I land airways/ SID/STAR support. --- src/Autopilot/route_mgr.cxx | 66 +-- src/Autopilot/route_mgr.hxx | 11 + src/GUI/Makefile.am | 12 +- src/GUI/WaypointList.cxx | 772 ++++++++++++++++++++++++++++++++++++ src/GUI/WaypointList.hxx | 171 ++++++++ src/GUI/dialog.cxx | 17 +- src/GUI/dialog.hxx | 2 + 7 files changed, 1016 insertions(+), 35 deletions(-) create mode 100644 src/GUI/WaypointList.cxx create mode 100644 src/GUI/WaypointList.hxx diff --git a/src/Autopilot/route_mgr.cxx b/src/Autopilot/route_mgr.cxx index d6d65cc20..64263dc25 100644 --- a/src/Autopilot/route_mgr.cxx +++ b/src/Autopilot/route_mgr.cxx @@ -172,6 +172,7 @@ void FGRouteMgr::init() { wpn->getChild("eta", 0, true); _route->clear(); + _route->set_current(0); update_mirror(); _pathNode = fgGetNode(RM "file-path", 0, true); @@ -285,17 +286,32 @@ void FGRouteMgr::setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDi aProp->setStringValue( eta_str ); } -void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) { +void FGRouteMgr::add_waypoint( const SGWayPoint& wp, int n ) +{ _route->add_waypoint( wp, n ); - if (_route->current_index() > n) { + if ((n >= 0) && (_route->current_index() > n)) { _route->set_current(_route->current_index() + 1); } - update_mirror(); - _edited->fireValueChanged(); + waypointsChanged(); } +void FGRouteMgr::waypointsChanged() +{ + double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM; + totalDistance->setDoubleValue(routeDistanceNm); + double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0); + if (cruiseSpeedKts > 1.0) { + // very very crude approximation, doesn't allow for climb / descent + // performance or anything else at all + ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0)); + } + + update_mirror(); + _edited->fireValueChanged(); + checkFinished(); +} SGWayPoint FGRouteMgr::pop_waypoint( int n ) { if ( _route->size() <= 0 ) { @@ -313,10 +329,7 @@ SGWayPoint FGRouteMgr::pop_waypoint( int n ) { SGWayPoint wp = _route->get_waypoint(n); _route->delete_waypoint(n); - update_mirror(); - _edited->fireValueChanged(); - checkFinished(); - + waypointsChanged(); return wp; } @@ -467,9 +480,15 @@ void FGRouteMgr::InputListener::valueChanged(SGPropertyNode *prop) mgr->loadRoute(); } else if (!strcmp(s, "@SAVE")) { mgr->saveRoute(); - } else if (!strcmp(s, "@POP")) - mgr->pop_waypoint(0); - else if (!strncmp(s, "@DELETE", 7)) + } else if (!strcmp(s, "@POP")) { + SG_LOG(SG_AUTOPILOT, SG_WARN, "route-manager @POP command is deprecated"); + } else if (!strcmp(s, "@NEXT")) { + mgr->jumpToIndex(mgr->currentWaypoint() + 1); + } else if (!strcmp(s, "@PREVIOUS")) { + mgr->jumpToIndex(mgr->currentWaypoint() - 1); + } else if (!strncmp(s, "@JUMP", 5)) { + mgr->jumpToIndex(atoi(s + 5)); + } else if (!strncmp(s, "@DELETE", 7)) mgr->pop_waypoint(atoi(s + 7)); else if (!strncmp(s, "@INSERT", 7)) { char *r; @@ -529,16 +548,6 @@ bool FGRouteMgr::activate() } _route->set_current(0); - - double routeDistanceNm = _route->total_distance() * SG_METER_TO_NM; - totalDistance->setDoubleValue(routeDistanceNm); - double cruiseSpeedKts = cruise->getDoubleValue("speed", 0.0); - if (cruiseSpeedKts > 1.0) { - // very very crude approximation, doesn't allow for climb / descent - // performance or anything else at all - ete->setDoubleValue(routeDistanceNm / cruiseSpeedKts * (60.0 * 60.0)); - } - active->setBoolValue(true); SG_LOG(SG_AUTOPILOT, SG_INFO, "route-manager, activate route ok"); return true; @@ -576,11 +585,6 @@ bool FGRouteMgr::checkFinished() void FGRouteMgr::jumpToIndex(int index) { - if (!active->getBoolValue()) { - SG_LOG(SG_AUTOPILOT, SG_ALERT, "trying to sequence waypoints with no active route"); - return; - } - if ((index < 0) || (index >= _route->size())) { SG_LOG(SG_AUTOPILOT, SG_ALERT, "passed invalid index (" << index << ") to FGRouteMgr::jumpToIndex"); @@ -637,6 +641,16 @@ int FGRouteMgr::currentWaypoint() const return _route->current_index(); } +void FGRouteMgr::setWaypointTargetAltitudeFt(unsigned int index, int altFt) +{ + SGWayPoint wp = _route->get_waypoint(index); + wp.setTargetAltFt(altFt); + // simplest way to update a waypoint is to remove and re-add it + _route->delete_waypoint(index); + _route->add_waypoint(wp, index); + waypointsChanged(); +} + void FGRouteMgr::saveRoute() { SGPath path(_pathNode->getStringValue()); diff --git a/src/Autopilot/route_mgr.hxx b/src/Autopilot/route_mgr.hxx index b09657259..a81689248 100644 --- a/src/Autopilot/route_mgr.hxx +++ b/src/Autopilot/route_mgr.hxx @@ -113,6 +113,12 @@ private: */ SGWayPoint* make_waypoint(const string& target); + /** + * Helper to keep various pieces of state in sync when the SGRoute is + * modified (waypoints added, inserted, removed). Notably, this fires the + * 'edited' signal. + */ + void waypointsChanged(); void update_mirror(); @@ -188,6 +194,11 @@ public: */ void jumpToIndex(int index); + /** + * + */ + void setWaypointTargetAltitudeFt(unsigned int index, int altFt); + void saveRoute(); void loadRoute(); }; diff --git a/src/GUI/Makefile.am b/src/GUI/Makefile.am index 890807f2a..d0ede62b6 100644 --- a/src/GUI/Makefile.am +++ b/src/GUI/Makefile.am @@ -16,13 +16,15 @@ endif libGUI_a_SOURCES = \ new_gui.cxx new_gui.hxx \ dialog.cxx dialog.hxx \ - menubar.cxx menubar.hxx \ - gui.cxx gui.h gui_funcs.cxx \ - fonts.cxx \ - AirportList.cxx AirportList.hxx \ + menubar.cxx menubar.hxx \ + gui.cxx gui.h gui_funcs.cxx \ + fonts.cxx \ + AirportList.cxx AirportList.hxx \ property_list.cxx property_list.hxx \ layout.cxx layout-props.cxx layout.hxx \ - SafeTexFont.cxx SafeTexFont.hxx + SafeTexFont.cxx SafeTexFont.hxx \ + WaypointList.cxx WaypointList.hxx \ + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/GUI/WaypointList.cxx b/src/GUI/WaypointList.cxx new file mode 100644 index 000000000..0ad02a30d --- /dev/null +++ b/src/GUI/WaypointList.cxx @@ -0,0 +1,772 @@ + + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include "WaypointList.hxx" + +#include +#include + +#include +#include +#include + +#include
+#include
+ +#include + +enum { + SCROLL_NO = 0, + SCROLL_UP, + SCROLL_DOWN +}; + +static const int DRAG_START_DISTANCE_PX = 5; + +class RouteManagerWaypointModel : + public WaypointList::Model, + public SGPropertyChangeListener +{ +public: + RouteManagerWaypointModel() + { + _rm = static_cast(globals->get_subsystem("route-manager")); + + SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true); + routeEdited->addChangeListener(this); + } + + virtual ~RouteManagerWaypointModel() + { + SGPropertyNode* routeEdited = fgGetNode("/autopilot/route-manager/signals/edited", true); + routeEdited->removeChangeListener(this); + } + +// implement WaypointList::Model + virtual unsigned int numWaypoints() const + { + return _rm->size(); + } + + virtual int currentWaypoint() const + { + return _rm->currentWaypoint(); + } + + virtual SGWayPoint waypointAt(unsigned int index) const + { + return _rm->get_waypoint(index); + } + + virtual void deleteAt(unsigned int index) + { + _rm->pop_waypoint(index); + } + + virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt) + { + _rm->setWaypointTargetAltitudeFt(index, altFt); + } + + virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int destIndex) + { + if (destIndex > srcIndex) { + --destIndex; + } + + SGWayPoint wp = _rm->pop_waypoint(srcIndex); + _rm->add_waypoint(wp, destIndex); + } + + virtual void setUpdateCallback(SGCallback* cb) + { + _cb = cb; + } + +// implement SGPropertyChangeListener + void valueChanged(SGPropertyNode *prop) + { + if (_cb) { + (*_cb)(); + } + } +private: + FGRouteMgr* _rm; + SGCallback* _cb; +}; + +////////////////////////////////////////////////////////////////////////////// + +static void drawClippedString(puFont& font, const char* s, int x, int y, int maxWidth) +{ + int fullWidth = font.getStringWidth(s); + if (fullWidth <= maxWidth) { // common case, easy and efficent + font.drawString(s, x, y); + return; + } + + int len = strlen(s); + char buf[len]; + memcpy(buf, s, len); + do { + buf[--len] = 0; + fullWidth = font.getStringWidth(buf); + } while (fullWidth > maxWidth); + + font.drawString(buf, x, y); +} + +////////////////////////////////////////////////////////////////////////////// + +WaypointList::WaypointList(int x, int y, int width, int height) : + puFrame(x, y, width, height), + GUI_ID(FGCLASS_WAYPOINTLIST), + _scrollPx(0), + _dragging(false), + _dragScroll(SCROLL_NO), + _showLatLon(false), + _model(NULL), + _updateCallback(NULL), + _scrollCallback(NULL) +{ + // pretend to be a list, so fgPopup doesn't mess with our mouse events + type |= PUCLASS_LIST; + setModel(new RouteManagerWaypointModel()); + setSize(width, height); + setValue(-1); +} + +WaypointList::~WaypointList() +{ + delete _model; + delete _updateCallback; + delete _scrollCallback; +} + +void WaypointList::setUpdateCallback(SGCallback* cb) +{ + _updateCallback = cb; +} + +void WaypointList::setScrollCallback(SGCallback* cb) +{ + _scrollCallback = cb; +} + +void WaypointList::setSize(int width, int height) +{ + double scrollP = getVScrollPercent(); + _heightPx = height; + puFrame::setSize(width, height); + + if (wantsVScroll()) { + setVScrollPercent(scrollP); + } else { + _scrollPx = 0; + } +} + +int WaypointList::checkHit ( int button, int updown, int x, int y ) +{ + if ( isHit( x, y ) || ( puActiveWidget () == this ) ) + { + doHit ( button, updown, x, y ) ; + return TRUE ; + } + + return FALSE ; +} + + +void WaypointList::doHit( int button, int updown, int x, int y ) +{ + puFrame::doHit(button, updown, x, y); + if (updown == PU_DRAG) { + handleDrag(x, y); + return; + } + + if (button != active_mouse_button) { + return; + } + + if (updown == PU_UP) { + puDeactivateWidget(); + if (_dragging) { + doDrop(x, y); + return; + } + } else if (updown == PU_DOWN) { + puSetActiveWidget(this, x, y); + _mouseDownX = x; + _mouseDownY = y; + return; + } + +// update selection + int row = rowForY(y - abox.min[1]); + if (row >= (int) _model->numWaypoints()) { + row = -1; // 'no selection' + } + + if (row == getSelected()) { + _showLatLon = !_showLatLon; + puPostRefresh(); + return; + } + + setSelected(row); +} + +void WaypointList::handleDrag(int x, int y) +{ + if (!_dragging) { + // don't start drags immediately, require a certain mouse movement first + int manhattanLength = abs(x - _mouseDownX) + abs(y - _mouseDownY); + if (manhattanLength < DRAG_START_DISTANCE_PX) { + return; + } + + _dragSourceRow = rowForY(y - abox.min[1]); + _dragging = true; + _dragScroll = SCROLL_NO; + } + + if (y < abox.min[1]) { + if (_dragScroll != SCROLL_DOWN) { + _dragScroll = SCROLL_DOWN; + _dragScrollTime.stamp(); + } + } else if (y > abox.max[1]) { + if (_dragScroll != SCROLL_UP) { + _dragScroll = SCROLL_UP; + _dragScrollTime.stamp(); + } + } else { + _dragScroll = SCROLL_NO; + _dragTargetRow = rowForY(y - abox.min[1] - (rowHeightPx() / 2)); + } +} + +void WaypointList::doDrop(int x, int y) +{ + _dragging = false; + puDeactivateWidget(); + + if ((y < abox.min[1]) || (y >= abox.max[1])) { + return; + } + + if (_dragSourceRow != _dragTargetRow) { + _model->moveWaypointToIndex(_dragSourceRow, _dragTargetRow); + + // keep row indexes linged up when moving an item down the list + if (_dragSourceRow < _dragTargetRow) { + --_dragTargetRow; + } + + setSelected(_dragTargetRow); + } +} + +void WaypointList::invokeDownCallback(void) +{ + _dragging = false; + _dragScroll = SCROLL_NO; + SG_LOG(SG_GENERAL, SG_INFO, "cancel drag"); +} + +int WaypointList::rowForY(int y) const +{ + if (!_model) { + return -1; + } + + // flip y to increase down, not up (as rows do) + int flipY = _heightPx - y; + int row = (flipY + _scrollPx) / rowHeightPx(); + return row; +} + +void WaypointList::draw( int dx, int dy ) +{ + puFrame::draw(dx, dy); + + if (!_model) { + return; + } + + if (_dragScroll != SCROLL_NO) { + doDragScroll(); + } + + glEnable(GL_SCISSOR_TEST); + GLint sx = (int) abox.min[0], + sy = abox.min[1]; + GLsizei w = (GLsizei) abox.max[0] - abox.min[0], + h = _heightPx; + + sx += border_thickness; + sy += border_thickness; + w -= 2 * border_thickness; + h -= 2 * border_thickness; + + glScissor(sx + dx, sy + dy, w, h); + int row = firstVisibleRow(), + final = lastVisibleRow(), + rowHeight = rowHeightPx(), + y = rowHeight; + + y -= (_scrollPx % rowHeight); // partially draw the first row + + for ( ; row <= final; ++row, y += rowHeight) { + drawRow(dx, dy, row, y); + } // of row drawing iteration + + glDisable(GL_SCISSOR_TEST); + + if (_dragging) { + // draw the insert marker after the rows + int insertY = (_dragTargetRow * rowHeight) - _scrollPx; + SG_CLAMP_RANGE(insertY, 0, std::min(_heightPx, totalHeightPx())); + + glColor4f(1.0f, 0.5f, 0.0f, 0.8f); + glLineWidth(3.0f); + glBegin(GL_LINES); + glVertex2f(dx + abox.min[0], dy + abox.max[1] - insertY); + glVertex2f(dx + abox.max[0], dy + abox.max[1] - insertY); + glEnd(); + } +} + +void WaypointList::drawRow(int dx, int dy, int rowIndex, int y) +{ + bool isSelected = (rowIndex == getSelected()); + bool isCurrent = (rowIndex == _model->currentWaypoint()); + bool isDragSource = (_dragging && (rowIndex == _dragSourceRow)); + + puBox bkgBox = abox; + bkgBox.min[1] = abox.max[1] - y; + bkgBox.max[1] = bkgBox.min[1] + rowHeightPx(); + + puColour currentColor; + puSetColor(currentColor, 1.0, 1.0, 0.0, 0.5); + + if (isDragSource) { + // draw later, on *top* of text string + } else if (isCurrent) { + bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0); + } else if (isSelected) { // -PLAIN means selected, apparently + bkgBox.draw(dx, dy, -PUSTYLE_PLAIN, colour, false, 0); + } + + int xx = dx + abox.min[0] + PUSTR_LGAP; + int yy = dy + abox.max[1] - y ; + yy += 4; // center text in row height + + // row textual data + const SGWayPoint wp(_model->waypointAt(rowIndex)); + char buffer[128]; + int count = ::snprintf(buffer, 128, "%03d %-5s", rowIndex, wp.get_id().c_str()); + + if (wp.get_name().size() > 0 && (wp.get_name() != wp.get_id())) { + // append name if present, and different to id + ::snprintf(buffer + count, 128 - count, " (%s)", wp.get_name().c_str()); + } + + glColor4fv ( colour [ PUCOL_LEGEND ] ) ; + drawClippedString(legendFont, buffer, xx, yy, 300); + + if (_showLatLon) { + char ns = (wp.get_target_lat() > 0.0) ? 'N' : 'S'; + char ew = (wp.get_target_lon() > 0.0) ? 'E' : 'W'; + + ::snprintf(buffer, 128 - count, "%4.2f%c %4.2f%c", + fabs(wp.get_target_lon()), ew, fabs(wp.get_target_lat()), ns); + } else { + ::snprintf(buffer, 128 - count, "%03.0f %5.1fnm", + wp.get_track(), wp.get_distance() * SG_METER_TO_NM); + } + + legendFont.drawString(buffer, xx + 300 + PUSTR_LGAP, yy); + + int altFt = (int) wp.get_target_alt() * SG_METER_TO_FEET; + if (altFt > -9990) { + int altHundredFt = (altFt + 50) / 100; // round to nearest 100ft + if (altHundredFt < 100) { + count = ::snprintf(buffer, 128, "%d'", altHundredFt * 100); + } else { // display as a flight-level + count = ::snprintf(buffer, 128, "FL%d", altHundredFt); + } + + legendFont.drawString(buffer, xx + 400 + PUSTR_LGAP, yy); + } // of valid wp altitude + + if (isDragSource) { + puSetColor(currentColor, 1.0, 0.5, 0.0, 0.5); + bkgBox.draw(dx, dy, PUSTYLE_PLAIN, ¤tColor, false, 0); + } +} + +const double SCROLL_PX_SEC = 200.0; + +void WaypointList::doDragScroll() +{ + double dt = (SGTimeStamp::now() - _dragScrollTime).toSecs(); + _dragScrollTime.stamp(); + int deltaPx = (int)(dt * SCROLL_PX_SEC); + + if (_dragScroll == SCROLL_UP) { + _scrollPx = _scrollPx - deltaPx; + SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx()); + _dragTargetRow = firstVisibleRow(); + } else { + _scrollPx = _scrollPx + deltaPx; + SG_CLAMP_RANGE(_scrollPx, 0, scrollRangePx()); + _dragTargetRow = lastFullyVisibleRow() + 1; + } + + if (_scrollCallback) { + (*_scrollCallback)(); + } +} + +int WaypointList::getSelected() +{ + return getIntegerValue(); +} + +void WaypointList::setSelected(int rowIndex) +{ + if (rowIndex == getSelected()) { + return; + } + + setValue(rowIndex); + invokeCallback(); + if (rowIndex == -1) { + return; + } + + ensureRowVisible(rowIndex); +} + +void WaypointList::ensureRowVisible(int rowIndex) +{ + if ((rowIndex >= firstFullyVisibleRow()) && (rowIndex <= lastFullyVisibleRow())) { + return; // already visible, fine + } + + // ideal position would place the desired row in the middle of the + // visible section - hence subtract half the visible height. + int targetScrollPx = (rowIndex * rowHeightPx()) - (_heightPx / 2); + + // clamp the scroll value to something valid + SG_CLAMP_RANGE(targetScrollPx, 0, scrollRangePx()); + _scrollPx = targetScrollPx; + + puPostRefresh(); + if (_scrollCallback) { // keep scroll observers in sync + (*_scrollCallback)(); + } +} + +unsigned int WaypointList::numWaypoints() const +{ + if (!_model) { + return 0; + } + + return _model->numWaypoints(); +} + +bool WaypointList::wantsVScroll() const +{ + return totalHeightPx() > _heightPx; +} + +float WaypointList::getVScrollPercent() const +{ + float scrollRange = scrollRangePx(); + if (scrollRange < 1.0f) { + return 0.0; + } + + return _scrollPx / scrollRange; +} + +float WaypointList::getVScrollThumbPercent() const +{ + return _heightPx / (float) totalHeightPx(); +} + +void WaypointList::setVScrollPercent(float perc) +{ + float scrollRange = scrollRangePx(); + _scrollPx = (int)(scrollRange * perc); +} + +int WaypointList::firstVisibleRow() const +{ + return _scrollPx / rowHeightPx(); +} + +int WaypointList::firstFullyVisibleRow() const +{ + int rh = rowHeightPx(); + return (_scrollPx + rh - 1) / rh; +} + +int WaypointList::numVisibleRows() const +{ + int rh = rowHeightPx(); + int topOffset = _scrollPx % rh; // pixels of first visible row + return (_heightPx - topOffset + rh - 1) / rh; + +} + +int WaypointList::numFullyVisibleRows() const +{ + int rh = rowHeightPx(); + int topOffset = _scrollPx % rh; // pixels of first visible row + return (_heightPx - topOffset) / rh; +} + +int WaypointList::rowHeightPx() const +{ + return legendFont.getStringHeight() + PUSTR_BGAP; +} + +int WaypointList::scrollRangePx() const +{ + return std::max(0, totalHeightPx() - _heightPx); +} + +int WaypointList::totalHeightPx() const +{ + if (!_model) { + return 0; + } + + return (int) _model->numWaypoints() * rowHeightPx(); +} + +int WaypointList::lastFullyVisibleRow() const +{ + int row = firstFullyVisibleRow() + numFullyVisibleRows(); + return std::min(row, (int) _model->numWaypoints() - 1); +} + +int WaypointList::lastVisibleRow() const +{ + int row = firstVisibleRow() + numVisibleRows(); + return std::min(row, (int) _model->numWaypoints() - 1); +} + +void WaypointList::setModel(Model* model) +{ + if (_model) { + delete _model; + } + + _model = model; + _model->setUpdateCallback(make_callback(this, &WaypointList::modelUpdateCallback)); + + puPostRefresh(); +} + +int WaypointList::checkKey (int key, int updown ) +{ + if ((updown == PU_UP) || !isVisible () || !isActive () || (window != puGetWindow())) { + return FALSE ; + } + + switch (key) + { + case PU_KEY_HOME: + setSelected(0); + break; + + case PU_KEY_END: + setSelected(_model->numWaypoints() - 1); + break ; + + case PU_KEY_UP : + case PU_KEY_PAGE_UP : + if (getSelected() >= 0) { + setSelected(getSelected() - 1); + } + break ; + + case PU_KEY_DOWN : + case PU_KEY_PAGE_DOWN : { + int newSel = getSelected() + 1; + if (newSel >= (int) _model->numWaypoints()) { + setSelected(-1); + } else { + setSelected(newSel); + } + break ; + } + + case '-': + if (getSelected() >= 0) { + int newAlt = wayptAltFtHundreds(getSelected()) - 10; + if (newAlt < 0) { + _model->setWaypointTargetAltitudeFt(getSelected(), -9999); + } else { + _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100); + } + } + break; + + case '=': + if (getSelected() >= 0) { + int newAlt = wayptAltFtHundreds(getSelected()) + 10; + if (newAlt < 0) { + _model->setWaypointTargetAltitudeFt(getSelected(), 0); + } else { + _model->setWaypointTargetAltitudeFt(getSelected(), newAlt * 100); + } + } + break; + + case 0x7f: // delete + if (getSelected() >= 0) { + int index = getSelected(); + _model->deleteAt(index); + setSelected(index - 1); + } + break; + + default : + return FALSE; + } + + return TRUE ; +} + +int WaypointList::wayptAltFtHundreds(int index) const +{ + double alt = _model->waypointAt(index).get_target_alt(); + if (alt < -9990.0) { + return -9999; + } + + int altFt = (int) alt * SG_METER_TO_FEET; + return (altFt + 50) / 100; // round to nearest 100ft +} + +void WaypointList::modelUpdateCallback() +{ + // local stuff + + if (_updateCallback) { + (*_updateCallback)(); + } +} + +////////////////////////////////////////////////////////////////////////////// + + +static void handle_scrollbar(puObject* scrollbar) +{ + ScrolledWaypointList* self = (ScrolledWaypointList*)scrollbar->getUserData(); + self->setScrollPercent(scrollbar->getFloatValue()); +} + +static void waypointListCb(puObject* wpl) +{ + ScrolledWaypointList* self = (ScrolledWaypointList*)wpl->getUserData(); + self->setValue(wpl->getIntegerValue()); + self->invokeCallback(); +} + +ScrolledWaypointList::ScrolledWaypointList(int x, int y, int width, int height) : + puGroup(x,y), + _scrollWidth(16) +{ + // ensure our type is compound, so fgPopup::applySize doesn't descend into + // us, and try to cast our children's user-data to GUIInfo*. + type |= PUCLASS_LIST; + + init(width, height); +} + +void ScrolledWaypointList::setValue(float v) +{ + puGroup::setValue(v); + _list->setValue(v); +} + +void ScrolledWaypointList::setValue(int v) +{ + puGroup::setValue(v); + _list->setValue(v); +} + +void ScrolledWaypointList::init(int w, int h) +{ + _list = new WaypointList(0, 0, w, h); + _list->setUpdateCallback(make_callback(this, &ScrolledWaypointList::modelUpdated)); + _hasVScroll = _list->wantsVScroll(); + _list->setUserData(this); + _list->setCallback(waypointListCb); + + _list->setScrollCallback(make_callback(this, &ScrolledWaypointList::updateScroll)); + + _scrollbar = new puaScrollBar(w - _scrollWidth, 0, h, + 1 /*arrow*/, 1 /* vertical */, _scrollWidth); + _scrollbar->setMinValue(0.0); + _scrollbar->setMaxValue(1.0); + _scrollbar->setUserData(this); + _scrollbar->setCallback(handle_scrollbar); + close(); // close the group + + setSize(w, h); +} + +void ScrolledWaypointList::modelUpdated() +{ + int w, h; + getSize(&w, &h); + updateWantsScroll(w, h); +} + +void ScrolledWaypointList::setScrollPercent(float v) +{ + // slider's min is the bottom, so invert the value + _list->setVScrollPercent(1.0f - v); +} + +void ScrolledWaypointList::setSize(int w, int h) +{ + updateWantsScroll(w, h); +} + +void ScrolledWaypointList::updateWantsScroll(int w, int h) +{ + _hasVScroll = _list->wantsVScroll(); + + if (_hasVScroll) { + _scrollbar->reveal(); + _scrollbar->setPosition(w - _scrollWidth, 0); + _scrollbar->setSize(_scrollWidth, h); + _list->setSize(w - _scrollWidth, h); + updateScroll(); + } else { + _scrollbar->hide(); + _list->setSize(w, h); + } +} + +void ScrolledWaypointList::updateScroll() +{ + // _scrollbar->setMaxValue(_list->numWaypoints()); + _scrollbar->setValue(1.0f - _list->getVScrollPercent()); + _scrollbar->setSliderFraction(_list->getVScrollThumbPercent()); +} + diff --git a/src/GUI/WaypointList.hxx b/src/GUI/WaypointList.hxx new file mode 100644 index 000000000..165a1dad3 --- /dev/null +++ b/src/GUI/WaypointList.hxx @@ -0,0 +1,171 @@ +/** + * WaypointList.hxx - scrolling list of waypoints, with special formatting + */ + +#ifndef GUI_WAYPOINT_LIST_HXX +#define GUI_WAYPOINT_LIST_HXX + +#include +#include + +#include + +#include "dialog.hxx" // for GUI_ID + +// forward decls +class puaScrollBar; +class SGWayPoint; +class SGCallback; + +class WaypointList : public puFrame, public GUI_ID +{ +public: + WaypointList(int x, int y, int width, int height); + virtual ~WaypointList(); + + virtual void setSize(int width, int height); + virtual int checkHit ( int button, int updown, int x, int y); + virtual void doHit( int button, int updown, int x, int y ) ; + virtual void draw( int dx, int dy ) ; + virtual int checkKey(int key, int updown); + virtual void invokeDownCallback (void); + + void setSelected(int rowIndex); + int getSelected(); + + /** + * Do we want a vertical scrollbar (or similar) + */ + bool wantsVScroll() const; + + /** + * Get scrollbar position as a percentage of total range. + * returns negative number if scrolling is not possible + */ + float getVScrollPercent() const; + + /** + * + */ + void setVScrollPercent(float perc); + + /** + * Get required thumb size as percentage of total height + */ + float getVScrollThumbPercent() const; + + int numVisibleRows() const; + + void ensureRowVisible(int row); + + void setUpdateCallback(SGCallback* cb); + void setScrollCallback(SGCallback* cb); + + /** + * Abstract interface for waypoint source + */ + class Model + { + public: + virtual ~Model() { } + + virtual unsigned int numWaypoints() const = 0; + virtual int currentWaypoint() const = 0; + virtual SGWayPoint waypointAt(unsigned int index) const = 0; + + // update notifications + virtual void setUpdateCallback(SGCallback* cb) = 0; + + // editing operations + virtual void deleteAt(unsigned int index) = 0; + virtual void setWaypointTargetAltitudeFt(unsigned int index, int altFt) = 0; + virtual void moveWaypointToIndex(unsigned int srcIndex, unsigned int dstIndex) = 0; + }; + + void setModel(Model* model); + + unsigned int numWaypoints() const; +protected: + +private: + void drawRow(int dx, int dy, int rowIndex, int yOrigin); + + void handleDrag(int x, int y); + void doDrop(int x, int y); + void doDragScroll(); + + /** + * Pixel height of a row, including padding + */ + int rowHeightPx() const; + + /** + * model row corresponding to an on-screen y-value + */ + int rowForY(int y) const; + + /** + * reutrn rowheight * total number of rows, i.e the height we'd + * need to be to show every row without scrolling + */ + int totalHeightPx() const; + + /** + * Pixel scroll range, based on widget height and number of rows + */ + int scrollRangePx() const; + + int firstVisibleRow() const; + int lastVisibleRow() const; + + int numFullyVisibleRows() const; + int firstFullyVisibleRow() const; + int lastFullyVisibleRow() const; + + int wayptAltFtHundreds(int index) const; + + void modelUpdateCallback(); + + int _scrollPx; // scroll ammount (in pixels) + int _heightPx; + + bool _dragging; + int _dragSourceRow; + int _dragTargetRow; + int _mouseDownX, _mouseDownY; + + int _dragScroll; + SGTimeStamp _dragScrollTime; + + bool _showLatLon; + Model* _model; + SGCallback* _updateCallback; + SGCallback* _scrollCallback; +}; + +class ScrolledWaypointList : public puGroup +{ +public: + ScrolledWaypointList(int x, int y, int width, int height); + + virtual void setSize(int width, int height); + + void setScrollPercent(float v); + + virtual void setValue(float v); + virtual void setValue(int v); +private: + void init(int w, int h); + + void updateScroll(); + void updateWantsScroll(int w, int h); + + void modelUpdated(); + + puaScrollBar* _scrollbar; + WaypointList* _list; + int _scrollWidth; + bool _hasVScroll; +}; + +#endif // GUI_WAYPOINT_LIST_HXX diff --git a/src/GUI/dialog.cxx b/src/GUI/dialog.cxx index 95fe5f04c..fcc0f5a97 100644 --- a/src/GUI/dialog.cxx +++ b/src/GUI/dialog.cxx @@ -13,7 +13,7 @@ #include "AirportList.hxx" #include "property_list.hxx" #include "layout.hxx" - +#include "WaypointList.hxx" enum format_type { f_INVALID, f_INT, f_LONG, f_FLOAT, f_DOUBLE, f_STRING }; static const int FORMAT_BUFSIZE = 255; @@ -561,9 +561,14 @@ FGDialog::updateValues (const char *objectName) puObject *widget = _propertyObjects[i]->object; int widgetType = widget->getType(); - if ((widgetType & PUCLASS_LIST) && (dynamic_cast(widget)->id & FGCLASS_LIST)) { - fgList *pl = static_cast(widget); - pl->update(); + if (widgetType & PUCLASS_LIST) { + GUI_ID* gui_id = dynamic_cast(widget); + if (gui_id && (gui_id->id & FGCLASS_LIST)) { + fgList *pl = static_cast(widget); + pl->update(); + } else { + copy_to_pui(_propertyObjects[i]->node, widget); + } } else if (widgetType & PUCLASS_COMBOBOX) { fgComboBox* combo = static_cast(widget); combo->update(); @@ -865,6 +870,10 @@ FGDialog::makeObject (SGPropertyNode *props, int parentWidth, int parentHeight) setupObject(obj, props); setColor(obj, props, EDITFIELD); return obj; + } else if (type == "waypointlist") { + ScrolledWaypointList* obj = new ScrolledWaypointList(x, y, width, height); + setupObject(obj, props); + return obj; } else { return 0; } diff --git a/src/GUI/dialog.hxx b/src/GUI/dialog.hxx index 63aef704b..e08dbe75a 100644 --- a/src/GUI/dialog.hxx +++ b/src/GUI/dialog.hxx @@ -22,6 +22,8 @@ using std::vector; #define FGCLASS_LIST 0x00000001 #define FGCLASS_AIRPORTLIST 0x00000002 #define FGCLASS_PROPERTYLIST 0x00000004 +#define FGCLASS_WAYPOINTLIST 0x00000008 + class GUI_ID { public: GUI_ID(int id) : id(id) {} virtual ~GUI_ID() {} int id; }; From 247219d1f1ac08f9044b819676997b8cfc23fdb9 Mon Sep 17 00:00:00 2001 From: Tim Moore Date: Mon, 22 Feb 2010 09:32:32 +0100 Subject: [PATCH 23/29] remove trailing slash / empty line from Makefile.am --- src/GUI/Makefile.am | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GUI/Makefile.am b/src/GUI/Makefile.am index d0ede62b6..dbf432f97 100644 --- a/src/GUI/Makefile.am +++ b/src/GUI/Makefile.am @@ -23,8 +23,8 @@ libGUI_a_SOURCES = \ property_list.cxx property_list.hxx \ layout.cxx layout-props.cxx layout.hxx \ SafeTexFont.cxx SafeTexFont.hxx \ - WaypointList.cxx WaypointList.hxx \ - + WaypointList.cxx WaypointList.hxx + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src From f0e72608a744d8b7cdebb6a65ba2db8ed5ac073b Mon Sep 17 00:00:00 2001 From: jmt Date: Tue, 23 Feb 2010 00:17:53 +0000 Subject: [PATCH 24/29] waypointList: stop using GNU extension / C99 features. --- src/GUI/WaypointList.cxx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/GUI/WaypointList.cxx b/src/GUI/WaypointList.cxx index 0ad02a30d..5498076e5 100644 --- a/src/GUI/WaypointList.cxx +++ b/src/GUI/WaypointList.cxx @@ -108,15 +108,14 @@ static void drawClippedString(puFont& font, const char* s, int x, int y, int max return; } - int len = strlen(s); - char buf[len]; - memcpy(buf, s, len); + std::string buf(s); + int len = buf.size(); do { - buf[--len] = 0; - fullWidth = font.getStringWidth(buf); + buf.resize(--len); + fullWidth = font.getStringWidth(buf.c_str()); } while (fullWidth > maxWidth); - font.drawString(buf, x, y); + font.drawString(buf.c_str(), x, y); } ////////////////////////////////////////////////////////////////////////////// From 942c208636491147104729fe59a3528cdcd5695b Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 25 Feb 2010 14:33:56 +0000 Subject: [PATCH 25/29] Make sure we check for the proper simgear version. --- configure.ac | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/configure.ac b/configure.ac index fda72f9fc..b2a2b242e 100644 --- a/configure.ac +++ b/configure.ac @@ -500,7 +500,7 @@ if test "x$ac_cv_header_simgear_version_h" != "xyes"; then exit fi -AC_MSG_CHECKING([for SimGear 1.9.0 or newer]) +AC_MSG_CHECKING([for SimGear 2.0.0 or newer]) AC_TRY_RUN([ #include @@ -509,8 +509,8 @@ AC_TRY_RUN([ #define STRINGIFY(X) XSTRINGIFY(X) #define XSTRINGIFY(X) #X -#define MIN_MAJOR 1 -#define MIN_MINOR 9 +#define MIN_MAJOR 2 +#define MIN_MINOR 0 #define MIN_MICRO 0 int main() { From 0d0a25a00bcfaed54b754334a609c9870a2cd486 Mon Sep 17 00:00:00 2001 From: curt Date: Thu, 25 Feb 2010 14:48:08 +0000 Subject: [PATCH 26/29] Update the NEWS file to reflect v2.0.0 changes. --- NEWS | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 0876a8d1d..6590aeb49 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,80 @@ -Version 1.99.5 -* October 30, 2008 (source code snapshot release) +Version 2.0.0 - February 25, 2010 + + FlightGear 2.0.0. reflects the maturation of the OpenSceneGraph port + that started with the previous 1.9.0 release. In addition to many + internal code improvements, FlightGear 2.0.0. marks the introduction + of many new exciting improvements in the graphics and sound system, + as well as improved usability of key features, and improved behavior + of exsisting features. Highlights of this new version include: + Dramatic new 3D clouds, dramatic lighting conditions, improved + support for custom scenery, and many many new and detailed aircraft + models. + + Sound + * Complete overhaul of the sound code + * doppler effects + * distance attenuation + * 3D positional sound sources + * assignment of sound sources to external objects (i.e. AI controlled + aircraft) + * User selection of the sound device + + Visual Effects + * Use of Shaders for dynamic textures + * Use of Effects files + * Improved 3D clouds + * Color changes based on humidity and other weather effects allow for very + dramatic lighting conditions + * Dynamic water textures + * Text animation based on OSGText + + Usability + * Allow screenshots in more common file formats + * User selectable sound device + * More intuitive selection of the weather settings through the GUI and/or + commandline + + Infrastructure + * Airport geometry data can be read from the scenery, allowing for more + flexible regeneration of terrain tiles + + Internals + * Improved efficiency of the property tree + * A more efficient ground cache + * Many improvements to the route management code + * Removed many compiler warnings + * More realistic atmosphere model + + Behavior + * More realistic ILS behavior + * Autopilot improvements + * A generic autobrake function + * Winds over mountainous areas cause up- and downdrafts that can be used + for gliding + * More realistic behavior of the route manager + * Wild fires, which can be extinguished by firefighter aircraft operating + across the multplayer server + * Navaid frequencies and radials can be transmitted to Atlas + + Utilities + * A python script to visualize Yasim configuration files in Blender + + AI + * Allow traffic departing and arriving at the same airport + * Add Ground Vehicles - including automobiles, trucks, articulated trucks, + trains (including high speed trains) + * ATC interactions between AI aircraft and ground controllers + * Performance characteristics of AI aircraft can be specified in a + performance database + * Push-back vehicles are available for a selected number of aircraft + * Add escorts for AI carrier - frigates, guided missile cruiser, amphibious + warfare ships now make up the Vinson Battle Group + * Improved radar functionality - now detects AI escorts etc. + * AI objects are now solid (i.e. users can collide with them) + * Some preliminary support for SID/STAR procedures for AI aircraft + +Version 1.9.0 +* December 20, 2008 (source code snapshot release) New in 0.9.10 From f6ba7c9c94d65ffab1716b1a3fbd4270252bb745 Mon Sep 17 00:00:00 2001 From: jmt Date: Sun, 21 Feb 2010 21:05:16 +0000 Subject: [PATCH 27/29] MSVC project updates: add GUI/WaypointList[.cxx|.hxx] to build. --- projects/VC7.1/FlightGear.vcproj | 2 ++ projects/VC90/FlightGear/FlightGear.vcproj | 2 ++ 2 files changed, 4 insertions(+) diff --git a/projects/VC7.1/FlightGear.vcproj b/projects/VC7.1/FlightGear.vcproj index 9d0a96735..388947077 100755 --- a/projects/VC7.1/FlightGear.vcproj +++ b/projects/VC7.1/FlightGear.vcproj @@ -1826,6 +1826,8 @@ + + + + Date: Sun, 21 Feb 2010 21:56:37 +0000 Subject: [PATCH 28/29] Make automake happier. --- src/GUI/Makefile.am | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/GUI/Makefile.am b/src/GUI/Makefile.am index dbf432f97..5f8c98d0b 100644 --- a/src/GUI/Makefile.am +++ b/src/GUI/Makefile.am @@ -24,8 +24,7 @@ libGUI_a_SOURCES = \ layout.cxx layout-props.cxx layout.hxx \ SafeTexFont.cxx SafeTexFont.hxx \ WaypointList.cxx WaypointList.hxx - - + INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src layout_test_SOURCES = layout-test.cxx From 89023eda69a1ea731808b4bc083b7e2b30b473d9 Mon Sep 17 00:00:00 2001 From: "Curtis L. Olson" Date: Thu, 25 Feb 2010 08:35:33 -0600 Subject: [PATCH 29/29] Fix missed items for the 2.0.0 release. --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 15ba35f43..c86f1c148 100644 --- a/Makefile.am +++ b/Makefile.am @@ -52,7 +52,7 @@ data-tar: --exclude='*/Docs/source' \ --exclude='*/Models/MNUAV' \ --exclude='*/Models/Airspace' \ - -cjvf source/FlightGear-data-$(VERSION).tar.bz2 \ + -cjvf FlightGear-data-$(VERSION).tar.bz2 \ data/AI \ data/Aircraft/Generic \ data/Aircraft/Instruments \