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

View File

@@ -0,0 +1,20 @@
include(FlightGearComponent)
set(SOURCES
electrical.cxx
pitot.cxx
static.cxx
system_mgr.cxx
vacuum.cxx
)
set(HEADERS
electrical.hxx
pitot.hxx
static.hxx
system_mgr.hxx
vacuum.hxx
)
flightgear_component(Systems "${SOURCES}" "${HEADERS}")

12
src/Systems/README Normal file
View File

@@ -0,0 +1,12 @@
src/Systems/ - support code for aircraft systems
This directory contains support code for major aircraft systems,
including the static, pitot, electrical, and vacuum systems. The file
system_mgr.[ch]xx contains a subsystem group that holds all of the
individual systems. Every system should extend FGSubsystem, and then
should be added to the group in the FGSystemMgr constructor.
Eventually, there will be an XML configuration file to select what
system modules should be available, so that different aircraft (i.e. a
twin plane with two vacuum systems) can have appropriate support.

767
src/Systems/electrical.cxx Normal file
View File

@@ -0,0 +1,767 @@
// electrical.cxx - a flexible, generic electrical system model.
//
// Written by Curtis Olson, started September 2002.
//
// Copyright (C) 2002 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$
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/props/props_io.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include "electrical.hxx"
FGElectricalComponent::FGElectricalComponent() :
kind(-1),
name(""),
volts(0.0),
load_amps(0.0)
{
}
void FGElectricalComponent::add_prop(const std::string &s)
{
auto nd = fgGetNode(s, true);
props.push_back(nd);
}
void FGElectricalComponent::publishVoltageToProps() const
{
const auto v = get_volts();
for (const auto& nd : props) {
nd->setFloatValue(v);
}
}
FGElectricalSupplier::FGElectricalSupplier ( SGPropertyNode *node ) {
kind = FG_SUPPLIER;
// cout << "Creating a supplier" << endl;
name = node->getStringValue("name");
string _model = node->getStringValue("kind");
// cout << "_model = " << _model << endl;
if ( _model == "battery" ) {
model = FG_BATTERY;
amp_hours = node->getFloatValue("amp-hours", 40.0);
percent_remaining = node->getFloatValue("percent-remaining", 1.0);
charge_amps = node->getFloatValue("charge-amps", 7.0);
} else if ( _model == "alternator" ) {
model = FG_ALTERNATOR;
rpm_src = node->getStringValue("rpm-source");
rpm_threshold = node->getFloatValue("rpm-threshold", 600.0);
ideal_amps = node->getFloatValue("amps", 60.0);
} else if ( _model == "external" ) {
model = FG_EXTERNAL;
ideal_amps = node->getFloatValue("amps", 60.0);
} else {
model = FG_UNKNOWN;
}
ideal_volts = node->getFloatValue("volts");
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
// cout << " scanning: " << child->getName() << endl;
if ( child->getNameString() == "prop" ) {
string prop = child->getStringValue();
// cout << " Adding prop = " << prop << endl;
add_prop( prop );
fgSetFloat( prop.c_str(), ideal_amps );
}
}
_rpm_node = fgGetNode( rpm_src.c_str(), true);
}
float FGElectricalSupplier::apply_load( float amps, float dt ) {
if ( model == FG_BATTERY ) {
// calculate amp hours used
float amphrs_used = amps * dt / 3600.0;
// calculate percent of total available capacity
float percent_used = amphrs_used / amp_hours;
percent_remaining -= percent_used;
if ( percent_remaining < 0.0 ) {
percent_remaining = 0.0;
} else if ( percent_remaining > 1.0 ) {
percent_remaining = 1.0;
}
// cout << "battery percent = " << percent_remaining << endl;
return amp_hours * percent_remaining;
} else if ( model == FG_ALTERNATOR ) {
// scale alternator output for rpms < 600. For rpms >= 600
// give full output. This is just a WAG, and probably not how
// it really works but I'm keeping things "simple" to start.
float rpm = _rpm_node->getFloatValue();
float factor = rpm / rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
// cout << "alternator amps = " << amps * factor << endl;
float available_amps = ideal_amps * factor;
return available_amps - amps;
} else if ( model == FG_EXTERNAL ) {
// cout << "external amps = " << 0.0 << endl;
float available_amps = ideal_amps;
return available_amps - amps;
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "unknown supplier type" );
}
return 0.0;
}
float FGElectricalSupplier::get_output_volts() {
if ( model == FG_BATTERY ) {
// cout << "battery amps = " << amps << endl;
float x = 1.0 - percent_remaining;
float tmp = -(3.0 * x - 1.0);
float factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
// cout << "battery % = " << percent_remaining <<
// " factor = " << factor << endl;
// percent_remaining -= 0.001;
return ideal_volts * factor;
} else if ( model == FG_ALTERNATOR ) {
// scale alternator output for rpms < 600. For rpms >= 600
// give full output. This is just a WAG, and probably not how
// it really works but I'm keeping things "simple" to start.
float rpm = _rpm_node->getFloatValue();
float factor = rpm / rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
// cout << "alternator amps = " << amps * factor << endl;
return ideal_volts * factor;
} else if ( model == FG_EXTERNAL ) {
// cout << "external amps = " << 0.0 << endl;
return ideal_volts;
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "unknown supplier type" );
}
return 0.0;
}
float FGElectricalSupplier::get_output_amps() {
if ( model == FG_BATTERY ) {
// cout << "battery amp_hours = " << amp_hours << endl;
// This is a WAG, but produce enough amps to burn the entire
// battery in one minute.
return amp_hours * 60.0;
} else if ( model == FG_ALTERNATOR ) {
// scale alternator output for rpms < 600. For rpms >= 600
// give full output. This is just a WAG, and probably not how
// it really works but I'm keeping things "simple" to start.
float rpm = _rpm_node->getFloatValue();
float factor = rpm / rpm_threshold;
if ( factor > 1.0 ) {
factor = 1.0;
}
// cout << "alternator amps = " << ideal_amps * factor << endl;
return ideal_amps * factor;
} else if ( model == FG_EXTERNAL ) {
// cout << "external amps = " << 0.0 << endl;
return ideal_amps;
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "unknown supplier type" );
}
return 0.0;
}
FGElectricalBus::FGElectricalBus ( SGPropertyNode *node ) {
kind = FG_BUS;
name = node->getStringValue("name");
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
if ( child->getNameString() == "prop" ) {
string prop = child->getStringValue();
add_prop( prop );
}
}
}
FGElectricalOutput::FGElectricalOutput ( SGPropertyNode *node ) {
kind = FG_OUTPUT;
load_amps = 0.1; // arbitrary default value
name = node->getStringValue("name");
SGPropertyNode *draw = node->getNode("rated-draw");
if ( draw != NULL ) {
load_amps = draw->getFloatValue();
}
// cout << "rated draw = " << output_amps << endl;
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
if ( child->getNameString() == "prop" ) {
string prop = child->getStringValue();
add_prop( prop );
}
}
}
FGElectricalSwitch::FGElectricalSwitch( SGPropertyNode *node ) :
switch_node( NULL ),
rating_amps( 0.0f ),
circuit_breaker( false )
{
bool initial_state = true;
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
string cname = child->getNameString();
string cval = child->getStringValue();
if ( cname == "prop" ) {
switch_node = fgGetNode( cval.c_str(), true );
// cout << "switch node = " << cval << endl;
} else if ( cname == "initial-state" ) {
if ( cval == "off" || cval == "false" ) {
initial_state = false;
}
// cout << "initial state = " << initial_state << endl;
} else if ( cname == "rating-amps" ) {
rating_amps = atof( cval.c_str() );
circuit_breaker = true;
// cout << "initial state = " << initial_state << endl;
}
}
switch_node->setBoolValue( initial_state );
// cout << " value = " << switch_node->getBoolValue() << endl;
}
FGElectricalConnector::FGElectricalConnector ( SGPropertyNode *node,
FGElectricalSystem *es ) {
kind = FG_CONNECTOR;
name = "connector";
int i;
for ( i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
string cname = child->getNameString();
string cval = child->getStringValue();
// cout << " " << cname << " = " << cval << endl;
if ( cname == "input" ) {
FGElectricalComponent *s = es->find( child->getStringValue() );
if ( s != NULL ) {
add_input( s );
if ( s->get_kind() == FG_SUPPLIER ) {
s->add_output( this );
} else if ( s->get_kind() == FG_BUS ) {
s->add_output( this );
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT,
"Attempt to connect to something that can't provide an output: "
<< child->getStringValue() );
}
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "Can't find named source: "
<< child->getStringValue() );
}
} else if ( cname == "output" ) {
FGElectricalComponent *s = es->find( child->getStringValue() );
if ( s != NULL ) {
add_output( s );
if ( s->get_kind() == FG_BUS ) {
s->add_input( this );
} else if ( s->get_kind() == FG_OUTPUT ) {
s->add_input( this );
} else if ( s->get_kind() == FG_SUPPLIER &&
((FGElectricalSupplier *)s)->get_model()
== FGElectricalSupplier::FG_BATTERY ) {
s->add_output( this );
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT,
"Attempt to connect to something that can't provide an input: "
<< child->getStringValue() );
}
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "Can't find named source: "
<< child->getStringValue() );
}
} else if ( cname == "switch" ) {
// cout << "Switch = " << child->getStringValue() << endl;
FGElectricalSwitch s( child );
add_switch( s );
}
}
}
// set all switches to the specified state
void FGElectricalConnector::set_switches( bool state ) {
// cout << "setting switch state to " << state << endl;
for ( unsigned int i = 0; i < switches.size(); ++i ) {
switches[i].set_state( state );
}
}
// return true if all switches are true, false otherwise. A connector
// could have multiple switches, but they all need to be true(closed)
// for current to get through.
bool FGElectricalConnector::get_state() {
unsigned int i;
for ( i = 0; i < switches.size(); ++i ) {
if ( ! switches[i].get_state() ) {
return false;
}
}
return true;
}
FGElectricalSystem::FGElectricalSystem ( SGPropertyNode *node ) :
name(node->getStringValue("name", "electrical")),
num(node->getIntValue("number", 0)),
path(node->getStringValue("path")),
enabled(false)
{
}
FGElectricalSystem::~FGElectricalSystem()
{
SG_LOG(SG_SYSTEMS, SG_INFO, "Destroying elec system");
}
void FGElectricalSystem::init () {
SGPropertyNode_ptr config_props = new SGPropertyNode;
_volts_out = fgGetNode( "/systems/electrical/volts", true );
_amps_out = fgGetNode( "/systems/electrical/amps", true );
// allow the electrical system to be specified via the
// aircraft-set.xml file (for backwards compatibility) or through
// the aircraft-systems.xml file. If a -set.xml entry is
// specified, that overrides the system entry.
SGPropertyNode *path_n = fgGetNode("/sim/systems/electrical/path");
if ( path_n ) {
if ( path.length() ) {
SG_LOG( SG_SYSTEMS, SG_INFO,
"NOTICE: System manager configuration specifies an " <<
"electrical system: " << path << " but it is " <<
"being overridden by the one specified in the -set.xml " <<
"file: " << path_n->getStringValue() );
}
path = path_n->getStringValue();
}
if ( path.length() ) {
SGPath config = globals->resolve_aircraft_path(path);
if (!config.exists()) {
SG_LOG( SG_SYSTEMS, SG_ALERT, "Failed to find electrical system model: " << config );
return;
}
// load an obsolete xml configuration
SG_LOG( SG_SYSTEMS, SG_DEV_WARN,
"Reading deprecated xml electrical system model from\n "
<< config.str() );
try {
readProperties( config, config_props );
if ( build(config_props) ) {
enabled = true;
} else {
throw sg_exception("Logic error in electrical system file.");
}
} catch (const sg_exception&) {
SG_LOG( SG_SYSTEMS, SG_ALERT,
"Failed to load electrical system model: "
<< config );
}
} else {
SG_LOG( SG_SYSTEMS, SG_INFO,
"No xml-based electrical model specified for this model!");
}
if ( !enabled ) {
_amps_out->setDoubleValue(0);
}
}
void FGElectricalSystem::bind ()
{
_serviceable_node = fgGetNode("/systems/electrical/serviceable", true);
}
void FGElectricalSystem::unbind ()
{
_serviceable_node.reset();
_volts_out.reset();
_amps_out.reset();
}
void FGElectricalSystem::deleteComponents(comp_list& comps)
{
std::for_each(comps.begin(), comps.end(),
[](FGElectricalComponent* comp) {
delete comp;
});
comps.clear();
}
void FGElectricalSystem::shutdown()
{
deleteComponents(suppliers);
deleteComponents(buses);
deleteComponents(outputs);
deleteComponents(connectors);
}
void FGElectricalSystem::update (double dt)
{
if ( !enabled ) {
return;
}
// cout << "Updating electrical system, dt = " << dt << endl;
_serviceable = _serviceable_node->getBoolValue();
unsigned int i;
// zero out the voltage before we start, but don't clear the
// requested load values.
for ( i = 0; i < suppliers.size(); ++i ) {
suppliers[i]->set_volts( 0.0 );
}
for ( i = 0; i < buses.size(); ++i ) {
buses[i]->set_volts( 0.0 );
}
for ( i = 0; i < outputs.size(); ++i ) {
outputs[i]->set_volts( 0.0 );
}
for ( i = 0; i < connectors.size(); ++i ) {
connectors[i]->set_volts( 0.0 );
}
// for each "external" supplier, propagate the electrical current
for ( i = 0; i < suppliers.size(); ++i ) {
FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
if ( node->get_model() == FGElectricalSupplier::FG_EXTERNAL ) {
float load;
// cout << "Starting propagation: " << suppliers[i]->get_name()
// << endl;
load = propagate( suppliers[i], dt,
node->get_output_volts(),
node->get_output_amps(),
" " );
if ( node->apply_load( load, dt ) < 0.0 ) {
SG_LOG(SG_SYSTEMS, SG_ALERT,
"Error drawing more current than available!");
}
}
}
// for each "alternator" supplier, propagate the electrical
// current
for ( i = 0; i < suppliers.size(); ++i ) {
FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
if ( node->get_model() == FGElectricalSupplier::FG_ALTERNATOR) {
float load;
// cout << "Starting propagation: " << suppliers[i]->get_name()
// << endl;
load = propagate( suppliers[i], dt,
node->get_output_volts(),
node->get_output_amps(),
" " );
if ( node->apply_load( load, dt ) < 0.0 ) {
SG_LOG(SG_SYSTEMS, SG_ALERT,
"Error drawing more current than available!");
}
}
}
// for each "battery" supplier, propagate the electrical
// current
for ( i = 0; i < suppliers.size(); ++i ) {
FGElectricalSupplier *node = (FGElectricalSupplier *)suppliers[i];
if ( node->get_model() == FGElectricalSupplier::FG_BATTERY ) {
float load;
// cout << "Starting propagation: " << suppliers[i]->get_name()
// << endl;
load = propagate( suppliers[i], dt,
node->get_output_volts(),
node->get_output_amps(),
" " );
// cout << "battery load = " << load << endl;
if ( node->apply_load( load, dt ) < 0.0 ) {
SG_LOG(SG_SYSTEMS, SG_ALERT,
"Error drawing more current than available!");
}
}
}
float alt_norm
= fgGetFloat("/systems/electrical/suppliers/alternator") / 60.0;
// impliment an extremely simplistic voltage model (assumes
// certain naming conventions in electrical system config)
// FIXME: we probably want to be able to feed power from all
// engines if they are running and the master-alt is switched on
float volts = 0.0;
if ( fgGetBool("/controls/engines/engine[0]/master-bat") ) {
volts = 24.0;
}
if ( fgGetBool("/controls/engines/engine[0]/master-alt") ) {
if ( fgGetFloat("/engines/engine[0]/rpm") > 800 ) {
float alt_contrib = 28.0;
if ( alt_contrib > volts ) {
volts = alt_contrib;
}
} else if ( fgGetFloat("/engines/engine[0]/rpm") > 200 ) {
float alt_contrib = 20.0;
if ( alt_contrib > volts ) {
volts = alt_contrib;
}
}
}
_volts_out->setFloatValue( volts );
// impliment an extremely simplistic amps model (assumes certain
// naming conventions in the electrical system config) ... FIXME:
// make this more generic
float amps = 0.0;
if ( fgGetBool("/controls/engines/engine[0]/master-bat") ) {
if ( fgGetBool("/controls/engines/engine[0]/master-alt") &&
fgGetFloat("/engines/engine[0]/rpm") > 800 )
{
amps += 40.0 * alt_norm;
}
amps -= 15.0; // normal load
if ( fgGetBool("/controls/switches/flashing-beacon") ) {
amps -= 7.5;
}
if ( fgGetBool("/controls/switches/nav-lights") ) {
amps -= 7.5;
}
if ( amps > 7.0 ) {
amps = 7.0;
}
}
_amps_out->setFloatValue( amps );
}
bool FGElectricalSystem::build (SGPropertyNode* config_props) {
SGPropertyNode *node;
int i;
int count = config_props->nChildren();
for ( i = 0; i < count; ++i ) {
node = config_props->getChild(i);
string name = node->getNameString();
// cout << name << endl;
if ( name == "supplier" ) {
FGElectricalSupplier *s =
new FGElectricalSupplier( node );
suppliers.push_back( s );
} else if ( name == "bus" ) {
FGElectricalBus *b =
new FGElectricalBus( node );
buses.push_back( b );
} else if ( name == "output" ) {
FGElectricalOutput *o =
new FGElectricalOutput( node );
outputs.push_back( o );
} else if ( name == "connector" ) {
FGElectricalConnector *c =
new FGElectricalConnector( node, this );
connectors.push_back( c );
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "Unknown component type specified: "
<< name );
return false;
}
}
return true;
}
// propagate the electrical current through the network, returns the
// total current drawn by the children of this node.
float FGElectricalSystem::propagate( FGElectricalComponent *node, double dt,
float input_volts, float input_amps,
string s ) {
s += " ";
float total_load = 0.0;
// determine the current to carry forward
float volts = 0.0;
if ( !_serviceable) {
volts = 0;
} else if ( node->get_kind() == FGElectricalComponent::FG_SUPPLIER ) {
// cout << s << "is a supplier (" << node->get_name() << ")" << endl;
FGElectricalSupplier *supplier = (FGElectricalSupplier *)node;
if ( supplier->get_model() == FGElectricalSupplier::FG_BATTERY ) {
// cout << s << " (and is a battery)" << endl;
float battery_volts = supplier->get_output_volts();
if ( battery_volts < (input_volts - 0.1) ) {
// special handling of a battery charge condition
// cout << s << " (and is being charged) in v = "
// << input_volts << " current v = " << battery_volts
// << endl;
supplier->apply_load( -supplier->get_charge_amps(), dt );
return supplier->get_charge_amps();
}
}
volts = input_volts;
} else if ( node->get_kind() == FGElectricalComponent::FG_BUS ) {
// cout << s << "is a bus (" << node->get_name() << ")" << endl;
volts = input_volts;
} else if ( node->get_kind() == FGElectricalComponent::FG_OUTPUT ) {
// cout << s << "is an output (" << node->get_name() << ")" << endl;
volts = input_volts;
if ( volts > 1.0 ) {
// draw current if we have voltage
total_load = node->get_load_amps();
}
} else if ( node->get_kind() == FGElectricalComponent::FG_CONNECTOR ) {
// cout << s << "is a connector (" << node->get_name() << ")" << endl;
if ( ((FGElectricalConnector *)node)->get_state() ) {
volts = input_volts;
} else {
volts = 0.0;
}
// cout << s << " input_volts = " << volts << endl;
} else {
SG_LOG( SG_SYSTEMS, SG_ALERT, "unknown node type" );
}
int i;
// if this node has found a stronger power source, update the
// value and propagate to all children
if ( volts > node->get_volts() ) {
node->set_volts( volts );
for ( i = 0; i < node->get_num_outputs(); ++i ) {
FGElectricalComponent *child = node->get_output(i);
// send current equal to load
total_load += propagate( child, dt,
volts, child->get_load_amps(),
s );
}
// if not an output node, register the downstream current draw
// (sum of all children) with this node. If volts are zero,
// current draw should be zero.
if ( node->get_kind() != FGElectricalComponent::FG_OUTPUT ) {
node->set_load_amps( total_load );
}
node->set_available_amps( input_amps - total_load );
node->publishVoltageToProps();
/*
cout << s << node->get_name() << " -> (volts) " << node->get_volts()
<< endl;
cout << s << node->get_name() << " -> (load amps) " << total_load
<< endl;
cout << s << node->get_name() << " -> (input amps) " << input_amps
<< endl;
cout << s << node->get_name() << " -> (extra amps) "
<< node->get_available_amps() << endl;
*/
return total_load;
} else {
// cout << s << "no further propagation" << endl;
return 0.0;
}
}
// search for the named component and return a pointer to it, NULL otherwise
FGElectricalComponent *FGElectricalSystem::find ( const string &name ) {
unsigned int i;
string s;
// search suppliers
for ( i = 0; i < suppliers.size(); ++i ) {
s = suppliers[i]->get_name();
// cout << " " << s << endl;
if ( s == name ) {
return suppliers[i];
}
}
// then search buses
for ( i = 0; i < buses.size(); ++i ) {
s = buses[i]->get_name();
// cout << " " << s << endl;
if ( s == name ) {
return buses[i];
}
}
// then search outputs
for ( i = 0; i < outputs.size(); ++i ) {
s = outputs[i]->get_name();
// cout << " " << s << endl;
if ( s == name ) {
return outputs[i];
}
}
// nothing found
return NULL;
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<FGElectricalSystem> registrantFGElectricalSystem;
#endif

261
src/Systems/electrical.hxx Normal file
View File

@@ -0,0 +1,261 @@
// electrical.hxx - a flexible, generic electrical system model.
//
// Written by Curtis Olson, started September 2002.
//
// Copyright (C) 2002 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$
#ifndef _SYSTEMS_ELECTRICAL_HXX
#define _SYSTEMS_ELECTRICAL_HXX 1
#include <string>
#include <vector>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
// Forward declaration
class FGElectricalSystem;
// Base class for other electrical components
class FGElectricalComponent
{
public:
enum FGElectricalComponentType {
FG_UNKNOWN,
FG_SUPPLIER,
FG_BUS,
FG_OUTPUT,
FG_CONNECTOR
};
protected:
using comp_list = std::vector<FGElectricalComponent *> ;
int kind;
std::string name;
float volts;
float load_amps; // sum of current draw (load) due to
// this node and all it's children
float available_amps; // available current (after the load
// is subtracted)
comp_list inputs;
comp_list outputs;
simgear::PropertyList props;
public:
FGElectricalComponent();
virtual ~FGElectricalComponent() = default;
inline const std::string& get_name() { return name; }
inline int get_kind() const { return kind; }
inline float get_volts() const { return volts; }
inline void set_volts( float val ) { volts = val; }
inline float get_load_amps() const { return load_amps; }
inline void set_load_amps( float val ) { load_amps = val; }
inline float get_available_amps() const { return available_amps; }
inline void set_available_amps( float val ) { available_amps = val; }
inline int get_num_inputs() const { return outputs.size(); }
inline FGElectricalComponent *get_input( const int i ) {
return inputs[i];
}
inline void add_input( FGElectricalComponent *c ) {
inputs.push_back( c );
}
inline int get_num_outputs() const { return outputs.size(); }
inline FGElectricalComponent *get_output( const int i ) {
return outputs[i];
}
inline void add_output( FGElectricalComponent *c ) {
outputs.push_back( c );
}
void add_prop( const std::string &s );
void publishVoltageToProps() const;
};
// Electrical supplier
class FGElectricalSupplier : public FGElectricalComponent
{
public:
enum FGSupplierType {
FG_BATTERY,
FG_ALTERNATOR,
FG_EXTERNAL,
FG_UNKNOWN
};
private:
SGPropertyNode_ptr _rpm_node;
FGSupplierType model; // store supplier type
float ideal_volts; // ideal volts
// alternator fields
string rpm_src; // property name of alternator power source
float rpm_threshold; // minimal rpm to generate full power
// alt & ext supplier fields
float ideal_amps; // total amps produced (above rpm threshold).
// battery fields
float amp_hours; // fully charged battery capacity
float percent_remaining; // percent of charge remaining
float charge_amps; // maximum charge load battery can draw
public:
FGElectricalSupplier ( SGPropertyNode *node );
~FGElectricalSupplier () {}
inline FGSupplierType get_model() const { return model; }
float apply_load( float amps, float dt );
float get_output_volts();
float get_output_amps();
float get_charge_amps() const { return charge_amps; }
};
// Electrical bus (can take multiple inputs and provide multiple
// outputs)
class FGElectricalBus : public FGElectricalComponent
{
public:
FGElectricalBus ( SGPropertyNode *node );
~FGElectricalBus () {}
};
// A lot like an FGElectricalBus, but here for convenience and future
// flexibility
class FGElectricalOutput : public FGElectricalComponent
{
public:
FGElectricalOutput ( SGPropertyNode *node );
~FGElectricalOutput () {}
};
// Model an electrical switch. If the rating_amps > 0 then this
// becomes a circuit breaker type switch that can trip
class FGElectricalSwitch
{
private:
SGPropertyNode_ptr switch_node;
float rating_amps;
bool circuit_breaker;
public:
FGElectricalSwitch( SGPropertyNode *node );
~FGElectricalSwitch() { };
inline bool get_state() const { return switch_node->getBoolValue(); }
void set_state( bool val ) { switch_node->setBoolValue( val ); }
};
// Connects multiple sources to multiple destinations with optional
// switches/fuses/circuit breakers inline
class FGElectricalConnector : public FGElectricalComponent
{
comp_list inputs;
comp_list outputs;
typedef vector< FGElectricalSwitch> switch_list;
switch_list switches;
public:
FGElectricalConnector ( SGPropertyNode *node, FGElectricalSystem *es );
~FGElectricalConnector () {}
void add_switch( FGElectricalSwitch s ) {
switches.push_back( s );
}
// set all switches to the specified state
void set_switches( bool state );
bool get_state();
};
/**
* Model an electrical system. This is a fairly simplistic system
*
*/
class FGElectricalSystem : public SGSubsystem
{
public:
FGElectricalSystem ( SGPropertyNode *node );
virtual ~FGElectricalSystem ();
// Subsystem API.
void bind() override;
void init() override;
void shutdown() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "electrical"; }
bool build (SGPropertyNode* config_props);
float propagate( FGElectricalComponent *node, double dt,
float input_volts, float input_amps,
std::string s = "" );
FGElectricalComponent *find ( const std::string &name );
protected:
typedef vector<FGElectricalComponent *> comp_list;
private:
void deleteComponents(comp_list& comps);
std::string name;
int num;
std::string path;
bool enabled;
comp_list suppliers;
comp_list buses;
comp_list outputs;
comp_list connectors;
SGPropertyNode_ptr _volts_out;
SGPropertyNode_ptr _amps_out;
SGPropertyNode_ptr _serviceable_node;
bool _serviceable = true;
};
#endif // _SYSTEMS_ELECTRICAL_HXX

97
src/Systems/pitot.cxx Normal file
View File

@@ -0,0 +1,97 @@
// pitot.cxx - the pitot air system.
// Written by David Megginson, started 2002.
//
// Last modified by Eric van den Berg, 01 Nov 2013
// This file is in the Public Domain and comes with no warranty.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/constants.h>
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
#include "pitot.hxx"
PitotSystem::PitotSystem ( SGPropertyNode *node )
:
_name(node->getStringValue("name", "pitot")),
_num(node->getIntValue("number", 0)),
_stall_factor(cos(node->getDoubleValue("stall-deg", 60.0) * SGD_DEGREES_TO_RADIANS ))
// this is the projection factor for the stall angle.
{
}
PitotSystem::~PitotSystem ()
{
}
void
PitotSystem::init ()
{
string branch;
branch = "/systems/" + _name;
SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
_serviceable_node = node->getChild("serviceable", 0, true);
_pressure_node = fgGetNode("/environment/pressure-inhg", true);
_mach_node = fgGetNode("/velocities/mach", true);
_alpha_deg_node = fgGetNode("/orientation/alpha-deg", true);
_beta_deg_node = fgGetNode("/orientation/side-slip-deg", true);
_total_pressure_node = node->getChild("total-pressure-inhg", 0, true);
_measured_total_pressure_node = node->getChild("measured-total-pressure-inhg", 0, true);
if ( _stall_factor < 0 ) { // |stall angle| > 90°
_stall_factor = cos(60 * SGD_DEGREES_TO_RADIANS);
}
}
void
PitotSystem::bind ()
{
}
void
PitotSystem::unbind ()
{
}
void
PitotSystem::update (double dt)
{
if (_serviceable_node->getBoolValue()) {
double p = _pressure_node->getDoubleValue();
double mach = _mach_node->getDoubleValue();
double alpha = _alpha_deg_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
double beta = _beta_deg_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
double x_proj_factor = cos(alpha) * fabs(cos(beta)); // the factor to project the total speed vector on the longitudinal body axis
double p_t = p; // pitot tube stalled: total pressure = static pressure
double p_t_meas = p;
if ( x_proj_factor > _stall_factor ) { // NOTE: alpha: -180 - 180, beta: -90 - 90, pitot probe is stalled at more than 60 deg
p_t = p * pow(1 + 0.2 * mach*mach*x_proj_factor*x_proj_factor, 3.5 ); // total pressure in the pitot tube if not stalled
p_t_meas = p_t;
if (mach > 1) {
p_t_meas = p * pow( 1.2 * mach*mach, 3.5 ) * pow( 2.8/2.4*mach*mach - 0.4 / 2.4 , -2.5 ); // measured total pressure by pitot tube (Rayleigh formula, at Mach>1, normal shockwave in front of pitot tube)
}
}
_total_pressure_node->setDoubleValue(p_t);
_measured_total_pressure_node->setDoubleValue(p_t_meas);
}
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<PitotSystem> registrantPitotSystem(
SGSubsystemMgr::POST_FDM,
{{"static", SGSubsystemMgr::Dependency::SOFT},
{"vacuum", SGSubsystemMgr::Dependency::SOFT}});
#endif
// end of pitot.cxx

65
src/Systems/pitot.hxx Normal file
View File

@@ -0,0 +1,65 @@
// pitot.hxx - the pitot air system.
// Written by David Megginson, started 2002.
//
// Last modified by Eric van den Berg, 01 Nov 2013
// This file is in the Public Domain and comes with no warranty.
#pragma once
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/compiler.h>
#include <string>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
/**
* Model a pitot air system.
*
* The output is the sum of static and dynamic pressure (not just the
* dynamic pressure).
*
* Input properties:
*
* /systems/"name"/serviceable
* /environment/pressure-inhg
* /velocities/mach
*
* Output properties:
*
* /systems/"name"/total-pressure-inhg
* /systems/"name"/measured-total-pressure-inhg
*/
class PitotSystem : public SGSubsystem
{
public:
PitotSystem ( SGPropertyNode *node );
virtual ~PitotSystem ();
// Subsystem API.
void bind() override;
void init() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "pitot"; }
private:
std::string _name;
int _num;
double _stall_factor;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _pressure_node;
SGPropertyNode_ptr _mach_node;
SGPropertyNode_ptr _total_pressure_node;
SGPropertyNode_ptr _measured_total_pressure_node;
SGPropertyNode_ptr _alpha_deg_node;
SGPropertyNode_ptr _beta_deg_node;
};

121
src/Systems/static.cxx Normal file
View File

@@ -0,0 +1,121 @@
// static.cxx - the static air system.
// Written by David Megginson, started 2002.
//
// Last modified by Eric van den Berg, 09 Nov 2013
// This file is in the Public Domain and comes with no warranty.
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "static.hxx"
#include <string>
#include <Main/fg_props.hxx>
#include <Main/util.hxx>
#include <simgear/constants.h>
#include <simgear/math/SGMisc.hxx>
#include <simgear/math/SGLimits.hxx>
#include <simgear/math/SGMathFwd.hxx>
#include <simgear/sg_inlines.h>
StaticSystem::StaticSystem ( SGPropertyNode *node )
:
_name(node->getStringValue("name", "static")),
_num(node->getIntValue("number", 0)),
_tau(SGMiscd::max(.0,node->getDoubleValue("tau", 1))),
_error_factor(node->getDoubleValue("error-factor", 0)),
_type(node->getIntValue("type", 0))
{
}
StaticSystem::~StaticSystem ()
{
}
void
StaticSystem::init ()
{
std::string branch = "/systems/" + _name;
SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
_serviceable_node = node->getChild("serviceable", 0, true);
_pressure_in_node = fgGetNode("/environment/pressure-inhg", true);
_pressure_out_node = node->getChild("pressure-inhg", 0, true);
_beta_node = fgGetNode("/orientation/side-slip-deg", true);
_alpha_node = fgGetNode("/orientation/alpha-deg", true);
_mach_node = fgGetNode("/velocities/mach", true);
SG_CLAMP_RANGE(_error_factor,0.0,1.0); // making sure the error_factor is between 0 and 1
reinit();
}
void
StaticSystem::reinit ()
{
// start with settled static pressure
_pressure_out_node->setDoubleValue(_pressure_in_node->getDoubleValue());
}
void
StaticSystem::bind ()
{
}
void
StaticSystem::unbind ()
{
}
void
StaticSystem::update (double dt)
{
if (_serviceable_node->getBoolValue()) {
double p_new = _pressure_in_node->getDoubleValue(); //current static pressure around aircraft
double p = _pressure_out_node->getDoubleValue(); //last pressure in aircraft static system
double beta;
double alpha;
double mach;
double trat = _tau ? dt/_tau : SGLimitsd::max();
double proj_factor = 0;
double pt;
double qc_part;
if (_type == 1) { // type 1 = static pressure dependent on side-slip only: static port on the fuselage
beta = _beta_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
proj_factor = sin(beta);
}
if (_type == 2) { // type 2 = static pressure dependent on aoa and side-slip: static port on the pitot tube
alpha = _alpha_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
beta = _beta_node->getDoubleValue() * SGD_DEGREES_TO_RADIANS;
proj_factor = sqrt( 1.0 - cos(beta)*cos(beta) * cos(alpha)*cos(alpha) );
}
if ( (_type ==1) || (_type == 2) ) {
mach = _mach_node->getDoubleValue();
pt = p_new * pow(1 + 0.2 * mach*mach*proj_factor*proj_factor, 3.5 ); //total pressure perpendicular to static port (=perpendicular to body x-axis)
qc_part = (pt - p_new) * _error_factor ; //part of impact pressure to be added to static pressure (due to sideslip)
p_new = p_new + qc_part;
}
_pressure_out_node->setDoubleValue(
_tau > .0 ? fgGetLowPass(p, p_new, trat) : p_new
); //setting new pressure in static system
}
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<StaticSystem> registrantStaticSystem(
SGSubsystemMgr::GENERAL,
{{"vacuum", SGSubsystemMgr::Dependency::HARD}});
#endif
// end of static.cxx

67
src/Systems/static.hxx Normal file
View File

@@ -0,0 +1,67 @@
// static.hxx - the static air system.
// Written by David Megginson, started 2002.
//
// Last modified by Eric van den Berg, 09 November 2013
// This file is in the Public Domain and comes with no warranty.
#ifndef __SYSTEMS_STATIC_HXX
#define __SYSTEMS_STATIC_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
/**
* Model a static air system.
*
* Input properties:
*
* /environment/pressure-inhg
* /systems/"name"/serviceable
* /orientation/alpha-deg
* /orientation/side-slip-rad
* /velocities/mach
*
* Output properties:
*
* /systems/"name"/pressure-inhg
*
* TODO: support alternate air with errors
*/
class StaticSystem : public SGSubsystem
{
public:
StaticSystem ( SGPropertyNode *node );
StaticSystem ( int i );
virtual ~StaticSystem ();
// Subsystem API.
void bind() override;
void init() override;
void reinit() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "static"; }
private:
std::string _name;
int _num;
double _tau;
double _error_factor;
int _type;
SGPropertyNode_ptr _serviceable_node;
SGPropertyNode_ptr _pressure_in_node;
SGPropertyNode_ptr _pressure_out_node;
SGPropertyNode_ptr _beta_node;
SGPropertyNode_ptr _alpha_node;
SGPropertyNode_ptr _mach_node;
};
#endif // __SYSTEMS_STATIC_HXX

103
src/Systems/system_mgr.cxx Normal file
View File

@@ -0,0 +1,103 @@
// system_mgr.cxx - manage aircraft systems.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain and comes with no warranty.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/structure/exception.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/props/props_io.hxx>
#include <Main/fg_props.hxx>
#include <Main/globals.hxx>
#include <Main/util.hxx>
#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
#include "system_mgr.hxx"
#include "electrical.hxx"
#include "pitot.hxx"
#include "static.hxx"
#include "vacuum.hxx"
FGSystemMgr::FGSystemMgr ()
{
SGPropertyNode_ptr config_props = new SGPropertyNode;
SGPropertyNode *path_n = fgGetNode("/sim/systems/path");
if (path_n) {
SGPath config = globals->resolve_aircraft_path(path_n->getStringValue());
if (!config.exists()) {
SG_LOG( SG_SYSTEMS, SG_DEV_ALERT, "System model file not found:" << config);
return;
}
SG_LOG( SG_SYSTEMS, SG_INFO, "Reading systems from "
<< config );
try
{
readProperties( config, config_props );
build(config_props);
}
catch( const sg_exception& )
{
SG_LOG( SG_SYSTEMS, SG_ALERT, "Failed to load systems system model: "
<< config );
}
} else {
SG_LOG( SG_SYSTEMS, SG_WARN,
"No systems model specified for this model!");
}
}
FGSystemMgr::~FGSystemMgr ()
{
}
bool FGSystemMgr::build (SGPropertyNode* config_props)
{
SGPropertyNode *node;
int i;
int count = config_props->nChildren();
for ( i = 0; i < count; ++i ) {
node = config_props->getChild(i);
string name = node->getNameString();
std::ostringstream temp;
temp << i;
if ( name == "electrical" ) {
set_subsystem( "electrical" + temp.str(),
new FGElectricalSystem( node ) );
} else if ( name == "pitot" ) {
set_subsystem( "system" + temp.str(),
new PitotSystem( node ) );
} else if ( name == "static" ) {
set_subsystem( "system" + temp.str(),
new StaticSystem( node ) );
} else if ( name == "vacuum" ) {
set_subsystem( "system" + temp.str(),
new VacuumSystem( node ) );
} else {
SG_LOG(SG_SYSTEMS, SG_ALERT, "Ignoring unknown system: " << name);
}
}
return true;
}
// Register the subsystem.
SGSubsystemMgr::Registrant<FGSystemMgr> registrantFGSystemMgr(
SGSubsystemMgr::FDM);
// end of system_manager.cxx

View File

@@ -0,0 +1,40 @@
// system_mgr.hxx - manage aircraft systems.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain and comes with no warranty.
#ifndef __SYSTEM_MGR_HXX
#define __SYSTEM_MGR_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <simgear/compiler.h>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
/**
* Manage aircraft systems.
*
* Multiple aircraft systems can be configured for each aircraft.
*/
class FGSystemMgr : public SGSubsystemGroup
{
public:
FGSystemMgr ();
virtual ~FGSystemMgr ();
// Subsystem identification.
static const char* staticSubsystemClassId() { return "systems"; }
bool build (SGPropertyNode* config_props);
};
#endif // __SYSTEM_MGR_HXX

107
src/Systems/vacuum.cxx Normal file
View File

@@ -0,0 +1,107 @@
// vacuum.cxx - a vacuum pump connected to the aircraft engine.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain and comes with no warranty.
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "vacuum.hxx"
#include <cstring>
#include <Main/fg_props.hxx>
VacuumSystem::VacuumSystem ( SGPropertyNode *node )
:
_name(node->getStringValue("name", "vacuum")),
_num(node->getIntValue("number", 0)),
_scale(node->getDoubleValue("scale", 1.0))
{
for ( int i = 0; i < node->nChildren(); ++i ) {
SGPropertyNode *child = node->getChild(i);
if (child->getNameString() == "rpm")
_rpms.push_back(child->getStringValue());
}
}
VacuumSystem::~VacuumSystem ()
{
}
void
VacuumSystem::init()
{
unsigned int i;
std::string branch;
branch = "/systems/" + _name;
SGPropertyNode *node = fgGetNode(branch.c_str(), _num, true );
_serviceable_node = node->getChild("serviceable", 0, true);
for ( i = 0; i < _rpms.size(); i++ ) {
SGPropertyNode_ptr _rpm_node = fgGetNode(_rpms[i].c_str(), true);
_rpm_nodes.push_back( _rpm_node );
}
_pressure_node = fgGetNode("/environment/pressure-inhg", true);
_suction_node = node->getChild("suction-inhg", 0, true);
reinit();
}
void
VacuumSystem::reinit()
{
_suction_node->setDoubleValue(0.0);
}
void
VacuumSystem::bind ()
{
}
void
VacuumSystem::unbind ()
{
}
void
VacuumSystem::update (double dt)
{
// Model taken from steam.cxx
double suction;
unsigned int i;
if (!_serviceable_node->getBoolValue()) {
suction = 0.0;
} else {
// select the source with the max rpm
double rpm = 0.0;
for ( i = 0; i < _rpm_nodes.size(); i++ ) {
double tmp = _rpm_nodes[i]->getDoubleValue() * _scale;
if ( tmp > rpm ) {
rpm = tmp;
}
}
double pressure = _pressure_node->getDoubleValue();
// This magic formula yields about 4 inhg at 700 rpm
suction = pressure * rpm / (rpm + 4875.0);
// simple regulator model that clamps smoothly to about 5 inhg
// over a normal rpm range
double max = (rpm > 0 ? 5.39 - 1.0 / ( rpm * 0.00111 ) : 0);
if ( suction < 0.0 ) suction = 0.0;
if ( suction > max ) suction = max;
}
_suction_node->setDoubleValue(suction);
}
// Register the subsystem.
#if 0
SGSubsystemMgr::Registrant<VacuumSystem> registrantVacuumSystem;
#endif
// end of vacuum.cxx

64
src/Systems/vacuum.hxx Normal file
View File

@@ -0,0 +1,64 @@
// vacuum.hxx - a vacuum pump connected to the aircraft engine.
// Written by David Megginson, started 2002.
//
// This file is in the Public Domain and comes with no warranty.
#ifndef __SYSTEMS_VACUUM_HXX
#define __SYSTEMS_VACUUM_HXX 1
#ifndef __cplusplus
# error This library requires C++
#endif
#include <simgear/math/sg_types.hxx>
#include <simgear/props/props.hxx>
#include <simgear/structure/subsystem_mgr.hxx>
/**
* Model a vacuum-pump system.
*
* Multiple pumps (i.e. for a multiengine aircraft) can be specified.
*
* Input properties:
*
* "rpm1"
* "rpm2"
* "..."
* /environment/pressure-inhg
* /systems/"name"/serviceable
*
* Output properties:
*
* /systems/"name"/suction-inhg
*/
class VacuumSystem : public SGSubsystem
{
public:
VacuumSystem( SGPropertyNode *node );
VacuumSystem( int i );
virtual ~VacuumSystem ();
// Subsystem API.
void bind() override;
void init() override;
void reinit() override;
void unbind() override;
void update(double dt) override;
// Subsystem identification.
static const char* staticSubsystemClassId() { return "vacuum"; }
private:
std::string _name;
int _num;
string_list _rpms;
double _scale;
SGPropertyNode_ptr _serviceable_node;
std::vector<SGPropertyNode_ptr> _rpm_nodes;
SGPropertyNode_ptr _pressure_node;
SGPropertyNode_ptr _suction_node;
};
#endif // __SYSTEMS_VACUUM_HXX