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 );
scale = NULL;
min = NULL;
max = NULL;
+ periodical = NULL;
if( node == NULL )
return;
abs = n->getBoolValue();
}
+ if( (n = node->getChild( "period" )) != NULL ) {
+ periodical = new FGPeriodicalValue( n );
+ }
+
SGPropertyNode *valueNode = node->getChild( "value" );
if ( valueNode != NULL ) {
value = valueNode->getDoubleValue();
if( value > m )
value = m;
}
+
+ if( periodical ) {
+ value = periodical->normalize( value );
+ }
return abs ? fabs(value) : value;
}
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);
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 );
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 ) );
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 (filterType == exponential)
- {
- double alpha = 1 / ((TfInput.get_value()/dt) + 1);
- output.push_front(alpha * input[0] +
- (1 - alpha) * output[0]);
- }
- else if (filterType == doubleExponential)
+ if ( !enabled || dt < SGLimitsd::min() )
+ return;
+
+ /*
+ * Exponential filter
+ *
+ * Output[n] = alpha*Input[n] + (1-alpha)*Output[n-1]
+ *
+ */
+ if( debug ) cout << "Updating " << get_name()
+ << " dt " << dt << endl;
+
+ 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;
+
+ if ((output[0] - input[0]) > maxChange)
{
- 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]);
+ output.push_front(output[0] - maxChange);
}
- else if (filterType == movingAverage)
+ else if ((output[0] - input[0]) < -maxChange)
{
- output.push_front(output[0] +
- (input[0] - input.back()) / samplesInput.get_value());
+ output.push_front(output[0] + maxChange);
}
- else if (filterType == noiseSpike)
+ else if (fabs(input[0] - output[0]) <= maxChange)
{
- double maxChange = rateOfChangeInput.get_value() * 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]);
- }
+ output.push_front(input[0]);
}
- else if (filterType == gain)
- {
- output[0] = gainInput.get_value() * 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 == 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[0] = clamp(output[0]) ;
+ set_output_value( output[0] );
- output.resize(2);
+ output.resize(2);
- if (debug)
- {
- cout << "input:" << input[0]
- << "\toutput:" << output[0] << endl;
+ if (debug)
+ {
+ cout << "input:" << input[0]
+ << "\toutput:" << output[0] << endl;
+ }
+}
+
+FGXMLAutoLogic::FGXMLAutoLogic(SGPropertyNode * node ) :
+ FGXMLAutoComponent(),
+ inverted(false)
+{
+ parseNode(node);
+}
+
+bool FGXMLAutoLogic::parseNodeHook(const std::string& aName, SGPropertyNode* aNode)
+{
+ if (aName == "input") {
+ input = sgReadCondition( fgGetNode("/"), aNode );
+ } else if (aName == "inverted") {
+ inverted = aNode->getBoolValue();
+ } else {
+ return false;
+ }
+
+ return true;
+}
+
+void FGXMLAutoLogic::update(double dt)
+{
+ if ( isPropertyEnabled() ) {
+ if ( !enabled ) {
+ // we have just been enabled
}
+ enabled = true;
+ } else {
+ enabled = false;
+ do_feedback();
+ }
+
+ if ( !enabled || dt < SGLimitsd::min() )
+ return;
+
+ if( input == NULL ) {
+ if ( debug ) cout << "No input for " << get_name() << endl;
+ return;
}
+
+ bool i = input->test();
+
+ if ( debug ) cout << "Updating " << get_name() << ": " << (inverted ? !i : i) << endl;
+
+ set_output_value( i );
+}
+
+
+FGXMLAutopilotGroup::FGXMLAutopilotGroup() :
+ SGSubsystemGroup()
+#ifdef XMLAUTO_USEHELPER
+ ,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 )),
+ track(fgGetNode( "/orientation/track-deg", true ))
+#endif
+{
}
-FGXMLAutopilotGroup::FGXMLAutopilotGroup()
+void FGXMLAutopilotGroup::update( double dt )
{
+ // update all configured autopilots
+ SGSubsystemGroup::update( dt );
+#ifdef XMLAUTO_USEHELPER
+ // 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
+ diff = target_nav1->getDoubleValue() - track->getDoubleValue();
+ 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;
+ }
+#endif
}
void FGXMLAutopilotGroup::reinit()
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 );
}
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" ) {
components.push_back( new FGPredictor( node ) );
} else if ( name == "filter" ) {
components.push_back( new FGDigitalFilter( node ) );
+ } else if ( name == "logic" ) {
+ components.push_back( new FGXMLAutoLogic( node ) );
} else {
- SG_LOG( SG_ALL, SG_ALERT, "Unknown top level section: "
- << name );
- return false;
+ SG_LOG( SG_AUTOPILOT, SG_WARN, "Unknown top level autopilot section: " << name );
+// return false;
}
}
return true;
}
-
-/*
- * Update helper values
- */
-static void 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 (based on
- // DG indicated heading)
- 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 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
- 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_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; }
- 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();
- if ( diff < -180.0 ) { diff += 360.0; }
- if ( diff > 180.0 ) { diff -= 360.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; }
- 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; }
- 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 );