16#include <simgear/structure/exception.hxx>
17#include <simgear/misc/sg_path.hxx>
18#include <simgear/debug/logstream.hxx>
19#include <simgear/props/props_io.hxx>
44 for (
const auto& nd :
props) {
53 name = node->getStringValue(
"name");
54 std::string _model = node->getStringValue(
"kind");
56 if ( _model ==
"battery" ) {
58 amp_hours = node->getFloatValue(
"amp-hours", 40.0);
59 percent_remaining = node->getFloatValue(
"percent-remaining", 1.0);
60 charge_amps = node->getFloatValue(
"charge-amps", 7.0);
61 }
else if ( _model ==
"alternator" ) {
63 rpm_src = node->getStringValue(
"rpm-source");
64 rpm_threshold = node->getFloatValue(
"rpm-threshold", 600.0);
65 ideal_amps = node->getFloatValue(
"amps", 60.0);
66 }
else if ( _model ==
"external" ) {
68 ideal_amps = node->getFloatValue(
"amps", 60.0);
72 ideal_volts = node->getFloatValue(
"volts");
75 for (
i = 0;
i < node->nChildren(); ++
i ) {
76 SGPropertyNode *child = node->getChild(
i);
78 if ( child->getNameString() ==
"prop" ) {
79 std::string prop = child->getStringValue();
86 _rpm_node =
fgGetNode( rpm_src.c_str(),
true);
93 float amphrs_used = amps * dt / 3600.0;
96 float percent_used = amphrs_used / amp_hours;
97 percent_remaining -= percent_used;
98 if ( percent_remaining < 0.0 ) {
99 percent_remaining = 0.0;
100 }
else if ( percent_remaining > 1.0 ) {
101 percent_remaining = 1.0;
104 return amp_hours * percent_remaining;
109 float rpm = _rpm_node->getFloatValue();
110 float factor = rpm / rpm_threshold;
111 if ( factor > 1.0 ) {
122 SG_LOG( SG_SYSTEMS, SG_ALERT,
"unknown supplier type" );
132 float x = 1.0 - percent_remaining;
133 float tmp = -(3.0 * x - 1.0);
134 float factor = (tmp*tmp*tmp*tmp*tmp + 32) / 32;
138 return ideal_volts * factor;
143 float rpm = _rpm_node->getFloatValue();
144 float factor = rpm / rpm_threshold;
145 if ( factor > 1.0 ) {
149 return ideal_volts * factor;
154 SG_LOG( SG_SYSTEMS, SG_ALERT,
"unknown supplier type" );
167 return amp_hours * 60.0;
172 float rpm = _rpm_node->getFloatValue();
173 float factor = rpm / rpm_threshold;
174 if ( factor > 1.0 ) {
178 return ideal_amps * factor;
183 SG_LOG( SG_SYSTEMS, SG_ALERT,
"unknown supplier type" );
193 name = node->getStringValue(
"name");
195 for (
i = 0;
i < node->nChildren(); ++
i ) {
196 SGPropertyNode *child = node->getChild(
i);
197 if ( child->getNameString() ==
"prop" ) {
198 std::string prop = child->getStringValue();
209 name = node->getStringValue(
"name");
210 SGPropertyNode *draw = node->getNode(
"rated-draw");
211 if ( draw != NULL ) {
217 for (
i = 0;
i < node->nChildren(); ++
i ) {
218 SGPropertyNode *child = node->getChild(
i);
219 if ( child->getNameString() ==
"prop" ) {
220 std::string prop = child->getStringValue();
230 circuit_breaker( false )
232 bool initial_state =
true;
234 for (
i = 0;
i < node->nChildren(); ++
i ) {
235 SGPropertyNode *child = node->getChild(
i);
236 std::string cname = child->getNameString();
237 std::string cval = child->getStringValue();
238 if ( cname ==
"prop" ) {
239 switch_node =
fgGetNode( cval.c_str(),
true );
241 }
else if ( cname ==
"initial-state" ) {
242 if ( cval ==
"off" || cval ==
"false" ) {
243 initial_state =
false;
246 }
else if ( cname ==
"rating-amps" ) {
247 rating_amps =
atof( cval.c_str() );
248 circuit_breaker =
true;
253 switch_node->setBoolValue( initial_state );
263 for (
i = 0;
i < node->nChildren(); ++
i ) {
264 SGPropertyNode *child = node->getChild(
i);
265 std::string cname = child->getNameString();
266 std::string cval = child->getStringValue();
268 if ( cname ==
"input" ) {
277 SG_LOG( SG_SYSTEMS, SG_ALERT,
278 "Attempt to connect to something that can't provide an output: "
279 << child->getStringValue() );
282 SG_LOG( SG_SYSTEMS, SG_ALERT,
"Can't find named source: "
283 << child->getStringValue() );
285 }
else if ( cname ==
"output" ) {
298 SG_LOG( SG_SYSTEMS, SG_ALERT,
299 "Attempt to connect to something that can't provide an input: "
300 << child->getStringValue() );
303 SG_LOG( SG_SYSTEMS, SG_ALERT,
"Can't find named source: "
304 << child->getStringValue() );
306 }
else if ( cname ==
"switch" ) {
318 for (
unsigned int i = 0;
i < switches.size(); ++
i ) {
319 switches[
i].set_state( state );
329 for (
i = 0;
i < switches.size(); ++
i ) {
340 name(node->getStringValue(
"name",
"electrical")),
341 num(node->getIntValue(
"number", 0)),
342 path(node->getStringValue(
"path")),
350 SG_LOG(SG_SYSTEMS, SG_INFO,
"Destroying elec system");
355 SGPropertyNode_ptr config_props =
new SGPropertyNode;
357 _volts_out =
fgGetNode(
"/systems/electrical/volts",
true );
358 _amps_out =
fgGetNode(
"/systems/electrical/amps",
true );
364 SGPropertyNode *path_n =
fgGetNode(
"/sim/systems/electrical/path");
366 if ( path.length() ) {
367 SG_LOG( SG_SYSTEMS, SG_INFO,
368 "NOTICE: System manager configuration specifies an " <<
369 "electrical system: " << path <<
" but it is " <<
370 "being overridden by the one specified in the -set.xml " <<
371 "file: " << path_n->getStringValue() );
374 path = path_n->getStringValue();
377 if ( path.length() ) {
378 SGPath config =
globals->resolve_aircraft_path(path);
379 if (!config.exists()) {
380 SG_LOG( SG_SYSTEMS, SG_ALERT,
"Failed to find electrical system model: " << config );
385 SG_LOG( SG_SYSTEMS, SG_DEV_WARN,
386 "Reading deprecated xml electrical system model from\n "
389 readProperties( config, config_props );
391 if (
build(config_props) ) {
394 throw sg_exception(
"Logic error in electrical system file.");
396 }
catch (
const sg_exception&) {
397 SG_LOG( SG_SYSTEMS, SG_ALERT,
398 "Failed to load electrical system model: "
402 SG_LOG( SG_SYSTEMS, SG_INFO,
403 "No xml-based electrical model specified for this model!");
407 _amps_out->setDoubleValue(0);
415 _serviceable_node =
fgGetNode(
"/systems/electrical/serviceable",
true);
421 _serviceable_node.reset();
426void FGElectricalSystem::deleteComponents(comp_list& comps)
428 std::for_each(comps.begin(), comps.end(),
438 deleteComponents(suppliers);
439 deleteComponents(buses);
440 deleteComponents(outputs);
441 deleteComponents(connectors);
451 _serviceable = _serviceable_node->getBoolValue();
457 for (
i = 0;
i < suppliers.size(); ++
i ) {
458 suppliers[
i]->set_volts( 0.0 );
460 for (
i = 0;
i < buses.size(); ++
i ) {
461 buses[
i]->set_volts( 0.0 );
463 for (
i = 0;
i < outputs.size(); ++
i ) {
464 outputs[
i]->set_volts( 0.0 );
466 for (
i = 0;
i < connectors.size(); ++
i ) {
467 connectors[
i]->set_volts( 0.0 );
471 for (
i = 0;
i < suppliers.size(); ++
i ) {
483 SG_LOG(SG_SYSTEMS, SG_ALERT,
484 "Error drawing more current than available!");
491 for (
i = 0;
i < suppliers.size(); ++
i ) {
503 SG_LOG(SG_SYSTEMS, SG_ALERT,
504 "Error drawing more current than available!");
511 for (
i = 0;
i < suppliers.size(); ++
i ) {
524 SG_LOG(SG_SYSTEMS, SG_ALERT,
525 "Error drawing more current than available!");
531 =
fgGetFloat(
"/systems/electrical/suppliers/alternator") / 60.0;
538 if (
fgGetBool(
"/controls/engines/engine[0]/master-bat") ) {
541 if (
fgGetBool(
"/controls/engines/engine[0]/master-alt") ) {
542 if (
fgGetFloat(
"/engines/engine[0]/rpm") > 800 ) {
543 float alt_contrib = 28.0;
544 if ( alt_contrib > volts ) {
547 }
else if (
fgGetFloat(
"/engines/engine[0]/rpm") > 200 ) {
548 float alt_contrib = 20.0;
549 if ( alt_contrib > volts ) {
554 _volts_out->setFloatValue( volts );
560 if (
fgGetBool(
"/controls/engines/engine[0]/master-bat") ) {
561 if (
fgGetBool(
"/controls/engines/engine[0]/master-alt") &&
564 amps += 40.0 * alt_norm;
567 if (
fgGetBool(
"/controls/switches/flashing-beacon") ) {
570 if (
fgGetBool(
"/controls/switches/nav-lights") ) {
577 _amps_out->setFloatValue( amps );
582 SGPropertyNode *node;
585 int count = config_props->nChildren();
586 for (
i = 0;
i < count; ++
i ) {
587 node = config_props->getChild(
i);
588 std::string name = node->getNameString();
590 if ( name ==
"supplier" ) {
593 suppliers.push_back( s );
594 }
else if ( name ==
"bus" ) {
597 buses.push_back( b );
598 }
else if ( name ==
"output" ) {
601 outputs.push_back( o );
602 }
else if ( name ==
"connector" ) {
605 connectors.push_back( c );
607 SG_LOG( SG_SYSTEMS, SG_ALERT,
"Unknown component type specified: "
620 float input_volts,
float input_amps,
624 float total_load = 0.0;
628 if ( !_serviceable) {
636 if ( battery_volts < (input_volts - 0.1) ) {
665 SG_LOG( SG_SYSTEMS, SG_ALERT,
"unknown node type" );
718 for (
i = 0;
i < suppliers.size(); ++
i ) {
719 s = suppliers[
i]->get_name();
727 for (
i = 0;
i < buses.size(); ++
i ) {
728 s = buses[
i]->get_name();
736 for (
i = 0;
i < outputs.size(); ++
i ) {
737 s = outputs[
i]->get_name();
751SGSubsystemMgr::Registrant<FGElectricalSystem> registrantFGElectricalSystem;
FGElectricalBus(SGPropertyNode *node)
void add_input(FGElectricalComponent *c)
void set_volts(float val)
simgear::PropertyList props
void add_prop(const std::string &s)
int get_num_outputs() const
void set_load_amps(float val)
float get_load_amps() const
void set_available_amps(float val)
FGElectricalComponent * get_output(const int i)
void publishVoltageToProps() const
void add_output(FGElectricalComponent *c)
FGElectricalConnector(SGPropertyNode *node, FGElectricalSystem *es)
void set_switches(bool state)
void add_switch(FGElectricalSwitch s)
FGElectricalOutput(SGPropertyNode *node)
FGElectricalSupplier(SGPropertyNode *node)
float get_charge_amps() const
float apply_load(float amps, float dt)
FGSupplierType get_model() const
FGElectricalSwitch(SGPropertyNode *node)
Model an electrical system.
bool build(SGPropertyNode *config_props)
float propagate(FGElectricalComponent *node, double dt, float input_volts, float input_amps, std::string s="")
void update(double dt) override
FGElectricalSystem(SGPropertyNode *node)
FGElectricalComponent * find(const std::string &name)
virtual ~FGElectricalSystem()
static double atof(const string &str)
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
float fgGetFloat(const char *name, float defaultValue)
Get a float value for a property.
bool fgSetFloat(const char *name, float val)
Set a float value for a property.