]> git.mxchange.org Git - flightgear.git/blobdiff - src/Autopilot/xmlauto.cxx
James Turner:
[flightgear.git] / src / Autopilot / xmlauto.cxx
index 217d0e1a8d4a60641afb7cab027fe47ef149e075..17ea68cbaf949a4a7ee28f4f1488fea24a084a1d 100644 (file)
@@ -2,7 +2,7 @@
 //
 // Written by Curtis Olson, started January 2004.
 //
-// Copyright (C) 2004  Curtis L. Olson  - curt@flightgear.org
+// Copyright (C) 2004  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
 //
 // 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //
 // $Id$
 
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <iostream>
 
 #include <simgear/structure/exception.hxx>
 #include <simgear/misc/sg_path.hxx>
+#include <simgear/sg_inlines.h>
+#include <simgear/props/props_io.hxx>
 
 #include <Main/fg_props.hxx>
 #include <Main/globals.hxx>
+#include <Main/util.hxx>
 
 #include "xmlauto.hxx"
 
+using std::cout;
+using std::endl;
 
 FGPIDController::FGPIDController( SGPropertyNode *node ):
     debug( false ),
     y_n( 0.0 ),
     r_n( 0.0 ),
+    y_scale( 1.0 ),
+    r_scale( 1.0 ),
+    y_offset( 0.0 ),
+    r_offset( 0.0 ),
     Kp( 0.0 ),
     alpha( 0.1 ),
     beta( 1.0 ),
@@ -46,8 +60,8 @@ FGPIDController::FGPIDController( SGPropertyNode *node ):
     edf_n_1( 0.0 ),
     edf_n_2( 0.0 ),
     u_n_1( 0.0 ),
-    r_scale( 1.0 ),
-    y_scale( 1.0 )
+    desiredTs( 0.0 ),
+    elapsedTime( 0.0 )
 {
     int i;
     for ( i = 0; i < node->nChildren(); ++i ) {
@@ -71,6 +85,10 @@ FGPIDController::FGPIDController( SGPropertyNode *node ):
             if ( val != NULL ) {
                 enable_value = val->getStringValue();
             }
+            SGPropertyNode *pass = child->getChild( "honor-passive" );
+            if ( pass != NULL ) {
+                honor_passive = pass->getBoolValue();
+            }
         } else if ( cname == "input" ) {
             SGPropertyNode *prop = child->getChild( "prop" );
             if ( prop != NULL ) {
@@ -80,6 +98,10 @@ FGPIDController::FGPIDController( SGPropertyNode *node ):
             if ( prop != NULL ) {
                 y_scale = prop->getDoubleValue();
             }
+            prop = child->getChild( "offset" );
+            if ( prop != NULL ) {
+                y_offset = prop->getDoubleValue();
+            }
         } else if ( cname == "reference" ) {
             SGPropertyNode *prop = child->getChild( "prop" );
             if ( prop != NULL ) {
@@ -87,13 +109,17 @@ FGPIDController::FGPIDController( SGPropertyNode *node ):
             } else {
                 prop = child->getChild( "value" );
                 if ( prop != NULL ) {
-                    r_n = prop->getDoubleValue();
+                    r_n_value = prop->getDoubleValue();
                 }
             }
             prop = child->getChild( "scale" );
             if ( prop != NULL ) {
                 r_scale = prop->getDoubleValue();
             }
+            prop = child->getChild( "offset" );
+            if ( prop != NULL ) {
+                r_offset = prop->getDoubleValue();
+            }
         } else if ( cname == "output" ) {
             int i = 0;
             SGPropertyNode *prop;
@@ -103,46 +129,151 @@ FGPIDController::FGPIDController( SGPropertyNode *node ):
                 i++;
             }
         } else if ( cname == "config" ) {
-            SGPropertyNode *prop;
+            SGPropertyNode *config;
 
-            prop = child->getChild( "Kp" );
-            if ( prop != NULL ) {
-                Kp = prop->getDoubleValue();
+            config = child->getChild( "Ts" );
+            if ( config != NULL ) {
+                desiredTs = config->getDoubleValue();
             }
+            
+            config = child->getChild( "Kp" );
+            if ( config != NULL ) {
+                SGPropertyNode *val = config->getChild( "value" );
+                if ( val != NULL ) {
+                    Kp = val->getDoubleValue();
+                }
 
-            prop = child->getChild( "beta" );
-            if ( prop != NULL ) {
-                beta = prop->getDoubleValue();
+                SGPropertyNode *prop = config->getChild( "prop" );
+                if ( prop != NULL ) {
+                    Kp_prop = fgGetNode( prop->getStringValue(), true );
+                    if ( val != NULL ) {
+                        Kp_prop->setDoubleValue(Kp);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop == NULL) {
+                    Kp = config->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
+                    if ( name.length() ) {
+                        SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
+                    }
+                }
             }
 
-            prop = child->getChild( "alpha" );
-            if ( prop != NULL ) {
-                alpha = prop->getDoubleValue();
+            config = child->getChild( "beta" );
+            if ( config != NULL ) {
+                beta = config->getDoubleValue();
             }
 
-            prop = child->getChild( "gamma" );
-            if ( prop != NULL ) {
-                gamma = prop->getDoubleValue();
+            config = child->getChild( "alpha" );
+            if ( config != NULL ) {
+                alpha = config->getDoubleValue();
             }
 
-            prop = child->getChild( "Ti" );
-            if ( prop != NULL ) {
-                Ti = prop->getDoubleValue();
+            config = child->getChild( "gamma" );
+            if ( config != NULL ) {
+                gamma = config->getDoubleValue();
             }
 
-            prop = child->getChild( "Td" );
-            if ( prop != NULL ) {
-                Td = prop->getDoubleValue();
+            config = child->getChild( "Ti" );
+            if ( config != NULL ) {
+                SGPropertyNode *val = config->getChild( "value" );
+                if ( val != NULL ) {
+                    Ti = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop = config->getChild( "prop" );
+                if ( prop != NULL ) {
+                    Ti_prop = fgGetNode( prop->getStringValue(), true );
+                    if ( val != NULL ) {
+                        Ti_prop->setDoubleValue(Kp);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop == NULL) {
+                Ti = config->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Ti config. Please use <prop> and/or <value> tags." );
+                    if ( name.length() ) {
+                        SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
+                    }
+                }
             }
 
-            prop = child->getChild( "u_min" );
-            if ( prop != NULL ) {
-                u_min = prop->getDoubleValue();
+            config = child->getChild( "Td" );
+            if ( config != NULL ) {
+                SGPropertyNode *val = config->getChild( "value" );
+                if ( val != NULL ) {
+                    Td = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop = config->getChild( "prop" );
+                if ( prop != NULL ) {
+                    Td_prop = fgGetNode( prop->getStringValue(), true );
+                    if ( val != NULL ) {
+                        Td_prop->setDoubleValue(Kp);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop == NULL) {
+                Td = config->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Td config. Please use <prop> and/or <value> tags." );
+                    if ( name.length() ) {
+                        SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
+                    }
+                }
             }
 
-            prop = child->getChild( "u_max" );
-            if ( prop != NULL ) {
-                u_max = prop->getDoubleValue();
+            config = child->getChild( "u_min" );
+            if ( config != NULL ) {
+                SGPropertyNode *val = config->getChild( "value" );
+                if ( val != NULL ) {
+                    u_min = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop = config->getChild( "prop" );
+                if ( prop != NULL ) {
+                    umin_prop = fgGetNode( prop->getStringValue(), true );
+                    if ( val != NULL ) {
+                        umin_prop->setDoubleValue(u_min);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop == NULL) {
+                u_min = config->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
+                    if ( name.length() ) {
+                        SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
+                    }
+                }
+            }
+
+            config = child->getChild( "u_max" );
+            if ( config != NULL ) {
+                SGPropertyNode *val = config->getChild( "value" );
+                if ( val != NULL ) {
+                    u_max = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop = config->getChild( "prop" );
+                if ( prop != NULL ) {
+                    umax_prop = fgGetNode( prop->getStringValue(), true );
+                    if ( val != NULL ) {
+                        umax_prop->setDoubleValue(u_max);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop == NULL) {
+                u_max = config->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
+                    if ( name.length() ) {
+                        SG_LOG( SG_AUTOPILOT, SG_WARN, "Section = " << name );
+                    }
+                }
             }
         } else {
             SG_LOG( SG_AUTOPILOT, SG_WARN, "Error in autopilot config logic" );
@@ -213,13 +344,20 @@ void FGPIDController::update( double dt ) {
     double Tf;              // filter time
     double delta_u_n = 0.0; // incremental output
     double u_n = 0.0;       // absolute output
-    double Ts = dt;         // Sampling interval (sec)
-
-    if ( Ts <= 0.0 ) {
+    double Ts;              // sampling interval (sec)
+    if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
+    if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
+    if (Ti_prop != NULL)Ti = Ti_prop->getDoubleValue();
+    if (Td_prop != NULL)Td = Td_prop->getDoubleValue();
+    
+    elapsedTime += dt;
+    if ( elapsedTime <= desiredTs ) {
         // do nothing if time step is not positive (i.e. no time has
         // elapsed)
         return;
     }
+    Ts = elapsedTime;
+    elapsedTime = 0.0;
 
     if (enable_prop != NULL && enable_prop->getStringValue() == enable_value) {
         if ( !enabled ) {
@@ -237,16 +375,17 @@ void FGPIDController::update( double dt ) {
     }
 
     if ( enabled && Ts > 0.0) {
-        if ( debug ) cout << "Updating " << name << endl;
+        if ( debug ) cout << "Updating " << name
+                          << " Ts " << Ts << endl;
 
         double y_n = 0.0;
         if ( input_prop != NULL ) {
-            y_n = input_prop->getDoubleValue() * y_scale;
+            y_n = input_prop->getDoubleValue() * y_scale + y_offset;
         }
 
         double r_n = 0.0;
         if ( r_n_prop != NULL ) {
-            r_n = r_n_prop->getDoubleValue() * r_scale;
+            r_n = r_n_prop->getDoubleValue() * r_scale + r_offset;
         } else {
             r_n = r_n_value;
         }
@@ -281,12 +420,12 @@ void FGPIDController::update( double dt ) {
 
         // Calculates the incremental output:
         if ( Ti > 0.0 ) {
+            if (Kp_prop != NULL) {
+                Kp = Kp_prop->getDoubleValue();
+            }
             delta_u_n = Kp * ( (ep_n - ep_n_1)
                                + ((Ts/Ti) * e_n)
                                + ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
-        } else if ( Ti <= 0.0 ) {
-            delta_u_n = Kp * ( (ep_n - ep_n_1)
-                               + ((Td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2)) );
         }
 
         if ( debug ) {
@@ -299,10 +438,10 @@ void FGPIDController::update( double dt ) {
 
         // Integrator anti-windup logic:
         if ( delta_u_n > (u_max - u_n_1) ) {
-            delta_u_n = 0;
+            delta_u_n = u_max - u_n_1;
             if ( debug ) cout << " max saturation " << endl;
         } else if ( delta_u_n < (u_min - u_n_1) ) {
-            delta_u_n = 0;
+            delta_u_n = u_min - u_n_1;
             if ( debug ) cout << " min saturation " << endl;
         }
 
@@ -316,9 +455,18 @@ void FGPIDController::update( double dt ) {
         edf_n_2 = edf_n_1;
         edf_n_1 = edf_n;
 
-        unsigned int i;
-        for ( i = 0; i < output_list.size(); ++i ) {
-            output_list[i]->setDoubleValue( u_n );
+        // passive_ignore == true means that we go through all the
+        // motions, but drive the outputs.  This is analogous to
+        // running the autopilot with the "servos" off.  This is
+        // helpful for things like flight directors which position
+        // their vbars from the autopilot computations.
+        if ( passive_mode->getBoolValue() && honor_passive ) {
+            // skip output step
+        } else {
+            unsigned int i;
+            for ( i = 0; i < output_list.size(); ++i ) {
+                output_list[i]->setDoubleValue( u_n );
+            }
         }
     } else if ( !enabled ) {
         ep_n  = 0.0;
@@ -344,10 +492,10 @@ FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
     debug( false ),
     y_n( 0.0 ),
     r_n( 0.0 ),
-    u_min( 0.0 ),
-    u_max( 0.0 ),
     y_scale( 1.0 ),
-    r_scale ( 1.0 )
+    r_scale ( 1.0 ),
+    u_min( 0.0 ),
+    u_max( 0.0 )
 {
     int i;
     for ( i = 0; i < node->nChildren(); ++i ) {
@@ -387,7 +535,7 @@ FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
             } else {
                 prop = child->getChild( "value" );
                 if ( prop != NULL ) {
-                    r_n = prop->getDoubleValue();
+                    r_n_value = prop->getDoubleValue();
                 }
             }
             prop = child->getChild( "scale" );
@@ -407,7 +555,24 @@ FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
 
             prop = child->getChild( "Kp" );
             if ( prop != NULL ) {
+                SGPropertyNode *val = prop->getChild( "value" );
+                if ( val != NULL ) {
+                    Kp = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop1 = prop->getChild( "prop" );
+                if ( prop1 != NULL ) {
+                    Kp_prop = fgGetNode( prop1->getStringValue(), true );
+                    if ( val != NULL ) {
+                        Kp_prop->setDoubleValue(Kp);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop1 == NULL) {
                 Kp = prop->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated Kp config. Please use <prop> and/or <value> tags." );
+                }
                 proportional = true;
             }
 
@@ -419,13 +584,47 @@ FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
 
             prop = child->getChild( "u_min" );
             if ( prop != NULL ) {
+                SGPropertyNode *val = prop->getChild( "value" );
+                if ( val != NULL ) {
+                    u_min = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop1 = prop->getChild( "prop" );
+                if ( prop1 != NULL ) {
+                    umin_prop = fgGetNode( prop1->getStringValue(), true );
+                    if ( val != NULL ) {
+                        umin_prop->setDoubleValue(u_min);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop1 == NULL) {
                 u_min = prop->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_min config. Please use <prop> and/or <value> tags." );
+                }
                 clamp = true;
             }
 
             prop = child->getChild( "u_max" );
             if ( prop != NULL ) {
+                SGPropertyNode *val = prop->getChild( "value" );
+                if ( val != NULL ) {
+                    u_max = val->getDoubleValue();
+                }
+
+                SGPropertyNode *prop1 = prop->getChild( "prop" );
+                if ( prop1 != NULL ) {
+                    umax_prop = fgGetNode( prop1->getStringValue(), true );
+                    if ( val != NULL ) {
+                        umax_prop->setDoubleValue(u_max);
+                    }
+                }
+
+                // output deprecated usage warning
+                if (val == NULL && prop1 == NULL) {
                 u_max = prop->getDoubleValue();
+                    SG_LOG( SG_AUTOPILOT, SG_WARN, "Deprecated u_max config. Please use <prop> and/or <value> tags." );
+                }
                 clamp = true;
             }
         } else {
@@ -439,6 +638,10 @@ FGPISimpleController::FGPISimpleController( SGPropertyNode *node ):
 
 
 void FGPISimpleController::update( double dt ) {
+    if (umin_prop != NULL)u_min = umin_prop->getDoubleValue();
+    if (umax_prop != NULL)u_max = umax_prop->getDoubleValue();
+    if (Kp_prop != NULL)Kp = Kp_prop->getDoubleValue();
+
     if (enable_prop != NULL && enable_prop->getStringValue() == enable_value) {
         if ( !enabled ) {
             // we have just been enabled, zero out int_sum
@@ -512,12 +715,12 @@ void FGPISimpleController::update( double dt ) {
 
 
 FGPredictor::FGPredictor ( SGPropertyNode *node ):
-    debug( false ),
-    ivalue( 0.0 ),
     last_value ( 999999999.9 ),
     average ( 0.0 ),
     seconds( 0.0 ),
-    filter_gain( 0.0 )
+    filter_gain( 0.0 ),
+    debug( false ),
+    ivalue( 0.0 )
 {
     int i;
     for ( i = 0; i < node->nChildren(); ++i ) {
@@ -591,6 +794,187 @@ void FGPredictor::update( double dt ) {
 }
 
 
+FGDigitalFilter::FGDigitalFilter(SGPropertyNode *node):
+    Tf( 1.0 ),
+    samples( 1 ),
+    rateOfChange( 1.0 ),
+    gainFactor( 1.0 ),
+    gain_prop( NULL ),
+    output_min_clamp( -std::numeric_limits<double>::max() ),
+    output_max_clamp( std::numeric_limits<double>::max() )
+{
+    int i;
+    for ( i = 0; i < node->nChildren(); ++i ) {
+        SGPropertyNode *child = node->getChild(i);
+        string cname = child->getName();
+        string cval = child->getStringValue();
+        if ( cname == "name" ) {
+            name = cval;
+        } else if ( cname == "debug" ) {
+            debug = child->getBoolValue();
+        } else if ( cname == "enable" ) {
+            SGPropertyNode *prop = child->getChild( "prop" );
+            if ( prop != NULL ) {
+                enable_prop = fgGetNode( prop->getStringValue(), true );
+            }
+            SGPropertyNode *val = child->getChild( "value" );
+            if ( val != NULL ) {
+                enable_value = val->getStringValue();
+            }
+            SGPropertyNode *pass = child->getChild( "honor-passive" );
+            if ( pass != NULL ) {
+                honor_passive = pass->getBoolValue();
+            }
+        } else if ( cname == "type" ) {
+            if ( cval == "exponential" ) {
+                filterType = exponential;
+            } else if (cval == "double-exponential") {
+                filterType = doubleExponential;
+            } else if (cval == "moving-average") {
+                filterType = movingAverage;
+            } else if (cval == "noise-spike") {
+                filterType = noiseSpike;
+            } else if (cval == "gain") {
+                filterType = gain;
+            } else if (cval == "reciprocal") {
+                filterType = reciprocal;
+            }
+        } else if ( cname == "input" ) {
+            input_prop = fgGetNode( child->getStringValue(), true );
+        } else if ( cname == "filter-time" ) {
+            Tf = child->getDoubleValue();
+        } else if ( cname == "samples" ) {
+            samples = child->getIntValue();
+        } else if ( cname == "max-rate-of-change" ) {
+            rateOfChange = child->getDoubleValue();
+        } else if ( cname == "gain" ) {
+            SGPropertyNode *val = child->getChild( "value" );
+            if ( val != NULL ) {
+                gainFactor = val->getDoubleValue();
+            }
+            SGPropertyNode *prop = child->getChild( "prop" );
+            if ( prop != NULL ) {
+                gain_prop = fgGetNode( prop->getStringValue(), true );
+                gain_prop->setDoubleValue(gainFactor);
+            }
+        } else if ( cname == "u_min" ) {
+            output_min_clamp = child->getDoubleValue();
+        } else if ( cname == "u_max" ) {
+            output_max_clamp = child->getDoubleValue();
+        } else if ( cname == "output" ) {
+            SGPropertyNode *tmp = fgGetNode( child->getStringValue(), true );
+            output_list.push_back( tmp );
+        }
+    }
+
+    output.resize(2, 0.0);
+    input.resize(samples + 1, 0.0);
+}
+
+void FGDigitalFilter::update(double dt)
+{
+    if ( (input_prop != NULL && 
+          enable_prop != NULL && 
+          enable_prop->getStringValue() == enable_value) ||
+         (enable_prop == NULL &&
+          input_prop != NULL) ) {
+
+        input.push_front(input_prop->getDoubleValue());
+        input.resize(samples + 1, 0.0);
+
+        if ( !enabled ) {
+            // first time being enabled, initialize output to the
+            // value of the output property to avoid bumping.
+            output.push_front(output_list[0]->getDoubleValue());
+        }
+
+        enabled = true;
+    } else {
+        enabled = false;
+    }
+
+    if ( enabled && dt > 0.0 ) {
+        /*
+         * Exponential filter
+         *
+         * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
+         *
+         */
+
+        if (filterType == exponential)
+        {
+            double alpha = 1 / ((Tf/dt) + 1);
+            output.push_front(alpha * input[0] + 
+                              (1 - alpha) * output[0]);
+        } 
+        else if (filterType == doubleExponential)
+        {
+            double alpha = 1 / ((Tf/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()) / samples);
+        }
+        else if (filterType == noiseSpike)
+        {
+            double maxChange = rateOfChange * dt;
+
+            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)
+        {
+            if (gain_prop != NULL) {
+                gainFactor = gain_prop->getDoubleValue();
+            }
+            output[0] = gainFactor * input[0];
+        }
+        else if (filterType == reciprocal)
+        {
+            if (gain_prop != NULL) {
+                gainFactor = gain_prop->getDoubleValue();
+            }
+            if (input[0] != 0.0) {
+                output[0] = gainFactor / input[0];
+            }
+        }
+
+        if (output[0] < output_min_clamp) {
+            output[0] = output_min_clamp;
+        }
+        else if (output[0] > output_max_clamp) {
+            output[0] = output_max_clamp;
+        }
+
+        unsigned int i;
+        for ( i = 0; i < output_list.size(); ++i ) {
+            output_list[i]->setDoubleValue( output[0] );
+        }
+        output.resize(2);
+
+        if (debug)
+        {
+            cout << "input:" << input[0] 
+                 << "\toutput:" << output[0] << endl;
+        }
+    }
+}
+
+
 FGXMLAutopilot::FGXMLAutopilot() {
 }
 
@@ -615,7 +999,7 @@ void FGXMLAutopilot::init() {
 
             if ( ! build() ) {
                 SG_LOG( SG_ALL, SG_ALERT,
-                        "Detected an internal inconsistancy in the autopilot");
+                        "Detected an internal inconsistency in the autopilot");
                 SG_LOG( SG_ALL, SG_ALERT,
                         " configuration.  See earlier errors for" );
                 SG_LOG( SG_ALL, SG_ALERT,
@@ -637,7 +1021,6 @@ void FGXMLAutopilot::init() {
 void FGXMLAutopilot::reinit() {
     components.clear();
     init();
-    build();
 }
 
 
@@ -657,14 +1040,13 @@ bool FGXMLAutopilot::build() {
         string name = node->getName();
         // cout << name << endl;
         if ( name == "pid-controller" ) {
-            FGXMLAutoComponent *c = new FGPIDController( node );
-            components.push_back( c );
+            components.push_back( new FGPIDController( node ) );
         } else if ( name == "pi-simple-controller" ) {
-            FGXMLAutoComponent *c = new FGPISimpleController( node );
-            components.push_back( c );
+            components.push_back( new FGPISimpleController( node ) );
         } else if ( name == "predict-simple" ) {
-            FGXMLAutoComponent *c = new FGPredictor( node );
-            components.push_back( c );
+            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 );
@@ -751,7 +1133,7 @@ static void update_helper( double dt ) {
 
     // Calculate nav1 target heading error normalized to +/- 180.0
     static SGPropertyNode *target_nav1
-        = fgGetNode( "/radios/nav[0]/radials/target-auto-hdg-deg", true );
+        = fgGetNode( "/instrumentation/nav[0]/radials/target-auto-hdg-deg", true );
     static SGPropertyNode *true_nav1
         = fgGetNode( "/autopilot/internal/nav1-heading-error-deg", true );
     static SGPropertyNode *true_track_nav1
@@ -767,13 +1149,48 @@ static void update_helper( double dt ) {
     if ( diff > 180.0 ) { diff -= 360.0; }
     true_track_nav1->setDoubleValue( diff );
 
+    // Calculate nav1 selected course error normalized to +/- 180.0
+    // (based on DG indicated heading)
+    static SGPropertyNode *nav1_course_error
+        = fgGetNode( "/autopilot/internal/nav1-course-error", true );
+    static SGPropertyNode *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; }
+    SG_NORMALIZE_RANGE( diff, -180.0, 180.0 );
+    nav1_course_error->setDoubleValue( diff );
+
     // Calculate vertical speed in fpm
     static SGPropertyNode *vs_fps
         = fgGetNode( "/velocities/vertical-speed-fps", true );
     static SGPropertyNode *vs_fpm
         = fgGetNode( "/autopilot/internal/vert-speed-fpm", true );
 
-    vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );  
+    vs_fpm->setDoubleValue( vs_fps->getDoubleValue() * 60.0 );
+
+
+    // Calculate static port pressure rate in [inhg/s].
+    // Used to determine vertical speed.
+    static SGPropertyNode *static_pressure
+       = fgGetNode( "/systems/static[0]/pressure-inhg", true );
+    static SGPropertyNode *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;
+    }
+
 }
 
 
@@ -789,3 +1206,4 @@ void FGXMLAutopilot::update( double dt ) {
         components[i]->update( dt );
     }
 }
+