first commit
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
include(FlightGearComponent)
|
||||
|
||||
set(SOURCES
|
||||
analogcomponent.cxx
|
||||
autopilot.cxx
|
||||
autopilotgroup.cxx
|
||||
component.cxx
|
||||
digitalcomponent.cxx
|
||||
digitalfilter.cxx
|
||||
flipflop.cxx
|
||||
inputvalue.cxx
|
||||
logic.cxx
|
||||
pidcontroller.cxx
|
||||
pisimplecontroller.cxx
|
||||
predictor.cxx
|
||||
route_mgr.cxx
|
||||
)
|
||||
|
||||
set(HEADERS
|
||||
analogcomponent.hxx
|
||||
autopilot.hxx
|
||||
autopilotgroup.hxx
|
||||
component.hxx
|
||||
digitalcomponent.hxx
|
||||
digitalfilter.hxx
|
||||
flipflop.hxx
|
||||
inputvalue.hxx
|
||||
logic.hxx
|
||||
pidcontroller.hxx
|
||||
pisimplecontroller.hxx
|
||||
predictor.hxx
|
||||
route_mgr.hxx
|
||||
)
|
||||
|
||||
flightgear_component(Autopilot "${SOURCES}" "${HEADERS}")
|
||||
@@ -0,0 +1,130 @@
|
||||
// analogcomponent.cxx - Base class for analog autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#include "analogcomponent.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
AnalogComponent::AnalogComponent() :
|
||||
Component(),
|
||||
_feedback_if_disabled(false),
|
||||
_passive_mode( fgGetNode("/autopilot/locks/passive-mode", true) )
|
||||
{
|
||||
}
|
||||
|
||||
double AnalogComponent::clamp( double value ) const
|
||||
{
|
||||
//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( _minInput.size() + _maxInput.size() > 0 ) {
|
||||
double d = _maxInput.get_value();
|
||||
if( value > d ) value = d;
|
||||
d = _minInput.get_value();
|
||||
if( value < d ) value = d;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool AnalogComponent::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( cfg_name == "feedback-if-disabled" )
|
||||
{
|
||||
_feedback_if_disabled = cfg_node.getBoolValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "output" )
|
||||
{
|
||||
// grab all <prop> and <property> childs.
|
||||
bool found = false;
|
||||
for( int i = 0; i < cfg_node.nChildren(); ++i )
|
||||
{
|
||||
SGPropertyNode* child = cfg_node.getChild(i);
|
||||
const std::string& name = child->getNameString();
|
||||
|
||||
// Allow "prop" for backwards compatiblity
|
||||
if( name != "property" && name != "prop" )
|
||||
continue;
|
||||
|
||||
const auto trimmed = simgear::strutils::strip(child->getStringValue());
|
||||
_output_list.push_back( prop_root.getNode(trimmed, true) );
|
||||
found = true;
|
||||
}
|
||||
|
||||
// no <prop> elements, text node of <output> is property name
|
||||
if( !found ) {
|
||||
const auto trimmed = simgear::strutils::strip(cfg_node.getStringValue());
|
||||
_output_list.push_back(prop_root.getNode(trimmed, true));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "input" )
|
||||
{
|
||||
_valueInput.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "reference" )
|
||||
{
|
||||
_referenceInput.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "min" || cfg_name == "u_min" )
|
||||
{
|
||||
_minInput.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "max" || cfg_name == "u_max" )
|
||||
{
|
||||
_maxInput.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "period" )
|
||||
{
|
||||
_periodical = new PeriodicalValue(prop_root, cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
return Component::configure(cfg_node, cfg_name, prop_root);
|
||||
}
|
||||
|
||||
void AnalogComponent::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
|
||||
{
|
||||
_valueInput.collectDependentProperties(props);
|
||||
_referenceInput.collectDependentProperties(props);
|
||||
_minInput.collectDependentProperties(props);
|
||||
_maxInput.collectDependentProperties(props);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
// analogcomponent.hxx - Base class for analog autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __ANALOGCOMPONENT_HXX
|
||||
#define __ANALOGCOMPONENT_HXX 1
|
||||
|
||||
#include "inputvalue.hxx"
|
||||
#include "component.hxx"
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Base class for analog autopilot components
|
||||
*
|
||||
* Each analog component has
|
||||
* <ul>
|
||||
* <li>one value input</li>
|
||||
* <li>one reference input</li>
|
||||
* <li>one minimum clamp input</li>
|
||||
* <li>one maximum clamp input</li>
|
||||
* <li>an optional periodical definition</li>
|
||||
* </ul>
|
||||
*/
|
||||
class AnalogComponent : public Component
|
||||
{
|
||||
private:
|
||||
/**
|
||||
* @brief a flag signalling that the output property value shall be fed back
|
||||
* to the active input property if this component is disabled. This flag
|
||||
* reflects the <feedback-if-disabled> boolean property.
|
||||
*/
|
||||
bool _feedback_if_disabled;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief the value input
|
||||
*/
|
||||
InputValueList _valueInput;
|
||||
|
||||
/**
|
||||
* @brief the reference input
|
||||
*/
|
||||
InputValueList _referenceInput;
|
||||
|
||||
/**
|
||||
* @brief the minimum output clamp input
|
||||
*/
|
||||
InputValueList _minInput;
|
||||
|
||||
/**
|
||||
* @brief the maximum output clamp input
|
||||
*/
|
||||
InputValueList _maxInput;
|
||||
|
||||
/**
|
||||
* @brief the configuration for periodical outputs
|
||||
*/
|
||||
PeriodicalValue_ptr _periodical;
|
||||
|
||||
/**
|
||||
* @brief A constructor for an analog component. Call configure() to
|
||||
* configure this component from a property node
|
||||
*/
|
||||
AnalogComponent();
|
||||
|
||||
/**
|
||||
* @brief This method configures this analog component from a property node.
|
||||
* Gets called multiple times from the base class configure method
|
||||
* for every configuration node.
|
||||
* @param cfg_name Name of the configuration node provided in cfg_node
|
||||
* @param cfg_node Configuration node itself
|
||||
* @param prop_root Property root for all relative paths
|
||||
* @return true if the node was handled, false otherwise.
|
||||
*/
|
||||
bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root ) override;
|
||||
|
||||
/**
|
||||
* @brief clamp the given value if <min> and/or <max> inputs were given
|
||||
* @param the value to clamp
|
||||
* @return the clamped value
|
||||
*/
|
||||
double clamp( double value ) const;
|
||||
|
||||
/**
|
||||
* @brief overideable method being called from the update() method if this component
|
||||
* is disabled. Analog components feed back it's output value to the active
|
||||
input value if disabled and feedback-if-disabled is true
|
||||
*/
|
||||
void disabled( double dt ) override;
|
||||
|
||||
/**
|
||||
* @brief return the current double value of the output property
|
||||
* @return the current value of the output property
|
||||
* If no output property is configured, a value of zero will be returned.
|
||||
* If more than one output property is configured, the value of the first output property
|
||||
* is returned. The current value of the output property will be clamped to the configured
|
||||
* values of <min> and/or <max>.
|
||||
*/
|
||||
inline double get_output_value() const {
|
||||
return _output_list.empty() ? 0.0 : clamp(_output_list[0]->getDoubleValue());
|
||||
}
|
||||
|
||||
simgear::PropertyList _output_list;
|
||||
SGPropertyNode_ptr _passive_mode;
|
||||
|
||||
inline void set_output_value( double value ) {
|
||||
// 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 ( _honor_passive && _passive_mode->getBoolValue() ) return;
|
||||
value = clamp( value );
|
||||
for( simgear::PropertyList::iterator it = _output_list.begin();
|
||||
it != _output_list.end(); ++it)
|
||||
(*it)->setDoubleValue( value );
|
||||
}
|
||||
|
||||
public:
|
||||
const PeriodicalValue * getPeriodicalValue() const { return _periodical; }
|
||||
|
||||
/**
|
||||
Add to <props> all properties that are used by this component. Similar to
|
||||
SGExpression::collectDependentProperties().
|
||||
*/
|
||||
void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
|
||||
};
|
||||
|
||||
inline void AnalogComponent::disabled( double dt )
|
||||
{
|
||||
if( _feedback_if_disabled && ! _output_list.empty() ) {
|
||||
InputValue * input;
|
||||
if( (input = _valueInput.get_active() ) != NULL )
|
||||
input->set_value( _output_list[0]->getDoubleValue() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif // ANALOGCOMPONENT_HXX
|
||||
@@ -0,0 +1,239 @@
|
||||
// autopilot.cxx - an even more flexible, generic way to build autopilots
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "autopilot.hxx"
|
||||
|
||||
#include <simgear/structure/StateMachine.hxx>
|
||||
#include <simgear/sg_inlines.h>
|
||||
|
||||
#include "component.hxx"
|
||||
#include "functor.hxx"
|
||||
#include "predictor.hxx"
|
||||
#include "digitalfilter.hxx"
|
||||
#include "pisimplecontroller.hxx"
|
||||
#include "pidcontroller.hxx"
|
||||
#include "logic.hxx"
|
||||
#include "flipflop.hxx"
|
||||
|
||||
#include "Main/fg_props.hxx"
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
class StateMachineComponent : public Component
|
||||
{
|
||||
public:
|
||||
StateMachineComponent( SGPropertyNode& props_root,
|
||||
SGPropertyNode& cfg ) {
|
||||
inner = simgear::StateMachine::createFromPlist(&cfg, &props_root);
|
||||
}
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "state-machine"; }
|
||||
|
||||
virtual bool configure( const std::string & nodeName, SGPropertyNode_ptr config) {
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void update( bool firstTime, double dt ) {
|
||||
SG_UNUSED(firstTime);
|
||||
inner->update(dt);
|
||||
}
|
||||
|
||||
private:
|
||||
simgear::StateMachine_ptr inner;
|
||||
};
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
#if 0
|
||||
SGSubsystemMgr::Registrant<StateMachineComponent> registrantStateMachineComponent;
|
||||
#endif
|
||||
|
||||
|
||||
class StateMachineFunctor : public FunctorBase<Component>
|
||||
{
|
||||
public:
|
||||
virtual ~StateMachineFunctor() {}
|
||||
virtual Component* operator()( SGPropertyNode& cfg,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
return new StateMachineComponent(cfg, prop_root);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class ComponentForge : public map<string,FunctorBase<Component> *> {
|
||||
public:
|
||||
virtual ~ ComponentForge();
|
||||
};
|
||||
|
||||
ComponentForge::~ComponentForge()
|
||||
{
|
||||
for( iterator it = begin(); it != end(); ++it )
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
void readInterfaceProperties( SGPropertyNode_ptr prop_root,
|
||||
SGPropertyNode_ptr cfg )
|
||||
{
|
||||
simgear::PropertyList cfg_props = cfg->getChildren("property");
|
||||
for( simgear::PropertyList::iterator it = cfg_props.begin();
|
||||
it != cfg_props.end();
|
||||
++it )
|
||||
{
|
||||
SGPropertyNode_ptr prop = prop_root->getNode((*it)->getStringValue(), true);
|
||||
SGPropertyNode* val = (*it)->getNode("_attr_/value");
|
||||
|
||||
if( val )
|
||||
{
|
||||
prop->setDoubleValue( val->getDoubleValue() );
|
||||
|
||||
// TODO should we keep the _attr_ node, as soon as the property browser is
|
||||
// able to cope with it?
|
||||
(*it)->removeChild("_attr_", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static ComponentForge componentForge;
|
||||
|
||||
Autopilot::Autopilot( SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode ) :
|
||||
_name("unnamed autopilot"),
|
||||
_serviceable(true),
|
||||
_rootNode(rootNode)
|
||||
{
|
||||
if (componentForge.empty())
|
||||
{
|
||||
componentForge["pid-controller"] = new CreateAndConfigureFunctor<PIDController,Component>();
|
||||
componentForge["pi-simple-controller"] = new CreateAndConfigureFunctor<PISimpleController,Component>();
|
||||
componentForge["predict-simple"] = new CreateAndConfigureFunctor<Predictor,Component>();
|
||||
componentForge["filter"] = new CreateAndConfigureFunctor<DigitalFilter,Component>();
|
||||
componentForge["logic"] = new CreateAndConfigureFunctor<Logic,Component>();
|
||||
componentForge["flipflop"] = new CreateAndConfigureFunctor<FlipFlop,Component>();
|
||||
componentForge["state-machine"] = new StateMachineFunctor();
|
||||
}
|
||||
|
||||
if( !configNode )
|
||||
configNode = rootNode;
|
||||
|
||||
// property-root can be set in config file and overridden in the local system
|
||||
// node. This allows using the same autopilot multiple times but with
|
||||
// different paths (with all relative property paths being relative to the
|
||||
// node specified with property-root)
|
||||
SGPropertyNode_ptr prop_root_node = rootNode->getChild("property-root");
|
||||
if( !prop_root_node )
|
||||
prop_root_node = configNode->getChild("property-root");
|
||||
|
||||
SGPropertyNode_ptr prop_root =
|
||||
fgGetNode(prop_root_node ? prop_root_node->getStringValue() : "/", true);
|
||||
|
||||
// Just like the JSBSim interface properties for systems, create properties
|
||||
// given in the autopilot file and set to given (default) values.
|
||||
readInterfaceProperties(prop_root, configNode);
|
||||
|
||||
// Afterwards read the properties specified in local system node to allow
|
||||
// overriding initial or default values. This allows reusing components with
|
||||
// just different "parameter" values.
|
||||
readInterfaceProperties(prop_root, rootNode);
|
||||
|
||||
int count = configNode->nChildren();
|
||||
for( int i = 0; i < count; ++i )
|
||||
{
|
||||
SGPropertyNode_ptr node = configNode->getChild(i);
|
||||
string childName = node->getNameString();
|
||||
if( childName == "property"
|
||||
|| childName == "property-root" )
|
||||
continue;
|
||||
if( componentForge.count(childName) == 0 )
|
||||
{
|
||||
SG_LOG(SG_AUTOPILOT, SG_BULK, "unhandled element <" << childName << ">");
|
||||
continue;
|
||||
}
|
||||
|
||||
Component * component = (*componentForge[childName])(*prop_root, *node);
|
||||
if( component->subsystemId().length() == 0 ) {
|
||||
std::ostringstream buf;
|
||||
buf << "unnamed_component_" << i;
|
||||
}
|
||||
|
||||
double updateInterval = node->getDoubleValue( "update-interval-secs", 0.0 );
|
||||
|
||||
SG_LOG( SG_AUTOPILOT, SG_DEBUG, "adding autopilot component \"" << childName << "\" as \"" << component->subsystemId() << "\" with interval=" << updateInterval );
|
||||
add_component(component,updateInterval);
|
||||
}
|
||||
}
|
||||
|
||||
Autopilot::~Autopilot()
|
||||
{
|
||||
}
|
||||
|
||||
void Autopilot::bind()
|
||||
{
|
||||
fgTie( _rootNode->getNode("serviceable", true)->getPath().c_str(), this,
|
||||
&Autopilot::is_serviceable, &Autopilot::set_serviceable );
|
||||
SGSubsystemGroup::bind();
|
||||
}
|
||||
|
||||
void Autopilot::unbind()
|
||||
{
|
||||
_rootNode->untie( "serviceable" );
|
||||
SGSubsystemGroup::unbind();
|
||||
}
|
||||
|
||||
void Autopilot::add_component( Component * component, double updateInterval )
|
||||
{
|
||||
if( component == NULL ) return;
|
||||
|
||||
// check for duplicate name
|
||||
const auto originalName = string{component->subsystemId()};
|
||||
std::string name = originalName;
|
||||
if (name.empty()) {
|
||||
name = "unnamed_autopilot";
|
||||
}
|
||||
|
||||
for( unsigned int i = 0; get_subsystem( name) != nullptr; i++ ) {
|
||||
std::ostringstream buf;
|
||||
buf << component->subsystemId() << "_" << i;
|
||||
name = buf.str();
|
||||
}
|
||||
|
||||
if (!originalName.empty() && (name != originalName)) {
|
||||
SG_LOG( SG_AUTOPILOT, SG_DEV_WARN, "Duplicate autopilot component " << originalName << ", renamed to " << name );
|
||||
}
|
||||
|
||||
set_subsystem( name, component, updateInterval );
|
||||
}
|
||||
|
||||
void Autopilot::update( double dt )
|
||||
{
|
||||
if( !_serviceable || dt <= SGLimitsd::min() )
|
||||
return;
|
||||
SGSubsystemGroup::update( dt );
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
// autopilot.hxx - an even more flexible, generic way to build autopilots
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __AUTOPILOT_HXX
|
||||
#define __AUTOPILOT_HXX 1
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
class Component;
|
||||
|
||||
/**
|
||||
* @brief A SGSubsystemGroup implementation to serve as a collection
|
||||
* of Components
|
||||
*/
|
||||
class Autopilot : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
Autopilot( SGPropertyNode_ptr rootNode, SGPropertyNode_ptr configNode = NULL );
|
||||
~Autopilot();
|
||||
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "autopilot"; }
|
||||
|
||||
void set_serviceable( bool value ) { _serviceable = value; }
|
||||
bool is_serviceable() const { return _serviceable; }
|
||||
|
||||
std::string get_name() const { return _name; }
|
||||
void set_name( const std::string & name ) { _name = name; }
|
||||
|
||||
void add_component( Component * component, double updateInterval );
|
||||
|
||||
protected:
|
||||
|
||||
private:
|
||||
std::string _name;
|
||||
bool _serviceable;
|
||||
SGPropertyNode_ptr _rootNode;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AUTOPILOT_HXX 1
|
||||
@@ -0,0 +1,238 @@
|
||||
// autopilotgroup.cxx - an even more flexible, generic way to build autopilots
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include "autopilot.hxx"
|
||||
#include "autopilotgroup.hxx"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <simgear/debug/ErrorReportingCallback.hxx>
|
||||
#include <simgear/props/props_io.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <Main/sentryIntegration.hxx>
|
||||
|
||||
using std::vector;
|
||||
using simgear::PropertyList;
|
||||
using FGXMLAutopilot::Autopilot;
|
||||
|
||||
class FGXMLAutopilotGroupImplementation : public FGXMLAutopilotGroup
|
||||
{
|
||||
public:
|
||||
FGXMLAutopilotGroupImplementation(const std::string& nodeName):
|
||||
FGXMLAutopilotGroup(),
|
||||
_nodeName(nodeName)
|
||||
{}
|
||||
|
||||
// Subsystem API.
|
||||
void init() override;
|
||||
InitStatus incrementalInit() override;
|
||||
void reinit() override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "xml-autopilot-group"; }
|
||||
|
||||
virtual void addAutopilot( const std::string& name,
|
||||
SGPropertyNode_ptr apNode,
|
||||
SGPropertyNode_ptr config );
|
||||
virtual void removeAutopilot( const std::string & name );
|
||||
|
||||
private:
|
||||
void initFrom( SGPropertyNode_ptr rootNode, const char * childName );
|
||||
std::string _nodeName;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGXMLAutopilotGroupImplementation::addAutopilot( const std::string& name,
|
||||
SGPropertyNode_ptr apNode,
|
||||
SGPropertyNode_ptr config )
|
||||
{
|
||||
if( has_subsystem(name) )
|
||||
{
|
||||
SG_LOG( SG_AUTOPILOT,
|
||||
SG_ALERT,
|
||||
"NOT adding duplicate " << _nodeName << " name '" << name << "'");
|
||||
return;
|
||||
}
|
||||
|
||||
Autopilot* ap = new Autopilot(apNode, config);
|
||||
ap->set_name( name );
|
||||
|
||||
double updateInterval = config->getDoubleValue("update-interval-secs", 0.0);
|
||||
set_subsystem( name, ap, updateInterval );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGXMLAutopilotGroupImplementation::removeAutopilot(const std::string& name)
|
||||
{
|
||||
Autopilot* ap = static_cast<Autopilot*>(get_subsystem(name));
|
||||
if( !ap )
|
||||
{
|
||||
SG_LOG( SG_AUTOPILOT,
|
||||
SG_ALERT,
|
||||
"CAN NOT remove unknown " << _nodeName << " '" << name << "'");
|
||||
return;
|
||||
}
|
||||
|
||||
remove_subsystem(name);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGXMLAutopilotGroupImplementation::reinit()
|
||||
{
|
||||
SGSubsystemGroup::unbind();
|
||||
clearSubsystems();
|
||||
|
||||
// ensure we bind again, so the SGSubsystemGroup state is correct before
|
||||
// we call init. Since there's no actual group members at this point (we
|
||||
// cleared them just above) this is purely to ensure SGSubsystemGroup::_state
|
||||
// is BIND, so that ::init doesn't assert
|
||||
SGSubsystemGroup::bind();
|
||||
init();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
SGSubsystem::InitStatus FGXMLAutopilotGroupImplementation::incrementalInit()
|
||||
{
|
||||
init();
|
||||
return INIT_DONE;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGXMLAutopilotGroupImplementation::init()
|
||||
{
|
||||
initFrom(fgGetNode("/sim/systems"), _nodeName.c_str());
|
||||
SGSubsystemGroup::init();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void FGXMLAutopilotGroupImplementation::initFrom( SGPropertyNode_ptr rootNode,
|
||||
const char* childName )
|
||||
{
|
||||
if( !rootNode )
|
||||
return;
|
||||
|
||||
for( auto autopilotNode : rootNode->getChildren(childName) )
|
||||
{
|
||||
SGPropertyNode_ptr pathNode = autopilotNode->getNode("path");
|
||||
if( !pathNode )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_WARN,
|
||||
"No configuration file specified for this " << childName << "!"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string apName;
|
||||
SGPropertyNode_ptr nameNode = autopilotNode->getNode( "name" );
|
||||
if( nameNode != NULL ) {
|
||||
apName = nameNode->getStringValue();
|
||||
} else {
|
||||
std::ostringstream buf;
|
||||
buf << "unnamed_autopilot_" << autopilotNode->getIndex();
|
||||
apName = buf.str();
|
||||
}
|
||||
|
||||
{
|
||||
// check for duplicate names
|
||||
std::string name = apName;
|
||||
for( unsigned i = 0; get_subsystem( apName.c_str() ) != NULL; i++ ) {
|
||||
std::ostringstream buf;
|
||||
buf << name << "_" << i;
|
||||
apName = buf.str();
|
||||
}
|
||||
if( apName != name )
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_DEV_WARN,
|
||||
"Duplicate " << childName << " configuration name " << name
|
||||
<< ", renamed to " << apName
|
||||
);
|
||||
}
|
||||
|
||||
addAutopilotFromFile(apName, autopilotNode, pathNode->getStringValue());
|
||||
}
|
||||
}
|
||||
|
||||
void FGXMLAutopilotGroup::addAutopilotFromFile( const std::string& name,
|
||||
SGPropertyNode_ptr apNode,
|
||||
const std::string& path )
|
||||
{
|
||||
SGPath config = globals->resolve_maybe_aircraft_path(path);
|
||||
if( config.isNull() )
|
||||
{
|
||||
simgear::reportFailure(simgear::LoadFailure::NotFound, simgear::ErrorCode::AircraftSystems,
|
||||
string{"Autopilot XML not found:"} + path, sg_location{path});
|
||||
SG_LOG(
|
||||
SG_AUTOPILOT,
|
||||
SG_ALERT,
|
||||
"Cannot find property-rule configuration file '" << path << "'.");
|
||||
return;
|
||||
}
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_INFO,
|
||||
"Reading property-rule configuration from " << config
|
||||
);
|
||||
|
||||
try
|
||||
{
|
||||
SGPropertyNode_ptr configNode = new SGPropertyNode();
|
||||
readProperties(config, configNode);
|
||||
|
||||
SG_LOG(SG_AUTOPILOT, SG_INFO, "adding property-rule subsystem " << name);
|
||||
addAutopilot(name, apNode, configNode);
|
||||
}
|
||||
catch (const sg_exception& e)
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_ALERT,
|
||||
"Failed to load property-rule configuration: " << config
|
||||
<< ": " << e.getMessage()
|
||||
);
|
||||
simgear::reportFailure(simgear::LoadFailure::BadData, simgear::ErrorCode::AircraftSystems,
|
||||
string{"Autopilot XML faield to load:"} + e.getFormattedMessage(), e.getLocation());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FGXMLAutopilotGroup*
|
||||
FGXMLAutopilotGroup::createInstance(const std::string& nodeName)
|
||||
{
|
||||
return new FGXMLAutopilotGroupImplementation(nodeName);
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// autopilotgroup.hxx - an even more flexible, generic way to build autopilots
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef _XMLAUTO_HXX
|
||||
#define _XMLAUTO_HXX 1
|
||||
|
||||
/**
|
||||
* @brief Model an autopilot system by implementing a SGSubsystemGroup
|
||||
*
|
||||
*/
|
||||
class FGXMLAutopilotGroup : public SGSubsystemGroup
|
||||
{
|
||||
public:
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "xml-rules"; }
|
||||
|
||||
static FGXMLAutopilotGroup * createInstance(const std::string& nodeName);
|
||||
|
||||
void addAutopilotFromFile( const std::string & name, SGPropertyNode_ptr apNode, const std::string& path );
|
||||
virtual void addAutopilot( const std::string & name, SGPropertyNode_ptr apNode, SGPropertyNode_ptr config ) = 0;
|
||||
virtual void removeAutopilot( const std::string & name ) = 0;
|
||||
|
||||
protected:
|
||||
FGXMLAutopilotGroup() : SGSubsystemGroup() {}
|
||||
};
|
||||
|
||||
#endif // _XMLAUTO_HXX
|
||||
@@ -0,0 +1,146 @@
|
||||
// component.cxx - Base class for autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#include "component.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
#include <simgear/structure/exception.hxx>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
Component::Component() :
|
||||
_enable_value(NULL),
|
||||
_enabled(false),
|
||||
_debug(false),
|
||||
_honor_passive(false)
|
||||
{
|
||||
}
|
||||
|
||||
Component::~Component()
|
||||
{
|
||||
delete _enable_value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Component::configure( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg )
|
||||
{
|
||||
for( int i = 0; i < cfg.nChildren(); ++i )
|
||||
{
|
||||
SGPropertyNode_ptr child = cfg.getChild(i);
|
||||
std::string cname(child->getNameString());
|
||||
|
||||
if( !configure(*child, cname, prop_root)
|
||||
&& cname != "params" ) // 'params' is usually used to specify parameters
|
||||
// in PropertList files.
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_INFO,
|
||||
"Component::configure: unknown node: " << cname
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Component::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if ( cfg_name == "name" )
|
||||
{
|
||||
set_name(cfg_node.getStringValue());
|
||||
return true;
|
||||
}
|
||||
|
||||
if( cfg_name == "update-interval-secs" )
|
||||
// This is handled in autopilot.cxx
|
||||
return true;
|
||||
|
||||
if ( cfg_name == "debug" )
|
||||
{
|
||||
_debug = cfg_node.getBoolValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( cfg_name == "enable" )
|
||||
{
|
||||
SGPropertyNode_ptr prop;
|
||||
|
||||
if( (prop = cfg_node.getChild("condition")) != NULL ) {
|
||||
_condition = sgReadCondition(fgGetNode("/"), prop);
|
||||
return true;
|
||||
}
|
||||
if ( (prop = cfg_node.getChild( "property" )) != NULL ) {
|
||||
_enable_prop = fgGetNode( prop->getStringValue(), true );
|
||||
}
|
||||
|
||||
if ( (prop = cfg_node.getChild( "prop" )) != NULL ) {
|
||||
_enable_prop = fgGetNode( prop->getStringValue(), true );
|
||||
}
|
||||
|
||||
if ( (prop = cfg_node.getChild( "value" )) != NULL ) {
|
||||
delete _enable_value;
|
||||
_enable_value = new std::string(prop->getStringValue());
|
||||
}
|
||||
|
||||
if ( (prop = cfg_node.getChild( "honor-passive" )) != NULL ) {
|
||||
_honor_passive = prop->getBoolValue();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Component::isPropertyEnabled()
|
||||
{
|
||||
if( _condition )
|
||||
return _condition->test();
|
||||
|
||||
if( _enable_prop ) {
|
||||
if( _enable_value ) {
|
||||
return *_enable_value == _enable_prop->getStringValue();
|
||||
} else {
|
||||
return _enable_prop->getBoolValue();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Component::update( double dt )
|
||||
{
|
||||
bool firstTime = false;
|
||||
if( isPropertyEnabled() ) {
|
||||
firstTime = !_enabled;
|
||||
_enabled = true;
|
||||
} else {
|
||||
_enabled = false;
|
||||
}
|
||||
|
||||
if( _enabled ) update( firstTime, dt );
|
||||
else disabled( dt );
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
// component.hxx - Base class for autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
#include <simgear/props/propsfwd.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Base class for other autopilot components
|
||||
*/
|
||||
class Component : public SGSubsystem
|
||||
{
|
||||
private:
|
||||
SGSharedPtr<const SGCondition> _condition;
|
||||
SGPropertyNode_ptr _enable_prop;
|
||||
std::string * _enable_value;
|
||||
bool _enabled;
|
||||
|
||||
protected:
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
|
||||
/**
|
||||
* @brief pure virtual function to be implemented by the derived classes. Gets called from
|
||||
* the update method if it's not disabled with the firstTime parameter set to true if this
|
||||
* is the first call after being enabled
|
||||
* @param firstTime set to true if this is the first update call since this component has
|
||||
been enabled. Set to false for every subsequent call.
|
||||
* @param dt the elapsed time since the last call
|
||||
*/
|
||||
virtual void update( bool firstTime, double dt ) = 0;
|
||||
|
||||
/**
|
||||
* @brief overideable method being called from the update() method if this component
|
||||
* is disabled. It's a noop by default.
|
||||
*/
|
||||
virtual void disabled( double dt ) {}
|
||||
|
||||
/**
|
||||
* @brief debug flag, true if this component should generate some useful output
|
||||
* on every iteration
|
||||
*/
|
||||
bool _debug;
|
||||
|
||||
|
||||
/**
|
||||
* @brief a (historic) flag signalling the derived class that it should compute it's internal
|
||||
* state but shall not set the output properties if /autopilot/locks/passive-mode is true.
|
||||
*/
|
||||
bool _honor_passive;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief A constructor for an empty Component.
|
||||
*/
|
||||
Component();
|
||||
|
||||
/**
|
||||
* virtual destructor to clean up resources
|
||||
*/
|
||||
virtual ~Component();
|
||||
|
||||
// Subsystem API.
|
||||
void update(double dt) override;
|
||||
|
||||
/**
|
||||
* @brief configure this component from a property node. Iterates through
|
||||
* all nodes found as children under configNode and calls configure
|
||||
* of the derived class for each child.
|
||||
*
|
||||
* @param prop_root Property root for all relative paths
|
||||
* @param cfg Property node containing the configuration
|
||||
*/
|
||||
virtual bool configure( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg );
|
||||
|
||||
/**
|
||||
* @brief check if this component is enabled as configured in the
|
||||
* <enable> section
|
||||
* @return true if the enable-condition is true.
|
||||
*
|
||||
* If a <condition> is defined, this condition is evaluated,
|
||||
* <prop> and <value> tags are ignored.
|
||||
*
|
||||
* If a <prop> is defined and no <value> is defined, the property
|
||||
* named in the <prop><prop> tags is evaluated as boolean.
|
||||
*
|
||||
* If a <prop> is defined and a <value> is defined, the property named
|
||||
* in <prop></prop> is compared (as a string) to the value defined in
|
||||
* <value></value>
|
||||
*
|
||||
* Returns true, if neither <condition> nor <prop> exists
|
||||
*/
|
||||
bool isPropertyEnabled();
|
||||
};
|
||||
|
||||
} // of namespace FGXMLAutopilot
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
// digitalcomponent.cxx - Base class for digital autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "digitalcomponent.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
using std::string;
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
DigitalComponent::DigitalComponent() :
|
||||
_inverted(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool DigitalComponent::InputMap::get_value( const std::string & name ) const
|
||||
{
|
||||
// can't use map::operator[] here since it's not const
|
||||
const_iterator __i = lower_bound( name );
|
||||
if (__i == end() || key_comp()(name, (*__i).first))
|
||||
return false; // does not exist, return false
|
||||
|
||||
return (*__i).second->test();
|
||||
}
|
||||
|
||||
/*
|
||||
<input>
|
||||
<name>Foo</name>
|
||||
<condition>
|
||||
<and>...</and>
|
||||
</condition>
|
||||
</input>
|
||||
<output>
|
||||
<name>Bar</name>
|
||||
<property>/foo/bar</property>
|
||||
<inverted>true</inverted>
|
||||
</output>
|
||||
<output>/some/property</output>
|
||||
*/
|
||||
bool DigitalComponent::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if (cfg_name == "input") {
|
||||
SGPropertyNode_ptr nameNode = cfg_node.getNode("name");
|
||||
string name;
|
||||
if( nameNode != NULL ) {
|
||||
name = nameNode->getStringValue();
|
||||
} else {
|
||||
std::ostringstream buf;
|
||||
buf << "Input" << _input.size();
|
||||
name = buf.str();
|
||||
}
|
||||
_input[name] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "output") {
|
||||
SGPropertyNode_ptr n = cfg_node.getNode("name");
|
||||
string name;
|
||||
if( n != NULL ) {
|
||||
name = n->getStringValue();
|
||||
} else {
|
||||
std::ostringstream buf;
|
||||
buf << "Output" << _output.size();
|
||||
name = buf.str();
|
||||
}
|
||||
|
||||
DigitalOutput_ptr o = new DigitalOutput();
|
||||
_output[name] = o;
|
||||
|
||||
if( (n = cfg_node.getNode("inverted")) != NULL )
|
||||
o->setInverted( n->getBoolValue() );
|
||||
|
||||
if( (n = cfg_node.getNode("property")) != NULL ) {
|
||||
const auto trimmed = simgear::strutils::strip(n->getStringValue());
|
||||
o->setProperty( prop_root.getNode(trimmed, true) );
|
||||
}
|
||||
|
||||
if( cfg_node.nChildren() == 0 ) {
|
||||
const auto trimmed = simgear::strutils::strip(cfg_node.getStringValue());
|
||||
o->setProperty( prop_root.getNode(trimmed, true) );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "inverted") {
|
||||
_inverted = cfg_node.getBoolValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return Component::configure(cfg_node, cfg_name, prop_root);
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
// digitalcomponent.hxx - Base class for digital autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __DIGITALCOMPONENT_HXX
|
||||
#define __DIGITALCOMPONENT_HXX 1
|
||||
|
||||
#include "component.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/props/condition.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Models a digital output bound to a property. May be an inverted output.
|
||||
*/
|
||||
class DigitalOutput : public SGReferenced {
|
||||
|
||||
private:
|
||||
bool _inverted;
|
||||
SGPropertyNode_ptr _node;
|
||||
|
||||
protected:
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an empty, noninverting output
|
||||
*/
|
||||
DigitalOutput();
|
||||
|
||||
inline void setProperty( SGPropertyNode_ptr node );
|
||||
|
||||
inline void setInverted( bool value ) { _inverted = value; }
|
||||
inline bool isInverted() const { return _inverted; }
|
||||
|
||||
bool getValue() const;
|
||||
void setValue( bool value );
|
||||
};
|
||||
|
||||
inline DigitalOutput::DigitalOutput() : _inverted(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline void DigitalOutput::setProperty( SGPropertyNode_ptr node )
|
||||
{
|
||||
_node = node;
|
||||
_node->setBoolValue( node->getBoolValue() );
|
||||
}
|
||||
|
||||
inline bool DigitalOutput::getValue() const
|
||||
{
|
||||
if( _node == NULL ) return false;
|
||||
bool nodeState = _node->getBoolValue();
|
||||
return _inverted ? !nodeState : nodeState;
|
||||
}
|
||||
|
||||
inline void DigitalOutput::setValue( bool value )
|
||||
{
|
||||
if( _node == NULL ) return;
|
||||
_node->setBoolValue( _inverted ? !value : value );
|
||||
}
|
||||
|
||||
typedef SGSharedPtr<DigitalOutput> DigitalOutput_ptr;
|
||||
|
||||
/**
|
||||
* @brief Base class for digital autopilot components
|
||||
*
|
||||
* Each digital component has (at least)
|
||||
* <ul>
|
||||
* <li>one value input</li>
|
||||
* <li>any number of output properties</li>
|
||||
* </ul>
|
||||
*/
|
||||
class DigitalComponent : public Component
|
||||
{
|
||||
public:
|
||||
DigitalComponent();
|
||||
|
||||
class InputMap : public std::map<const std::string,SGSharedPtr<const SGCondition> >
|
||||
{
|
||||
public:
|
||||
bool get_value( const std::string & name ) const;
|
||||
};
|
||||
|
||||
|
||||
// typedef std::map<const std::string,SGSharedPtr<const SGCondition> > InputMap;
|
||||
typedef std::map<const std::string,DigitalOutput_ptr> OutputMap;
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief Named input "pins"
|
||||
*/
|
||||
InputMap _input;
|
||||
|
||||
/**
|
||||
* @brief Named output "pins"
|
||||
*/
|
||||
OutputMap _output;
|
||||
|
||||
/**
|
||||
* @brief Global "inverted" flag for the outputs
|
||||
*/
|
||||
bool _inverted;
|
||||
|
||||
/**
|
||||
* @brief Over-rideable hook method to allow derived classes to refine top-level
|
||||
* node parsing.
|
||||
* @param cfg_node
|
||||
* @param cfg_name
|
||||
* @param prop_root
|
||||
* @return true if the node was handled, false otherwise.
|
||||
*/
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
};
|
||||
|
||||
}
|
||||
#endif // DIGITALCOMPONENT_HXX
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,64 @@
|
||||
// digitalfilter.hxx - a selection of digital filters
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "analogcomponent.hxx"
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* brief@ DigitalFilter - a selection of digital filters
|
||||
*
|
||||
*/
|
||||
class DigitalFilter : public AnalogComponent
|
||||
{
|
||||
private:
|
||||
SGSharedPtr<class DigitalFilterImplementation> _implementation;
|
||||
|
||||
enum InitializeTo {
|
||||
INITIALIZE_OUTPUT,
|
||||
INITIALIZE_INPUT,
|
||||
INITIALIZE_NONE
|
||||
};
|
||||
|
||||
protected:
|
||||
bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root ) override;
|
||||
void update( bool firstTime, double dt) override;
|
||||
|
||||
InitializeTo _initializeTo = INITIALIZE_INPUT;
|
||||
|
||||
public:
|
||||
DigitalFilter();
|
||||
~DigitalFilter();
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "filter"; }
|
||||
|
||||
virtual bool configure( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg );
|
||||
};
|
||||
|
||||
} // namespace FGXMLAutopilot
|
||||
|
||||
@@ -0,0 +1,489 @@
|
||||
// flipflop.hxx - implementation of multiple flip flop types
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "flipflop.hxx"
|
||||
#include "functor.hxx"
|
||||
#include "inputvalue.hxx"
|
||||
#include <Main/fg_props.hxx>
|
||||
|
||||
using std::map;
|
||||
using std::string;
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Flip flop implementation for a RS flip flop with dominant RESET
|
||||
*
|
||||
* RS (reset-set) flip flops act as a fundamental latch. It has two input lines,
|
||||
* S (set) and R (reset). Activating the set input sets the output while activating
|
||||
* the reset input resets the output. If both inputs are activated, the output
|
||||
* is deactivated, too. This is why the RESET line is called dominant. Use a
|
||||
* SRFlipFlopImplementation for a dominant SET line.
|
||||
*
|
||||
* <table>
|
||||
* <tr>
|
||||
* <td colspan="3">Logictable</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>S</td><td>R</td><td>Q</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>unchanged</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>true</td><td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td><td>false</td><td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td><td>true</td><td>false</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
class RSFlipFlopImplementation : public FlipFlopImplementation {
|
||||
protected:
|
||||
bool _rIsDominant;
|
||||
public:
|
||||
RSFlipFlopImplementation( bool rIsDominant = true ) : _rIsDominant( rIsDominant ) {}
|
||||
virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Flip flop implementation for a RS flip flop with dominant SET
|
||||
*
|
||||
* SR (set-reset) flip flops act as a fundamental latch. It has two input lines,
|
||||
* S (set) and R (reset). Activating the set input sets the output while activating
|
||||
* the reset input resets the output. If both inputs are activated, the output
|
||||
* is activated, too. This is why the SET line is called dominant. Use a
|
||||
* RSFlipFlopImplementation for a dominant RESET line.
|
||||
*
|
||||
* <table>
|
||||
* <tr>
|
||||
* <td colspan="3">Logictable</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>S</td><td>R</td><td>Q</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>unchanged</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>true</td><td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td><td>false</td><td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td><td>true</td><td>true</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
class SRFlipFlopImplementation : public RSFlipFlopImplementation {
|
||||
public:
|
||||
SRFlipFlopImplementation() : RSFlipFlopImplementation( false ) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Base class for clocked flip flop implementation
|
||||
*
|
||||
* A clocked flip flop computes it's output on the raising edge (false/true transition)
|
||||
* of the clock input. If such a transition is detected, the onRaisingEdge method is called
|
||||
* by this implementation. All clocked flip flops inherit from the RS flip flop and may
|
||||
* be set or reset by the respective set/reset lines. Note that the RS implementation
|
||||
* ignores the clock, The output is set immediately, regardless of the state of the clock
|
||||
* input. The "clock" input is mandatory for clocked flip flops.
|
||||
*
|
||||
*/
|
||||
class ClockedFlipFlopImplementation : public RSFlipFlopImplementation {
|
||||
private:
|
||||
/**
|
||||
* @brief the previous state of the clock input
|
||||
*/
|
||||
bool _clock;
|
||||
protected:
|
||||
|
||||
/**
|
||||
* @brief pure virtual function to be implemented from the implementing class, gets called
|
||||
* from the update method if the raising edge of the clock input was detected.
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) = 0;
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief constructor for a ClockedFlipFlopImplementation
|
||||
* @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
|
||||
*/
|
||||
ClockedFlipFlopImplementation( bool rIsDominant = true ) : RSFlipFlopImplementation( rIsDominant ), _clock(false) {}
|
||||
|
||||
/**
|
||||
* @brief evaluates the output state from the input lines.
|
||||
* This method basically waits for a raising edge and calls onRaisingEdge
|
||||
* @param dt the elapsed time in seconds from since the last call
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a JK flip flop as a clocked flip flop
|
||||
*
|
||||
* The JK flip flop has five input lines: R, S, clock, J and K. The R and S lines work as described
|
||||
* in the RS flip flop. Setting the J line to true sets the output to true on the next raising
|
||||
* edge of the clock line. Setting the K line to true sets the output to false on the next raising
|
||||
* edge of the clock line. If both, J and K are true, the output is toggled at with every raising
|
||||
* edge of the clock line.
|
||||
*
|
||||
* Undefined inputs default to false.
|
||||
*
|
||||
* <table>
|
||||
* <tr>
|
||||
* <td colspan="7">Logictable</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>S</td><td>R</td><td>J</td><td>K</td><td>clock</td><td>Q (previous)</td><td>Q</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>false</td><td>false</td><td>any</td><td>any</td><td>unchanged</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>true</td><td>false</td><td>any</td><td>any</td><td>any</td><td>any</td><td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>any</td><td>true</td><td>any</td><td>any</td><td>any</td><td>any</td><td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>true</td><td>false</td><td>^</td><td>any</td><td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>false</td><td>true</td><td>^</td><td>any</td><td>false</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>false</td><td>true</td>
|
||||
* </tr>
|
||||
* <tr>
|
||||
* <td>false</td><td>false</td><td>true</td><td>true</td><td>^</td><td>true</td><td>false</td>
|
||||
* </tr>
|
||||
* </table>
|
||||
*/
|
||||
class JKFlipFlopImplementation : public ClockedFlipFlopImplementation {
|
||||
public:
|
||||
/**
|
||||
* @brief constructor for a JKFlipFlopImplementation
|
||||
* @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
|
||||
*/
|
||||
JKFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
|
||||
|
||||
/**
|
||||
* @brief compute the output state according to the logic table on the raising edge of the clock
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a D (delay) flip flop.
|
||||
*
|
||||
*/
|
||||
class DFlipFlopImplementation : public ClockedFlipFlopImplementation {
|
||||
public:
|
||||
/**
|
||||
* @brief constructor for a DFlipFlopImplementation
|
||||
* @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
|
||||
*/
|
||||
DFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
|
||||
|
||||
/**
|
||||
* @brief compute the output state according to the logic table on the raising edge of the clock
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
|
||||
q = input.get_value("D");
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a T (toggle) flip flop.
|
||||
*
|
||||
*/
|
||||
class TFlipFlopImplementation : public ClockedFlipFlopImplementation {
|
||||
public:
|
||||
/**
|
||||
* @brief constructor for a TFlipFlopImplementation
|
||||
* @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
|
||||
*/
|
||||
TFlipFlopImplementation( bool rIsDominant = true ) : ClockedFlipFlopImplementation ( rIsDominant ) {}
|
||||
|
||||
/**
|
||||
* @brief compute the output state according to the logic table on the raising edge of the clock
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool onRaisingEdge( DigitalComponent::InputMap input, bool & q ) {
|
||||
q = !q;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Implements a monostable flip flop
|
||||
*
|
||||
* The stable output state is false.
|
||||
*
|
||||
*/
|
||||
class MonoFlopImplementation : public JKFlipFlopImplementation {
|
||||
protected:
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
InputValueList _time;
|
||||
double _t;
|
||||
public:
|
||||
/**
|
||||
* @brief constructor for a MonoFlopImplementation
|
||||
* @param rIsDominant boolean flag to signal if RESET shall be dominant (true) or SET shall be dominant (false)
|
||||
*/
|
||||
MonoFlopImplementation( bool rIsDominant = true ) : JKFlipFlopImplementation( rIsDominant ), _t(0.0) {}
|
||||
/**
|
||||
* @brief evaluates the output state from the input lines and returns to the stable state
|
||||
* after expiry of the internal timer
|
||||
* @param dt the elapsed time in seconds from since the last call
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q );
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool MonoFlopImplementation::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( JKFlipFlopImplementation::configure(cfg_node, cfg_name, prop_root) )
|
||||
return true;
|
||||
|
||||
if (cfg_name == "time") {
|
||||
_time.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MonoFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
|
||||
{
|
||||
if( JKFlipFlopImplementation::getState( dt, input, q ) ) {
|
||||
_t = q ? _time.get_value() : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
_t -= dt;
|
||||
if( _t <= 0.0 ) {
|
||||
q = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool RSFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
|
||||
{
|
||||
bool s = input.get_value("S");
|
||||
bool r = input.get_value("R");
|
||||
|
||||
// s == false && q == false: no change, keep state
|
||||
if( s || r ) {
|
||||
if( _rIsDominant ) { // RS: reset is dominant
|
||||
if( s ) q = true; // set
|
||||
if( r ) q = false; // reset
|
||||
} else { // SR: set is dominant
|
||||
if( r ) q = false; // reset
|
||||
if( s ) q = true; // set
|
||||
}
|
||||
return true; // signal state changed
|
||||
}
|
||||
return false; // signal state unchagned
|
||||
}
|
||||
|
||||
bool ClockedFlipFlopImplementation::getState( double dt, DigitalComponent::InputMap input, bool & q )
|
||||
{
|
||||
bool c = input.get_value("clock");
|
||||
bool raisingEdge = c && !_clock;
|
||||
|
||||
_clock = c;
|
||||
|
||||
if( RSFlipFlopImplementation::getState( dt, input, q ) )
|
||||
return true;
|
||||
|
||||
|
||||
if( !raisingEdge ) return false; //signal no change
|
||||
return onRaisingEdge( input, q );
|
||||
}
|
||||
|
||||
bool JKFlipFlopImplementation::onRaisingEdge( DigitalComponent::InputMap input, bool & q )
|
||||
{
|
||||
bool j = input.get_value("J");
|
||||
bool k = input.get_value("K");
|
||||
|
||||
// j == false && k == false: no change, keep state
|
||||
if( (j || k) ) {
|
||||
if( j && k ) {
|
||||
q = !q; // toggle
|
||||
} else {
|
||||
if( j ) q = true; // set
|
||||
if( k ) q = false; // reset
|
||||
}
|
||||
return true; // signal state changed
|
||||
}
|
||||
|
||||
return false; // signal no change
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool FlipFlopImplementation::configure( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg )
|
||||
{
|
||||
for( int i = 0; i < cfg.nChildren(); ++i )
|
||||
{
|
||||
SGPropertyNode_ptr child = cfg.getChild(i);
|
||||
string cname(child->getNameString());
|
||||
|
||||
if( configure(*child, cname, prop_root) )
|
||||
continue;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static map<string,FunctorBase<FlipFlopImplementation> *> componentForge;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool FlipFlop::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( componentForge.empty() ) {
|
||||
componentForge["RS"] = new CreateAndConfigureFunctor<RSFlipFlopImplementation,FlipFlopImplementation>();
|
||||
componentForge["SR"] = new CreateAndConfigureFunctor<SRFlipFlopImplementation,FlipFlopImplementation>();
|
||||
componentForge["JK"] = new CreateAndConfigureFunctor<JKFlipFlopImplementation,FlipFlopImplementation>();
|
||||
componentForge["D"] = new CreateAndConfigureFunctor<DFlipFlopImplementation, FlipFlopImplementation>();
|
||||
componentForge["T"] = new CreateAndConfigureFunctor<TFlipFlopImplementation, FlipFlopImplementation>();
|
||||
componentForge["monostable"] = new CreateAndConfigureFunctor<MonoFlopImplementation, FlipFlopImplementation>();
|
||||
}
|
||||
|
||||
if( DigitalComponent::configure(cfg_node, cfg_name, prop_root) )
|
||||
return true;
|
||||
|
||||
if( cfg_name == "type" ) {
|
||||
string type(cfg_node.getStringValue());
|
||||
if( componentForge.count(type) == 0 ) {
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_BULK,
|
||||
"unhandled flip-flop type <" << type << ">"
|
||||
);
|
||||
return true;
|
||||
}
|
||||
_implementation = (*componentForge[type])(prop_root, *cfg_node.getParent());
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "set"||cfg_name == "S") {
|
||||
_input["S"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "reset" || cfg_name == "R" ) {
|
||||
_input["R"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "J") {
|
||||
_input["J"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "K") {
|
||||
_input["K"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "D") {
|
||||
_input["D"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "clock") {
|
||||
_input["clock"] = sgReadCondition(&prop_root, &cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FlipFlop::update( bool firstTime, double dt )
|
||||
{
|
||||
if( _implementation == NULL ) {
|
||||
SG_LOG( SG_AUTOPILOT, SG_ALERT, "No flip-flop implementation for " << subsystemId() << endl );
|
||||
return;
|
||||
}
|
||||
|
||||
bool q0, q;
|
||||
|
||||
q0 = q = get_output();
|
||||
|
||||
if( _implementation->getState( dt, _input, q ) && q0 != q ) {
|
||||
set_output( q );
|
||||
|
||||
if(_debug) {
|
||||
cout << "updating flip-flop \"" << subsystemId() << "\"" << endl;
|
||||
cout << "prev. Output:" << q0 << endl;
|
||||
for( InputMap::const_iterator it = _input.begin(); it != _input.end(); ++it )
|
||||
cout << "Input \"" << (*it).first << "\":" << (*it).second->test() << endl;
|
||||
cout << "new Output:" << q << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<FlipFlop> registrantFlipFlop;
|
||||
@@ -0,0 +1,102 @@
|
||||
// flipflop.hxx - implementation of multiple flip flop types
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __FLIPFLOPCOMPONENT_HXX
|
||||
#define __FLIPFLOPCOMPONENT_HXX 1
|
||||
|
||||
#include "logic.hxx"
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Interface for a flip flop implementation. Can be configured from a property node and
|
||||
* returns a state depending on input lines.
|
||||
*/
|
||||
class FlipFlopImplementation : public SGReferenced {
|
||||
protected:
|
||||
/**
|
||||
* @brief configure this component from a property node. Iterates through all nodes found
|
||||
* as childs under configNode and calls configure of the derived class for each child.
|
||||
* @param configNode the property node containing the configuration
|
||||
*/
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{ return false; }
|
||||
public:
|
||||
virtual ~FlipFlopImplementation() {}
|
||||
/**
|
||||
* @brief evaluates the output state from the input lines
|
||||
* @param dt the elapsed time in seconds from since the last call
|
||||
* @param input a map of named input lines
|
||||
* @param q a reference to a boolean variable to receive the output state
|
||||
* @return true if the state has changed, false otherwise
|
||||
*/
|
||||
virtual bool getState( double dt, DigitalComponent::InputMap input, bool & q ) { return false; }
|
||||
|
||||
/**
|
||||
* @brief configure this component from a property node. Iterates through all nodes found
|
||||
* as childs under configNode and calls configure of the derived class for each child.
|
||||
* @param configNode the property node containing the configuration
|
||||
*/
|
||||
bool configure( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg );
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A simple flipflop implementation
|
||||
*/
|
||||
class FlipFlop : public Logic
|
||||
{
|
||||
public:
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "flipflop"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Over-rideable hook method to allow derived classes to refine top-level
|
||||
* node parsing.
|
||||
* @param aName
|
||||
* @param aNode
|
||||
* @return true if the node was handled, false otherwise.
|
||||
*/
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
|
||||
/**
|
||||
* @brief Implementation of the pure virtual function of the Component class. Gets called from
|
||||
* the update method if it's not disabled with the firstTime parameter set to true if this
|
||||
* is the first call after being enabled
|
||||
* @param firstTime set to true if this is the first update call since this component has
|
||||
been enabled. Set to false for every subsequent call.
|
||||
* @param dt the elapsed time since the last call
|
||||
*/
|
||||
void update( bool firstTime, double dt );
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Pointer to the actual flip flop implementation
|
||||
*/
|
||||
SGSharedPtr<FlipFlopImplementation> _implementation;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // FLIPFLOPCOMPONENT_HXX
|
||||
@@ -0,0 +1,53 @@
|
||||
// functor.hxx - a utility to create object based on names
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __FUNCTOR_HXX
|
||||
#define __FUNCTOR_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
template <class TBase> class FunctorBase {
|
||||
public:
|
||||
virtual ~FunctorBase() {}
|
||||
virtual TBase * operator()( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg ) = 0;
|
||||
};
|
||||
|
||||
template <class TClass,class TBase> class CreateAndConfigureFunctor :
|
||||
public FunctorBase<TBase> {
|
||||
public:
|
||||
virtual TBase * operator()( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg )
|
||||
{
|
||||
TBase * base = new TClass();
|
||||
base->configure(prop_root, cfg);
|
||||
return base;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __FUNCTOR_HXX 1
|
||||
@@ -0,0 +1,310 @@
|
||||
// inputvalue.hxx - provide input to autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include "inputvalue.hxx"
|
||||
|
||||
#include <simgear/misc/strutils.hxx>
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
PeriodicalValue::PeriodicalValue( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg )
|
||||
{
|
||||
SGPropertyNode_ptr minNode = cfg.getChild( "min" );
|
||||
SGPropertyNode_ptr maxNode = cfg.getChild( "max" );
|
||||
if( !minNode || !maxNode )
|
||||
{
|
||||
SG_LOG
|
||||
(
|
||||
SG_AUTOPILOT,
|
||||
SG_ALERT,
|
||||
"periodical defined, but no <min> and/or <max> tag. Period ignored."
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
minPeriod = new InputValue(prop_root, *minNode);
|
||||
maxPeriod = new InputValue(prop_root, *maxNode);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
double PeriodicalValue::normalize( double value ) const
|
||||
{
|
||||
return SGMiscd::normalizePeriodic( minPeriod->get_value(),
|
||||
maxPeriod->get_value(),
|
||||
value );
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
double PeriodicalValue::normalizeSymmetric( double value ) const
|
||||
{
|
||||
double minValue = minPeriod->get_value();
|
||||
double maxValue = maxPeriod->get_value();
|
||||
|
||||
value = SGMiscd::normalizePeriodic( minValue, maxValue, value );
|
||||
double width_2 = (maxValue - minValue)/2;
|
||||
return value > width_2 ? width_2 - value : value;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
InputValue::InputValue( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg,
|
||||
double value,
|
||||
double offset,
|
||||
double scale ):
|
||||
_value(0.0),
|
||||
_abs(false)
|
||||
{
|
||||
parse(prop_root, cfg, value, offset, scale);
|
||||
}
|
||||
|
||||
InputValue::~InputValue()
|
||||
{
|
||||
if (_pathNode) {
|
||||
_pathNode->removeChangeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
void InputValue::initPropertyFromInitialValue()
|
||||
{
|
||||
double s = get_scale();
|
||||
if( s != 0 )
|
||||
_property->setDoubleValue( (_value - get_offset())/s );
|
||||
else
|
||||
_property->setDoubleValue(0); // if scale is zero, value*scale is zero
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void InputValue::parse( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg,
|
||||
double aValue,
|
||||
double aOffset,
|
||||
double aScale )
|
||||
{
|
||||
_value = aValue;
|
||||
_property = NULL;
|
||||
_offset = NULL;
|
||||
_scale = NULL;
|
||||
_min = NULL;
|
||||
_max = NULL;
|
||||
_periodical = NULL;
|
||||
|
||||
SGPropertyNode * n;
|
||||
|
||||
if( (n = cfg.getChild("condition")) != NULL )
|
||||
_condition = sgReadCondition(&prop_root, n);
|
||||
|
||||
if( (n = cfg.getChild( "scale" )) != NULL )
|
||||
_scale = new InputValue(prop_root, *n, aScale);
|
||||
|
||||
if( (n = cfg.getChild( "offset" )) != NULL )
|
||||
_offset = new InputValue(prop_root, *n, aOffset);
|
||||
|
||||
if( (n = cfg.getChild( "max" )) != NULL )
|
||||
_max = new InputValue(prop_root, *n);
|
||||
|
||||
if( (n = cfg.getChild( "min" )) != NULL )
|
||||
_min = new InputValue(prop_root, *n);
|
||||
|
||||
if( (n = cfg.getChild( "abs" )) != NULL )
|
||||
_abs = n->getBoolValue();
|
||||
|
||||
if( (n = cfg.getChild( "period" )) != NULL )
|
||||
_periodical = new PeriodicalValue(prop_root, *n);
|
||||
|
||||
|
||||
SGPropertyNode *valueNode = cfg.getChild("value");
|
||||
if( valueNode != NULL )
|
||||
_value = valueNode->getDoubleValue();
|
||||
|
||||
if( (n = cfg.getChild("expression")) != NULL )
|
||||
{
|
||||
_expression = SGReadDoubleExpression(&prop_root, n->getChild(0));
|
||||
return;
|
||||
}
|
||||
|
||||
if ((n = cfg.getChild("property-path"))) {
|
||||
// cache the root node, in case of changes
|
||||
_rootNode = &prop_root;
|
||||
const auto trimmed = simgear::strutils::strip(n->getStringValue());
|
||||
_pathNode = prop_root.getNode(trimmed, true);
|
||||
_pathNode->addChangeListener(this);
|
||||
|
||||
// if <property> is defined, should we use it to initialise
|
||||
// the path prop? not doing so for now.
|
||||
|
||||
const auto path = simgear::strutils::strip(_pathNode->getStringValue());
|
||||
if (!path.empty()) {
|
||||
_property = _rootNode->getNode(path);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if no <property> element, check for <prop> element for backwards
|
||||
// compatibility
|
||||
if( (n = cfg.getChild("property"))
|
||||
|| (n = cfg.getChild("prop" )) )
|
||||
{
|
||||
// tolerate leading & trailing whitespace from XML, in the property name
|
||||
const auto trimmed = simgear::strutils::strip(n->getStringValue());
|
||||
_property = prop_root.getNode(trimmed, true);
|
||||
if( valueNode )
|
||||
{
|
||||
initPropertyFromInitialValue();
|
||||
}
|
||||
|
||||
return;
|
||||
} // of have a <property> or <prop>
|
||||
|
||||
if( !valueNode )
|
||||
{
|
||||
// no <value>, <prop> or <expression> element, use text node
|
||||
std::string textnode = cfg.getStringValue();
|
||||
char * endp = NULL;
|
||||
// try to convert to a double value. If the textnode does not start with a number
|
||||
// endp will point to the beginning of the string. We assume this should be
|
||||
// a property name
|
||||
_value = strtod( textnode.c_str(), &endp );
|
||||
if( endp == textnode.c_str() )
|
||||
_property = prop_root.getNode(textnode, true);
|
||||
}
|
||||
}
|
||||
|
||||
void InputValue::set_value( double aValue )
|
||||
{
|
||||
if (!_property)
|
||||
return;
|
||||
|
||||
double s = get_scale();
|
||||
if( s != 0 )
|
||||
_property->setDoubleValue( (aValue - get_offset())/s );
|
||||
else
|
||||
_property->setDoubleValue( 0 ); // if scale is zero, value*scale is zero
|
||||
}
|
||||
|
||||
double InputValue::get_value() const
|
||||
{
|
||||
double value = _value;
|
||||
|
||||
if (_expression) {
|
||||
// compute the expression value
|
||||
value = _expression->getValue(NULL);
|
||||
|
||||
if (SGMiscd::isNaN(value)) {
|
||||
SG_LOG(SG_AUTOPILOT, SG_DEV_ALERT, "AP input: read NaN from expression");
|
||||
}
|
||||
} else if( _property != NULL ) {
|
||||
value = _property->getDoubleValue();
|
||||
|
||||
if (SGMiscd::isNaN(value)) {
|
||||
SG_LOG(SG_AUTOPILOT, SG_DEV_ALERT, "AP input: read NaN from:" << _property->getPath() );
|
||||
}
|
||||
} else {
|
||||
if (SGMiscd::isNaN(value)) {
|
||||
SG_LOG(SG_AUTOPILOT, SG_DEV_ALERT, "AP input is NaN." );
|
||||
}
|
||||
}
|
||||
|
||||
if( _scale )
|
||||
value *= _scale->get_value();
|
||||
|
||||
if( _offset )
|
||||
value += _offset->get_value();
|
||||
|
||||
if( _min ) {
|
||||
double m = _min->get_value();
|
||||
if( value < m )
|
||||
value = m;
|
||||
}
|
||||
|
||||
if( _max ) {
|
||||
double m = _max->get_value();
|
||||
if( value > m )
|
||||
value = m;
|
||||
}
|
||||
|
||||
if( _periodical ) {
|
||||
value = _periodical->normalize( value );
|
||||
}
|
||||
|
||||
return _abs ? fabs(value) : value;
|
||||
}
|
||||
|
||||
bool InputValue::is_enabled() const
|
||||
{
|
||||
if (_pathNode && !_property) {
|
||||
// if we have a configurable path, and it's currently not valid,
|
||||
// mark ourselves as disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_condition) {
|
||||
return _condition->test();
|
||||
}
|
||||
|
||||
return true; // default to enab;ed
|
||||
}
|
||||
|
||||
void InputValue::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
|
||||
{
|
||||
if (_property) props.insert(_property);
|
||||
if (_offset) _offset->collectDependentProperties(props);
|
||||
if (_scale) _scale->collectDependentProperties(props);
|
||||
if (_min) _min->collectDependentProperties(props);
|
||||
if (_max) _max->collectDependentProperties(props);
|
||||
if (_expression) _expression->collectDependentProperties(props);
|
||||
if (_pathNode) props.insert(_pathNode);
|
||||
}
|
||||
|
||||
void InputValue::valueChanged(SGPropertyNode *node)
|
||||
{
|
||||
assert(node == _pathNode);
|
||||
const auto path = simgear::strutils::strip(_pathNode->getStringValue());
|
||||
if (path.empty()) {
|
||||
// don't consider an empty string to mean the root node, that's not
|
||||
// useful behaviour
|
||||
_property.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
// important we don't create here: this allows an invalid path
|
||||
// to give us a null _property, which causes us to be marked as
|
||||
// disabled, allowing another input to be used
|
||||
auto propNode = _rootNode->getNode(path);
|
||||
if (propNode) {
|
||||
_property = propNode;
|
||||
} else {
|
||||
_property.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void InputValueList::collectDependentProperties(std::set<const SGPropertyNode*>& props) const
|
||||
{
|
||||
for (auto& iv: *this) {
|
||||
iv->collectDependentProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
// inputvalue.hxx - provide input to autopilot components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#ifndef _INPUTVALUE_HXX
|
||||
#define _INPUTVALUE_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include <simgear/structure/SGExpression.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
typedef SGSharedPtr<class InputValue> InputValue_ptr;
|
||||
typedef SGSharedPtr<class PeriodicalValue> PeriodicalValue_ptr;
|
||||
|
||||
/**
|
||||
* @brief Model a periodical value like angular values
|
||||
*
|
||||
* Most common use for periodical values are angular values.
|
||||
* If y = f(x) = f(x + n*period), this is a periodical function
|
||||
*/
|
||||
class PeriodicalValue : public SGReferenced {
|
||||
private:
|
||||
InputValue_ptr minPeriod; // The minimum value of the period
|
||||
InputValue_ptr maxPeriod; // The maximum value of the period
|
||||
public:
|
||||
PeriodicalValue( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg );
|
||||
double normalize( double value ) const;
|
||||
double normalizeSymmetric( double value ) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A input value for analog autopilot components
|
||||
*
|
||||
* Input values may be constants, property values, transformed with a scale
|
||||
* and/or offset, clamped to min/max values, be periodical, bound to
|
||||
* conditions or evaluated from expressions.
|
||||
*/
|
||||
class InputValue : public SGReferenced, public SGPropertyChangeListener {
|
||||
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
|
||||
InputValue_ptr _offset; // A fixed offset, defaults to zero
|
||||
InputValue_ptr _scale; // A constant scaling factor defaults to one
|
||||
InputValue_ptr _min; // A minimum clip defaults to no clipping
|
||||
InputValue_ptr _max; // A maximum clip defaults to no clipping
|
||||
PeriodicalValue_ptr _periodical; //
|
||||
SGSharedPtr<const SGCondition> _condition;
|
||||
SGSharedPtr<SGExpressiond> _expression; ///< expression to generate the value
|
||||
SGPropertyNode_ptr _pathNode;
|
||||
SGPropertyNode_ptr _rootNode;
|
||||
|
||||
void valueChanged(SGPropertyNode* node) override;
|
||||
void initPropertyFromInitialValue();
|
||||
public:
|
||||
InputValue( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& node,
|
||||
double value = 0.0,
|
||||
double offset = 0.0,
|
||||
double scale = 1.0 );
|
||||
~InputValue();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param prop_root Root node for all properties with relative path
|
||||
* @param cfg Configuration node
|
||||
* @param value Default initial value
|
||||
* @param offset Default initial offset
|
||||
* @param scale Default initial scale
|
||||
*/
|
||||
void parse( SGPropertyNode& prop_root,
|
||||
SGPropertyNode& cfg,
|
||||
double value = 0.0,
|
||||
double offset = 0.0,
|
||||
double scale = 1.0 );
|
||||
|
||||
/* get the value of this input, apply scale and offset and clipping */
|
||||
double get_value() const;
|
||||
|
||||
/* set the input value after applying offset and scale */
|
||||
void set_value( double value );
|
||||
|
||||
inline double get_scale() const {
|
||||
return _scale == NULL ? 1.0 : _scale->get_value();
|
||||
}
|
||||
|
||||
inline double get_offset() const {
|
||||
return _offset == NULL ? 0.0 : _offset->get_value();
|
||||
}
|
||||
|
||||
bool is_enabled() const;
|
||||
|
||||
void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A chained list of InputValues
|
||||
*
|
||||
* Many compoments support InputValueLists as input. Each InputValue may be bound to
|
||||
* a condition. This list supports the get_value() function to retrieve the value
|
||||
* of the first InputValue in this list that has a condition evaluating to true.
|
||||
*/
|
||||
class InputValueList : public std::vector<InputValue_ptr> {
|
||||
public:
|
||||
InputValueList( double def = 0.0 ) : _def(def) { }
|
||||
|
||||
InputValue_ptr get_active() const {
|
||||
for (const_iterator it = begin(); it != end(); ++it) {
|
||||
if( (*it)->is_enabled() )
|
||||
return *it;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
double get_value() const {
|
||||
InputValue_ptr input = get_active();
|
||||
return input == NULL ? _def : input->get_value();
|
||||
}
|
||||
|
||||
void collectDependentProperties(std::set<const SGPropertyNode*>& props) const;
|
||||
private:
|
||||
|
||||
double _def;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
// logic.cxx - Base class for logic components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
|
||||
// for some obscure reason, MSVC needs this to compile
|
||||
#ifdef _MSC_VER
|
||||
#ifndef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "logic.hxx"
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
bool Logic::get_input() const
|
||||
{
|
||||
// return state of first configured condition
|
||||
InputMap::const_iterator it = _input.begin();
|
||||
if( it == _input.end() ) return false; // no inputs?
|
||||
return (*it).second->test();
|
||||
}
|
||||
|
||||
void Logic::set_output( bool value )
|
||||
{
|
||||
// respect global inverted flag
|
||||
if( _inverted ) value = !value;
|
||||
|
||||
// set all outputs to the given value
|
||||
for( OutputMap::iterator it = _output.begin(); it != _output.end(); ++it )
|
||||
(*it).second->setValue( value );
|
||||
}
|
||||
|
||||
bool Logic::get_output() const
|
||||
{
|
||||
OutputMap::const_iterator it = _output.begin();
|
||||
bool q = it != _output.end() ? (*it).second->getValue() : false;
|
||||
return _inverted ? !q : q;
|
||||
}
|
||||
|
||||
void Logic::update( bool firstTime, double dt )
|
||||
{
|
||||
if(_debug) {
|
||||
bool q = get_output();
|
||||
bool a = get_input();
|
||||
if( a != q ) {
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
cout << "updating logic \"" << subsystemId() << "\"" << endl;
|
||||
cout << "prev. Output:" << q << endl;
|
||||
cout << "new Output:" << a << endl;
|
||||
}
|
||||
}
|
||||
set_output( get_input() );
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<Logic> registrantLogic;
|
||||
@@ -0,0 +1,51 @@
|
||||
// logic.hxx - Base class for logic components
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
//
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __LOGICCOMPONENT_HXX
|
||||
#define __LOGICCOMPONENT_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "digitalcomponent.hxx"
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief A simple logic class writing <condition> to a property
|
||||
*/
|
||||
class Logic : public DigitalComponent
|
||||
{
|
||||
public:
|
||||
bool get_input() const;
|
||||
void set_output( bool value );
|
||||
bool get_output() const;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "logic"; }
|
||||
|
||||
protected:
|
||||
void update( bool firstTime, double dt );
|
||||
};
|
||||
|
||||
}
|
||||
#endif // LOGICCOMPONENT_HXX
|
||||
|
||||
@@ -0,0 +1,298 @@
|
||||
// pidcontroller.cxx - implementation of PID controller
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "pidcontroller.hxx"
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
using std::endl;
|
||||
using std::cout;
|
||||
|
||||
PIDController::PIDController():
|
||||
AnalogComponent(),
|
||||
alpha( 0.1 ),
|
||||
beta( 1.0 ),
|
||||
gamma( 0.0 ),
|
||||
ep_n_1( 0.0 ),
|
||||
edf_n_1( 0.0 ),
|
||||
edf_n_2( 0.0 ),
|
||||
u_n_1( 0.0 ),
|
||||
desiredTs( 0.0 ),
|
||||
elapsedTime( 0.0 ),
|
||||
startup_current( false ),
|
||||
startup_its( 0 ),
|
||||
iteration( 0 )
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Roy Vegard Ovesen:
|
||||
*
|
||||
* Ok! Here is the PID controller algorithm that I would like to see
|
||||
* implemented:
|
||||
*
|
||||
* delta_u_n = Kp * [ (ep_n - ep_n-1) + ((Ts/Ti)*e_n)
|
||||
* + (Td/Ts)*(edf_n - 2*edf_n-1 + edf_n-2) ]
|
||||
*
|
||||
* u_n = u_n-1 + delta_u_n
|
||||
*
|
||||
* where:
|
||||
*
|
||||
* delta_u : The incremental output
|
||||
* Kp : Proportional gain
|
||||
* ep : Proportional error with reference weighing
|
||||
* ep = beta * r - y
|
||||
* where:
|
||||
* beta : Weighing factor
|
||||
* r : Reference (setpoint)
|
||||
* y : Process value, measured
|
||||
* e : Error
|
||||
* e = r - y
|
||||
* Ts : Sampling interval
|
||||
* Ti : Integrator time
|
||||
* Td : Derivator time
|
||||
* edf : Derivate error with reference weighing and filtering
|
||||
* edf_n = edf_n-1 / ((Ts/Tf) + 1) + ed_n * (Ts/Tf) / ((Ts/Tf) + 1)
|
||||
* where:
|
||||
* Tf : Filter time
|
||||
* Tf = alpha * Td , where alpha usually is set to 0.1
|
||||
* ed : Unfiltered derivate error with reference weighing
|
||||
* ed = gamma * r - y
|
||||
* where:
|
||||
* gamma : Weighing factor
|
||||
*
|
||||
* u : absolute output
|
||||
*
|
||||
* Index n means the n'th value.
|
||||
*
|
||||
*
|
||||
* Inputs:
|
||||
* enabled ,
|
||||
* y_n , r_n , beta=1 , gamma=0 , alpha=0.1 ,
|
||||
* Kp , Ti , Td , Ts (is the sampling time available?)
|
||||
* u_min , u_max
|
||||
*
|
||||
* Output:
|
||||
* u_n
|
||||
*/
|
||||
|
||||
void PIDController::update( bool firstTime, double dt )
|
||||
{
|
||||
elapsedTime += dt;
|
||||
if (firstTime) {
|
||||
iteration = 0;
|
||||
/* We always initialise edf_n_1 to zero, regardless of startup_its. */
|
||||
edf_n_1 = 0;
|
||||
}
|
||||
else if (elapsedTime <= desiredTs ) {
|
||||
// do nothing if not enough time has passed.
|
||||
return;
|
||||
}
|
||||
|
||||
iteration += 1;
|
||||
|
||||
/* We are going to do an iteration so reset elapsedTime. */
|
||||
double Ts = elapsedTime;
|
||||
elapsedTime = 0.0;
|
||||
|
||||
/* Read generic things from our AnalogComponent base class. */
|
||||
double y_n = _valueInput.get_value(); // input.
|
||||
double r_n = _referenceInput.get_value(); // reference.
|
||||
double u_min = _minInput.get_value(); // minimum output.
|
||||
double u_max = _maxInput.get_value(); // maximum output.
|
||||
|
||||
/* Read things specific to PIDController. */
|
||||
double td = Td.get_value(); // derivative time.
|
||||
double ti = Ti.get_value(); // (reciprocal?) integral time.
|
||||
|
||||
/*
|
||||
Now do the PID calculations.
|
||||
*/
|
||||
|
||||
double ep_n = beta * r_n - y_n; // proportional error.
|
||||
double e_n = r_n - y_n; // error.
|
||||
double ed_n = gamma * r_n - y_n; // derivate error.
|
||||
double Tf = alpha * td; // filter time.
|
||||
|
||||
double edf_n = 0.0; // derivate error.
|
||||
if (td > 0.0) {
|
||||
edf_n = edf_n_1 / (Ts/Tf + 1)
|
||||
+ ed_n * (Ts/Tf) / (Ts/Tf + 1);
|
||||
}
|
||||
|
||||
if (firstTime) {
|
||||
if (startup_current) {
|
||||
/* Seed our historical state with current values. This avoids spurious
|
||||
large terms in calculation of delta_u_n below. */
|
||||
ep_n_1 = ep_n;
|
||||
edf_n_2 = edf_n;
|
||||
edf_n_1 = edf_n;
|
||||
}
|
||||
else {
|
||||
// Default behaviour.
|
||||
ep_n_1 = 0;
|
||||
edf_n_2 = 0;
|
||||
// edf_n_1 is already set to zero above.
|
||||
}
|
||||
u_n_1 = get_output_value();
|
||||
}
|
||||
|
||||
double delta_u_n = 0.0; // incremental output
|
||||
if ( ti > 0.0 ) {
|
||||
delta_u_n = Kp.get_value() * (
|
||||
(ep_n - ep_n_1)
|
||||
+ ((Ts/ti) * e_n)
|
||||
+ ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
|
||||
);
|
||||
}
|
||||
|
||||
const char* saturation = "";
|
||||
double u_n; // absolute output
|
||||
|
||||
if (iteration > startup_its) {
|
||||
|
||||
/* Update the output, clipping to u_min..u_max. */
|
||||
|
||||
u_n = u_n_1 + delta_u_n;
|
||||
|
||||
if (u_n > u_max) {
|
||||
u_n = u_max;
|
||||
saturation = " max_saturation";
|
||||
}
|
||||
else if (u_n < u_min) {
|
||||
u_n = u_min;
|
||||
saturation = " min_saturation";
|
||||
}
|
||||
set_output_value( u_n );
|
||||
}
|
||||
else {
|
||||
/* Do not change the output. Instead get the current output value for
|
||||
use in setting our historical state below. */
|
||||
if (_debug) {
|
||||
cout << subsystemId()
|
||||
<< ": doing nothing."
|
||||
<< " startup_its=" << startup_its
|
||||
<< " iteration=" << iteration
|
||||
<< std::endl;
|
||||
}
|
||||
u_n = get_output_value();
|
||||
}
|
||||
|
||||
if ( _debug ) {
|
||||
cout
|
||||
<< "Updating " << subsystemId()
|
||||
<< " startup_its=" << startup_its
|
||||
<< " startup_current=" << startup_current
|
||||
<< " firstTime=" << firstTime
|
||||
<< " iteration=" << iteration
|
||||
<< " Ts=" << Ts
|
||||
<< " input=" << y_n
|
||||
<< " ref=" << r_n
|
||||
<< " ep_n=" << ep_n
|
||||
<< " ep_n_1=" << ep_n_1
|
||||
<< " e_n=" << e_n
|
||||
<< " ed_n=" << ed_n
|
||||
<< " Tf=" << Tf
|
||||
<< " edf_n=" << edf_n
|
||||
<< " edf_n_1=" << edf_n_1
|
||||
<< " edf_n_2=" << edf_n_2
|
||||
<< " ti=" << ti
|
||||
<< " delta_u_n=" << delta_u_n
|
||||
<< " P=" << Kp.get_value() * (ep_n - ep_n_1)
|
||||
<< " I=" << Kp.get_value() * ((Ts/ti) * e_n)
|
||||
<< " D=" << Kp.get_value() * ((td/Ts) * (edf_n - 2*edf_n_1 + edf_n_2))
|
||||
<< saturation
|
||||
<< " u_n_1=" << u_n_1
|
||||
<< " delta_u_n=" << delta_u_n
|
||||
<< " output=" << u_n
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
// Updates indexed values;
|
||||
u_n_1 = u_n;
|
||||
ep_n_1 = ep_n;
|
||||
edf_n_2 = edf_n_1;
|
||||
edf_n_1 = edf_n;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool PIDController::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( cfg_name == "config" ) {
|
||||
Component::configure(prop_root, cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Ts") {
|
||||
desiredTs = cfg_node.getDoubleValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Kp") {
|
||||
Kp.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Ti") {
|
||||
Ti.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Td") {
|
||||
Td.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "beta") {
|
||||
beta = cfg_node.getDoubleValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "alpha") {
|
||||
alpha = cfg_node.getDoubleValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "gamma") {
|
||||
gamma = cfg_node.getDoubleValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "startup-its") {
|
||||
startup_its = cfg_node.getIntValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "startup-current") {
|
||||
startup_current = cfg_node.getBoolValue();
|
||||
return true;
|
||||
}
|
||||
|
||||
return AnalogComponent::configure(cfg_node, cfg_name, prop_root);
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<PIDController> registrantPIDController;
|
||||
@@ -0,0 +1,93 @@
|
||||
// pidcontroller.hxx - implementation of PID controller
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __PIDCONTROLLER_HXX
|
||||
#define __PIDCONTROLLER_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "analogcomponent.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* Roy Ovesen's PID controller
|
||||
*/
|
||||
class PIDController : public AnalogComponent
|
||||
{
|
||||
private:
|
||||
// Configuration values
|
||||
InputValueList Kp; // proportional gain
|
||||
InputValueList Ti; // Integrator time (sec)
|
||||
InputValueList Td; // Derivator time (sec)
|
||||
|
||||
double alpha; // low pass filter weighing factor (usually 0.1)
|
||||
double beta; // process value weighing factor for
|
||||
// calculating proportional error
|
||||
// (usually 1.0)
|
||||
double gamma; // process value weighing factor for
|
||||
// calculating derivative error
|
||||
// (usually 0.0)
|
||||
|
||||
// Previous state tracking values
|
||||
double ep_n_1; // ep[n-1] (prop error)
|
||||
double edf_n_1; // edf[n-1] (derivative error)
|
||||
double edf_n_2; // edf[n-2] (derivative error)
|
||||
double u_n_1; // u[n-1] (output)
|
||||
double desiredTs; // desired sampling interval (sec)
|
||||
double elapsedTime; // elapsed time (sec)
|
||||
|
||||
/* If startup_current is false (the default), we initialise internal state
|
||||
variables to zero.
|
||||
|
||||
Otherwise we initialise internal variables to the current values, which can
|
||||
reduce initial transient behaviour. */
|
||||
bool startup_current;
|
||||
|
||||
/* For the first startup_its iterations we don't modify our output. Default
|
||||
is zero. */
|
||||
unsigned startup_its;
|
||||
|
||||
unsigned iteration;
|
||||
|
||||
protected:
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
|
||||
public:
|
||||
PIDController();
|
||||
~PIDController() {}
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "pid-controller"; }
|
||||
|
||||
void update( bool firstTime, double dt ) override;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // __PIDCONTROLLER_HXX
|
||||
@@ -0,0 +1,93 @@
|
||||
// pisimplecontroller.cxx - implementation of a simple PI controller
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "pisimplecontroller.hxx"
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
PISimpleController::PISimpleController() :
|
||||
AnalogComponent(),
|
||||
_int_sum( 0.0 )
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool PISimpleController::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( cfg_name == "config" ) {
|
||||
Component::configure(prop_root, cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Kp") {
|
||||
_Kp.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "Ki") {
|
||||
_Ki.push_back( new InputValue(prop_root, cfg_node) );
|
||||
return true;
|
||||
}
|
||||
|
||||
return AnalogComponent::configure(cfg_node, cfg_name, prop_root);
|
||||
}
|
||||
|
||||
void PISimpleController::update( bool firstTime, double dt )
|
||||
{
|
||||
if ( firstTime ) {
|
||||
// we have just been enabled, zero out int_sum
|
||||
_int_sum = 0.0;
|
||||
}
|
||||
|
||||
if ( _debug ) std::cout << "Updating " << subsystemId() << std::endl;
|
||||
double y_n = _valueInput.get_value();
|
||||
double r_n = _referenceInput.get_value();
|
||||
|
||||
double error = r_n - y_n;
|
||||
if ( _debug ) std::cout << "input = " << y_n
|
||||
<< " reference = " << r_n
|
||||
<< " error = " << error
|
||||
<< std::endl;
|
||||
|
||||
double prop_comp = clamp(error * _Kp.get_value());
|
||||
_int_sum += error * _Ki.get_value() * dt;
|
||||
|
||||
|
||||
double output = prop_comp + _int_sum;
|
||||
double clamped_output = clamp( output );
|
||||
if( output != clamped_output ) // anti-windup
|
||||
_int_sum = clamped_output - prop_comp;
|
||||
|
||||
if ( _debug ) std::cout << "prop_comp = " << prop_comp
|
||||
<< " int_sum = " << _int_sum << std::endl;
|
||||
|
||||
set_output_value( clamped_output );
|
||||
if ( _debug ) std::cout << "output = " << clamped_output << std::endl;
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<PISimpleController> registrantPISimpleController;
|
||||
@@ -0,0 +1,68 @@
|
||||
// pisimplecontroller.hxx - implementation of a simple PI controller
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __PISIMPLECONTROLLER_HXX
|
||||
#define __PISIMPLECONTROLLER_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "analogcomponent.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* A simplistic P [ + I ] PI controller
|
||||
*/
|
||||
class PISimpleController : public AnalogComponent
|
||||
{
|
||||
|
||||
private:
|
||||
// proportional component data
|
||||
InputValueList _Kp;
|
||||
|
||||
// integral component data
|
||||
InputValueList _Ki;
|
||||
double _int_sum;
|
||||
|
||||
protected:
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
|
||||
public:
|
||||
PISimpleController();
|
||||
~PISimpleController() {}
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "pi-simple-controller"; }
|
||||
|
||||
void update( bool firstTime, double dt );
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,83 @@
|
||||
// predictor.cxx - predict future values
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "predictor.hxx"
|
||||
|
||||
using namespace FGXMLAutopilot;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Predictor::Predictor () :
|
||||
AnalogComponent(),
|
||||
_last_value(0),
|
||||
_average(0)
|
||||
{
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Predictor::configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root )
|
||||
{
|
||||
if( cfg_name == "config" ) {
|
||||
Component::configure(prop_root, cfg_node);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "seconds") {
|
||||
_seconds.push_back( new InputValue(prop_root, cfg_node, 0) );
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cfg_name == "filter-gain") {
|
||||
_filter_gain.push_back( new InputValue(prop_root, cfg_node, 0) );
|
||||
return true;
|
||||
}
|
||||
|
||||
return AnalogComponent::configure(cfg_node, cfg_name, prop_root);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Predictor::update( bool firstTime, double dt )
|
||||
{
|
||||
double ivalue = _valueInput.get_value();
|
||||
|
||||
if ( firstTime ) {
|
||||
_last_value = ivalue;
|
||||
}
|
||||
|
||||
double current = (ivalue - _last_value)/dt; // calculate current error change (per second)
|
||||
_average = dt < 1.0 ? ((1.0 - dt) * _average + current * dt) : current;
|
||||
|
||||
// calculate output with filter gain adjustment
|
||||
double output = ivalue +
|
||||
(1.0 - _filter_gain.get_value()) * (_average * _seconds.get_value()) +
|
||||
_filter_gain.get_value() * (current * _seconds.get_value());
|
||||
output = clamp( output );
|
||||
set_output_value( output );
|
||||
|
||||
_last_value = ivalue;
|
||||
}
|
||||
|
||||
|
||||
// Register the subsystem.
|
||||
SGSubsystemMgr::Registrant<Predictor> registrantPredictor;
|
||||
@@ -0,0 +1,72 @@
|
||||
// predictor.hxx - predict future values
|
||||
//
|
||||
// Written by Torsten Dreyer
|
||||
// Based heavily on work created by Curtis Olson, started January 2004.
|
||||
//
|
||||
// Copyright (C) 2004 Curtis L. Olson - http://www.flightgear.org/~curt
|
||||
// Copyright (C) 2010 Torsten Dreyer - Torsten (at) t3r (dot) de
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
#ifndef __PREDICTOR_HXX
|
||||
#define __PREDICTOR_HXX 1
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include <config.h>
|
||||
#endif
|
||||
|
||||
#include "analogcomponent.hxx"
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
|
||||
namespace FGXMLAutopilot {
|
||||
|
||||
/**
|
||||
* @brief Simple moving average filter converts input value to predicted value "seconds".
|
||||
*
|
||||
* Smoothing as described by Curt Olson:
|
||||
* gain would be valid in the range of 0 - 1.0
|
||||
* 1.0 would mean no filtering.
|
||||
* 0.0 would mean no input.
|
||||
* 0.5 would mean (1 part past value + 1 part current value) / 2
|
||||
* 0.1 would mean (9 parts past value + 1 part current value) / 10
|
||||
* 0.25 would mean (3 parts past value + 1 part current value) / 4
|
||||
*/
|
||||
class Predictor : public AnalogComponent
|
||||
{
|
||||
private:
|
||||
double _last_value;
|
||||
double _average;
|
||||
InputValueList _seconds;
|
||||
InputValueList _filter_gain;
|
||||
|
||||
protected:
|
||||
virtual bool configure( SGPropertyNode& cfg_node,
|
||||
const std::string& cfg_name,
|
||||
SGPropertyNode& prop_root );
|
||||
|
||||
public:
|
||||
Predictor();
|
||||
~Predictor() {}
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "predict-simple"; }
|
||||
|
||||
void update( bool firstTime, double dt );
|
||||
};
|
||||
|
||||
} // namespace FGXMLAutopilot
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,240 @@
|
||||
// route_mgr.hxx - manage a route (i.e. a collection of waypoints)
|
||||
//
|
||||
// Written by Curtis Olson, started January 2004.
|
||||
//
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// $Id$
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <simgear/props/props.hxx>
|
||||
#include <simgear/structure/subsystem_mgr.hxx>
|
||||
|
||||
#include <Navaids/FlightPlan.hxx>
|
||||
|
||||
// forward decls
|
||||
class SGPath;
|
||||
class PropertyWatcher;
|
||||
class RoutePath;
|
||||
|
||||
/**
|
||||
* Top level route manager class
|
||||
*
|
||||
*/
|
||||
|
||||
class FGRouteMgr : public SGSubsystem,
|
||||
public flightgear::FlightPlan::Delegate
|
||||
{
|
||||
public:
|
||||
FGRouteMgr();
|
||||
~FGRouteMgr();
|
||||
|
||||
// Subsystem API.
|
||||
void bind() override;
|
||||
void init() override;
|
||||
void postinit() override;
|
||||
void unbind() override;
|
||||
void update(double dt) override;
|
||||
|
||||
// Subsystem identification.
|
||||
static const char* staticSubsystemClassId() { return "route-manager"; }
|
||||
|
||||
bool isRouteActive() const;
|
||||
|
||||
int currentIndex() const;
|
||||
|
||||
void setFlightPlan(const flightgear::FlightPlanRef& plan);
|
||||
flightgear::FlightPlanRef flightPlan() const;
|
||||
|
||||
void clearRoute();
|
||||
|
||||
flightgear::Waypt* currentWaypt() const;
|
||||
|
||||
int numLegs() const;
|
||||
|
||||
// deprecated
|
||||
int numWaypts() const
|
||||
{ return numLegs(); }
|
||||
|
||||
// deprecated
|
||||
flightgear::Waypt* wayptAtIndex(int index) const;
|
||||
|
||||
SGPropertyNode_ptr wayptNodeAtIndex(int index) const;
|
||||
|
||||
void removeLegAtIndex(int aIndex);
|
||||
|
||||
/**
|
||||
* Activate a built route. This checks for various mandatory pieces of
|
||||
* data, such as departure and destination airports, and creates waypoints
|
||||
* for them on the route structure.
|
||||
*
|
||||
* returns true if the route was activated successfully, or false if the
|
||||
* route could not be activated for some reason
|
||||
*/
|
||||
bool activate();
|
||||
|
||||
/**
|
||||
* deactivate the route if active
|
||||
*/
|
||||
void deactivate();
|
||||
|
||||
/**
|
||||
* Set the current waypoint to the specified index.
|
||||
*/
|
||||
void jumpToIndex(int index);
|
||||
|
||||
bool saveRoute(const SGPath& p);
|
||||
bool loadRoute(const SGPath& p);
|
||||
|
||||
/**
|
||||
@brief Buiild a waypoint from a string description. Passed to the FlightPlan code to do
|
||||
the actual parsing, see that method for details of syntax.
|
||||
|
||||
Insert position is used to indicate which existing route waypoint(s) to use, to select between
|
||||
ambiguous names.
|
||||
*/
|
||||
flightgear::WayptRef waypointFromString(const std::string& target, int insertPosition);
|
||||
|
||||
private:
|
||||
bool commandDefineUserWaypoint(const SGPropertyNode * arg, SGPropertyNode * root);
|
||||
bool commandDeleteUserWaypoint(const SGPropertyNode * arg, SGPropertyNode * root);
|
||||
|
||||
flightgear::FlightPlanRef _plan;
|
||||
|
||||
time_t _takeoffTime;
|
||||
time_t _touchdownTime;
|
||||
|
||||
// automatic inputs
|
||||
SGPropertyNode_ptr magvar;
|
||||
|
||||
// automatic outputs
|
||||
SGPropertyNode_ptr departure; ///< departure airport information
|
||||
SGPropertyNode_ptr destination; ///< destination airport information
|
||||
SGPropertyNode_ptr alternate; ///< alternate airport information
|
||||
SGPropertyNode_ptr cruise; ///< cruise information
|
||||
|
||||
SGPropertyNode_ptr totalDistance;
|
||||
SGPropertyNode_ptr distanceToGo;
|
||||
SGPropertyNode_ptr ete;
|
||||
SGPropertyNode_ptr elapsedFlightTime;
|
||||
|
||||
SGPropertyNode_ptr active;
|
||||
SGPropertyNode_ptr airborne;
|
||||
|
||||
SGPropertyNode_ptr wp0;
|
||||
SGPropertyNode_ptr wp1;
|
||||
SGPropertyNode_ptr wpn;
|
||||
|
||||
|
||||
SGPropertyNode_ptr _pathNode;
|
||||
SGPropertyNode_ptr _currentWpt;
|
||||
|
||||
|
||||
/**
|
||||
* Signal property to notify people that the route was edited
|
||||
*/
|
||||
SGPropertyNode_ptr _edited;
|
||||
|
||||
/**
|
||||
* Signal property to notify when the last waypoint is reached
|
||||
*/
|
||||
SGPropertyNode_ptr _finished;
|
||||
|
||||
SGPropertyNode_ptr _flightplanChanged;
|
||||
|
||||
void setETAPropertyFromDistance(SGPropertyNode_ptr aProp, double aDistance);
|
||||
|
||||
/**
|
||||
* retrieve the cached path distance along a leg
|
||||
*/
|
||||
double cachedLegPathDistanceM(int index) const;
|
||||
double cachedWaypointPathTotalDistance(int index) const;
|
||||
|
||||
class InputListener : public SGPropertyChangeListener {
|
||||
public:
|
||||
InputListener(FGRouteMgr *m) : mgr(m) {}
|
||||
virtual void valueChanged (SGPropertyNode * prop);
|
||||
private:
|
||||
FGRouteMgr *mgr;
|
||||
};
|
||||
|
||||
SGPropertyNode_ptr input;
|
||||
SGPropertyNode_ptr weightOnWheels;
|
||||
SGPropertyNode_ptr groundSpeed;
|
||||
|
||||
InputListener *listener;
|
||||
SGPropertyNode_ptr mirror;
|
||||
|
||||
std::unique_ptr<RoutePath> _routePath;
|
||||
|
||||
/**
|
||||
* Helper to keep various pieces of state in sync when the route is
|
||||
* modified (waypoints added, inserted, removed). Notably, this fires the
|
||||
* 'edited' signal.
|
||||
*/
|
||||
void waypointsChanged() override;
|
||||
|
||||
void update_mirror();
|
||||
|
||||
void currentWaypointChanged() override;
|
||||
|
||||
// tied getters and setters
|
||||
std::string getDepartureICAO() const;
|
||||
std::string getDepartureName() const;
|
||||
void setDepartureICAO(const std::string& aIdent);
|
||||
|
||||
std::string getDepartureRunway() const;
|
||||
void setDepartureRunway(const std::string& aIdent);
|
||||
|
||||
std::string getSID() const;
|
||||
void setSID(const std::string& aIdent);
|
||||
|
||||
std::string getDestinationICAO() const;
|
||||
std::string getDestinationName() const;
|
||||
void setDestinationICAO(const std::string& aIdent);
|
||||
|
||||
std::string getDestinationRunway() const;
|
||||
void setDestinationRunway(const std::string& aIdent);
|
||||
|
||||
std::string getApproach() const;
|
||||
void setApproach(const std::string& aIdent);
|
||||
|
||||
std::string getSTAR() const;
|
||||
void setSTAR(const std::string& aIdent);
|
||||
|
||||
double getDepartureFieldElevation() const;
|
||||
double getDestinationFieldElevation() const;
|
||||
|
||||
int getCruiseAltitudeFt() const;
|
||||
void setCruiseAltitudeFt(int ft);
|
||||
|
||||
int getCruiseFlightLevel() const;
|
||||
void setCruiseFlightLevel(int fl);
|
||||
|
||||
int getCruiseSpeedKnots() const;
|
||||
void setCruiseSpeedKnots(int kts);
|
||||
|
||||
double getCruiseSpeedMach() const;
|
||||
void setCruiseSpeedMach(double m);
|
||||
|
||||
std::string getAlternate() const;
|
||||
std::string getAlternateName() const;
|
||||
void setAlternate(const std::string &icao);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user