]> git.mxchange.org Git - flightgear.git/blobdiff - src/Autopilot/xmlauto.cxx
Autopilot: clean up the helpers code (which drives the various /internal/) properties...
[flightgear.git] / src / Autopilot / xmlauto.cxx
index 6d542855787ee720bc80f7a92336bdac8d13e37a..ff149a99c0c04daba89b25662ae54088bee08f9d 100644 (file)
 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 <min> and/or <max> 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<double>(p1,p2);
+  double max = std::max<double>(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;
@@ -69,7 +98,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 ) {
@@ -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;
 }
@@ -200,7 +237,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 );
@@ -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 );
@@ -793,47 +837,99 @@ void FGDigitalFilter::update(double dt)
     }
 }
 
+FGXMLAutopilotGroup::FGXMLAutopilotGroup()
+{
+}
 
-FGXMLAutopilot::FGXMLAutopilot() {
+void FGXMLAutopilotGroup::reinit()
+{
+    for( vector<string>::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<SGPropertyNode_ptr> 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;
+    }
 
-FGXMLAutopilot::~FGXMLAutopilot() {
-}
+    for( vector<SGPropertyNode_ptr>::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;
+        }
 
-void FGXMLAutopilot::init() {
-    config_props = fgGetNode( "/autopilot/new-config", true );
+        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();
+        }
 
-    SGPropertyNode *path_n = fgGetNode("/sim/systems/autopilot/path");
+        if( get_subsystem( apName.c_str() ) != NULL ) {
+            SG_LOG( SG_ALL, SG_ALERT, "Duplicate autopilot configuration name " << apName << " ignored" );
+            continue;
+        }
 
-    if ( path_n ) {
         SGPath config( globals->get_fg_root() );
-        config.append( path_n->getStringValue() );
+        config.append( pathNode->getStringValue() );
 
-        SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from "
-                << config.str() );
+        SG_LOG( SG_ALL, SG_INFO, "Reading autopilot configuration from " << config.str() );
+        // FGXMLAutopilot
+        FGXMLAutopilot * ap = new FGXMLAutopilot;
         try {
-            readProperties( config.str(), config_props );
+            SGPropertyNode_ptr root = new SGPropertyNode();
+            readProperties( config.str(), root );
 
-            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" );
+
+            if ( ! ap->build( root ) ) {
                 SG_LOG( SG_ALL, SG_ALERT,
-                        " details.");
-                exit(-1);
+                  "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;
         }
 
-    } else {
-        SG_LOG( SG_ALL, SG_WARN,
-                "No autopilot configuration specified for this model!");
+        SG_LOG( SG_ALL, SG_INFO, "adding  autopilot subsystem " << apName );
+        set_subsystem( apName, ap );
+        _autopilotNames.push_back( apName );
     }
+
+    SGSubsystemGroup::init();
+}
+
+FGXMLAutopilot::FGXMLAutopilot() {
+}
+
+
+FGXMLAutopilot::~FGXMLAutopilot() {
+}
+
+/* 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() 
+{
+  latNode = fgGetNode("/position/latitude-deg");
+  lonNode = fgGetNode("/position/longitude-deg");
 }
 
 
@@ -849,7 +945,7 @@ void FGXMLAutopilot::bind() {
 void FGXMLAutopilot::unbind() {
 }
 
-bool FGXMLAutopilot::build() {
+bool FGXMLAutopilot::build( SGPropertyNode_ptr config_props ) {
     SGPropertyNode *node;
     int i;
 
@@ -858,6 +954,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" ) {
@@ -866,10 +963,10 @@ bool FGXMLAutopilot::build() {
             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;
         }
     }
 
@@ -880,7 +977,7 @@ bool FGXMLAutopilot::build() {
 /*
  * 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
@@ -907,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
+    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() - ind_hdg->getDoubleValue();
-    if ( diff < -180.0 ) { diff += 360.0; }
-    if ( diff > 180.0 ) { diff -= 360.0; }
-    ind_bug_error->setDoubleValue( diff );
+    double diff = bug->getDoubleValue() - mag_hdg->getDoubleValue();
+    SG_NORMALIZE_RANGE(diff, -180.0, 180.0);
+    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 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
@@ -940,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
@@ -959,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 );
 
@@ -993,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;
     }
-
 }