//
// 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 "xmlauto.hxx"
+using std::cout;
+using std::endl;
FGPIDController::FGPIDController( SGPropertyNode *node ):
debug( false ),
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 ),
ep_n_1( 0.0 ),
edf_n_1( 0.0 ),
edf_n_2( 0.0 ),
- u_n_1( 0.0 )
+ u_n_1( 0.0 ),
+ desiredTs( 0.0 ),
+ elapsedTime( 0.0 )
{
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
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 ) {
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 ) {
} 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;
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" );
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 ) {
}
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;
}
// 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 ) {
// 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;
}
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;
} else {
prop = child->getChild( "value" );
if ( prop != NULL ) {
- r_n = prop->getDoubleValue();
+ r_n_value = prop->getDoubleValue();
}
}
prop = child->getChild( "scale" );
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;
}
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 {
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
}
-FGDigitalFilter::FGDigitalFilter(SGPropertyNode *node)
+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() )
{
- samples = 1;
-
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
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;
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 );
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, 0.0);
+ input.resize(samples + 1, 0.0);
}
void FGDigitalFilter::update(double dt)
{
- if ( input_prop != NULL ) {
+ 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, 0.0);
- // no sense if there isn't an input :-)
+ 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;
double alpha = 1 / ((Tf/dt) + 1);
output.push_front(alpha * input[0] +
(1 - alpha) * output[0]);
- unsigned int i;
- for ( i = 0; i < output_list.size(); ++i ) {
- output_list[i]->setDoubleValue( output[0] );
- }
- output.resize(1);
}
else if (filterType == doubleExponential)
{
output.push_front(alpha * alpha * input[0] +
2 * (1 - alpha) * output[0] -
(1 - alpha) * (1 - alpha) * output[1]);
- unsigned int i;
- for ( i = 0; i < output_list.size(); ++i ) {
- output_list[i]->setDoubleValue( output[0] );
- }
- output.resize(2);
}
else if (filterType == movingAverage)
{
output.push_front(output[0] +
(input[0] - input.back()) / samples);
- unsigned int i;
- for ( i = 0; i < output_list.size(); ++i ) {
- output_list[i]->setDoubleValue( output[0] );
- }
- output.resize(1);
}
else if (filterType == noiseSpike)
{
{
output.push_front(input[0]);
}
-
- unsigned int i;
- for ( i = 0; i < output_list.size(); ++i ) {
- output_list[i]->setDoubleValue( output[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();
}
- output.resize(1);
+ 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]
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,
void FGXMLAutopilot::reinit() {
components.clear();
init();
- 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" ) {
- FGXMLAutoComponent *c = new FGDigitalFilter( node );
- components.push_back( c );
+ components.push_back( new FGDigitalFilter( node ) );
} else {
SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
<< name );
components[i]->update( dt );
}
}
+