first commit

This commit is contained in:
Your Name
2022-10-20 20:29:11 +08:00
commit 4d531f8044
3238 changed files with 1387862 additions and 0 deletions
+35
View File
@@ -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}")
+130
View File
@@ -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);
}
+160
View File
@@ -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 &lt;feedback-if-disabled&gt; 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 &lt;min&gt; and/or &lt;max&gt; 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 &lt;min&gt; and/or &lt;max&gt;.
*/
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
+239
View File
@@ -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 );
}
+69
View File
@@ -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
+238
View File
@@ -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);
}
+47
View File
@@ -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
+146
View File
@@ -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 );
}
+121
View File
@@ -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
* &lt;enable&gt; section
* @return true if the enable-condition is true.
*
* If a &lt;condition&gt; is defined, this condition is evaluated,
* &lt;prop&gt; and &lt;value&gt; tags are ignored.
*
* If a &lt;prop&gt; is defined and no &lt;value&gt; is defined, the property
* named in the &lt;prop&gt;&lt;prop&gt; tags is evaluated as boolean.
*
* If a &lt;prop&gt; is defined and a &lt;value&gt; is defined, the property named
* in &lt;prop&gt;&lt;/prop&gt; is compared (as a string) to the value defined in
* &lt;value&gt;&lt;/value&gt;
*
* Returns true, if neither &lt;condition&gt; nor &lt;prop&gt; exists
*/
bool isPropertyEnabled();
};
} // of namespace FGXMLAutopilot
+115
View File
@@ -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);
}
+139
View File
@@ -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
+64
View File
@@ -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
+489
View File
@@ -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;
+102
View File
@@ -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
+53
View File
@@ -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
+310
View File
@@ -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);
}
}
+151
View File
@@ -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
+76
View File
@@ -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;
+51
View File
@@ -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 &lt;condition&gt; 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
+298
View File
@@ -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;
+93
View File
@@ -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
+93
View File
@@ -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;
+68
View File
@@ -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
+83
View File
@@ -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;
+72
View File
@@ -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
+240
View File
@@ -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);
};