]> git.mxchange.org Git - flightgear.git/blob - src/Systems/electrical.cxx
Added code to support a richer switch definition syntax. Currently we only
[flightgear.git] / src / Systems / electrical.cxx
1 // electrical.cxx - a flexible, generic electrical system model.
2 //
3 // Written by Curtis Olson, started September 2002.
4 //
5 // Copyright (C) 2002  Curtis L. Olson  - curt@flightgear.org
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23
24 #include <simgear/structure/exception.hxx>
25 #include <simgear/misc/sg_path.hxx>
26
27 #include <Main/fg_props.hxx>
28 #include <Main/globals.hxx>
29
30 #include "electrical.hxx"
31
32
33 FGElectricalComponent::FGElectricalComponent() :
34     kind(-1),
35     name(""),
36     volts(0.0),
37     load_amps(0.0)
38 {
39 }
40
41
42 FGElectricalSupplier::FGElectricalSupplier ( SGPropertyNode *node ) {
43     kind = FG_SUPPLIER;
44
45     // cout << "Creating a supplier" << endl;
46     name = node->getStringValue("name");
47     string _model = node->getStringValue("kind");
48     // cout << "_model = " << _model << endl;
49     if ( _model == "battery" ) {
50         model = FG_BATTERY;
51     } else if ( _model == "alternator" ) {
52         model = FG_ALTERNATOR;
53     } else if ( _model == "external" ) {
54         model = FG_EXTERNAL;
55     } else {
56         model = FG_UNKNOWN;
57     }
58     volts = node->getDoubleValue("volts");
59     amps = node->getDoubleValue("amps");
60     rpm_src = node->getStringValue("rpm-source");
61
62     int i;
63     for ( i = 0; i < node->nChildren(); ++i ) {
64         SGPropertyNode *child = node->getChild(i);
65         // cout << " scanning: " << child->getName() << endl;
66         if ( !strcmp(child->getName(), "prop") ) {
67             string prop = child->getStringValue();
68             // cout << "  Adding prop = " << prop << endl;
69             add_prop( prop );
70             fgSetDouble( prop.c_str(), amps );
71         }
72     }
73
74     _rpm_node = fgGetNode( rpm_src.c_str(), true);
75 }  
76
77
78 double FGElectricalSupplier::get_output() {
79     if ( model == FG_BATTERY ) {
80         // cout << "battery amps = " << amps << endl;
81         return amps;
82     } else if ( model == FG_ALTERNATOR ) {
83         // scale alternator output for rpms < 600.  For rpms >= 600
84         // give full output.  This is just a WAG, and probably not how
85         // it really works but I'm keeping things "simple" to start.
86         double rpm = _rpm_node->getDoubleValue();
87         double factor = rpm / 600.0;
88         if ( factor > 1.0 ) {
89             factor = 1.0;
90         }
91         // cout << "alternator amps = " << amps * factor << endl;
92         return amps * factor;
93     } else if ( model == FG_EXTERNAL ) {
94         // cout << "external amps = " << 0.0 << endl;
95         return 0.0;
96     } else {
97         SG_LOG( SG_ALL, SG_ALERT, "unknown supplier type" );
98     }
99
100     return 0.0;
101 }
102
103
104 FGElectricalBus::FGElectricalBus ( SGPropertyNode *node ) {
105     kind = FG_BUS;
106
107     name = node->getStringValue("name");
108     int i;
109     for ( i = 0; i < node->nChildren(); ++i ) {
110         SGPropertyNode *child = node->getChild(i);
111         if ( !strcmp(child->getName(), "prop") ) {
112             string prop = child->getStringValue();
113             add_prop( prop );
114         }
115     }
116 }  
117
118
119 FGElectricalOutput::FGElectricalOutput ( SGPropertyNode *node ) {
120     kind = FG_OUTPUT;
121     output_amps = 0.1;
122
123     name = node->getStringValue("name");
124     int i;
125     for ( i = 0; i < node->nChildren(); ++i ) {
126         SGPropertyNode *child = node->getChild(i);
127         if ( !strcmp(child->getName(), "prop") ) {
128             string prop = child->getStringValue();
129             add_prop( prop );
130         }
131     }
132 }  
133
134
135 FGElectricalSwitch::FGElectricalSwitch( SGPropertyNode *node ) :
136     switch_node( NULL ),
137     rating_amps( 0.0f ),
138     circuit_breaker( false )
139 {
140     bool initial_state = true;
141     int i;
142     for ( i = 0; i < node->nChildren(); ++i ) {
143         SGPropertyNode *child = node->getChild(i);
144         string cname = child->getName();
145         string cval = child->getStringValue();
146         if ( cname == "prop" ) {
147             switch_node = fgGetNode( cval.c_str(), true );
148             cout << "switch node = " << cval << endl;
149         } else if ( cname == "initial-state" ) {
150             if ( cval == "off" || cval == "false" ) {
151                 initial_state = false;
152             }
153             cout << "initial state = " << initial_state << endl;
154         }            
155     }
156
157     switch_node->setBoolValue( initial_state );
158     cout << "  value = " << switch_node->getBoolValue() << endl;
159 }
160
161
162 FGElectricalConnector::FGElectricalConnector ( SGPropertyNode *node,
163                                                FGElectricalSystem *es ) {
164     kind = FG_CONNECTOR;
165     name = "connector";
166     int i;
167     for ( i = 0; i < node->nChildren(); ++i ) {
168         SGPropertyNode *child = node->getChild(i);
169         string cname = child->getName();
170         string cval = child->getStringValue();
171         // cout << "  " << cname << " = " << cval << endl;
172         if ( cname == "input" ) {
173             FGElectricalComponent *s = es->find( child->getStringValue() );
174             if ( s != NULL ) {
175                 add_input( s );
176                 if ( s->get_kind() == FG_SUPPLIER ) {
177                     s->add_output( this );
178                 } else if ( s->get_kind() == FG_BUS ) {
179                     s->add_output( this );
180                 } else {
181                     SG_LOG( SG_ALL, SG_ALERT,
182                             "Attempt to connect to something that can't provide an output: " 
183                             << child->getStringValue() );
184                 }
185             } else {
186                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
187                         << child->getStringValue() );
188             }
189         } else if ( cname == "output" ) {
190             FGElectricalComponent *s = es->find( child->getStringValue() );
191             if ( s != NULL ) {
192                 add_output( s );
193                 if ( s->get_kind() == FG_BUS ) {
194                     s->add_input( this );
195                 } else if ( s->get_kind() == FG_OUTPUT ) {
196                     s->add_input( this );
197                 } else {
198                     SG_LOG( SG_ALL, SG_ALERT,
199                             "Attempt to connect to something that can't provide an input: " 
200                             << child->getStringValue() );
201                 }
202             } else {
203                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
204                         << child->getStringValue() );
205             }
206         } else if ( cname == "switch" ) {
207             // set default value of switch to true
208             // cout << "Switch = " << child->getStringValue() << endl;
209             FGElectricalSwitch s( child );
210             // FGElectricalSwitch s( fgGetNode(child->getStringValue(), true),
211             //                       100.0f,
212             //                       false );
213             // fgSetBool( child->getStringValue(), true );
214             add_switch( s );
215         }
216     }
217 }  
218
219
220 // set all switches to the specified state
221 void FGElectricalConnector::set_switches( bool state ) {
222     // cout << "setting switch state to " << state << endl;
223     for ( unsigned int i = 0; i < switches.size(); ++i ) {
224         switches[i].set_state( state );
225     }
226 }
227
228
229 // return true if all switches are true, false otherwise.  A connector
230 // could have multiple switches, but they all need to be true(closed)
231 // for current to get through.
232 bool FGElectricalConnector::get_state() {
233     unsigned int i;
234     for ( i = 0; i < switches.size(); ++i ) {
235         if ( ! switches[i].get_state() ) {
236             return false;
237         }
238     }
239
240     return true;
241 }
242
243
244 FGElectricalSystem::FGElectricalSystem () :
245     enabled(false)
246 {
247 }
248
249
250 FGElectricalSystem::~FGElectricalSystem () {
251 }
252
253
254 void FGElectricalSystem::init () {
255     config_props = new SGPropertyNode;
256
257     SGPropertyNode *path_n = fgGetNode("/sim/systems/electrical/path");
258     _volts_out = fgGetNode( "/systems/electrical/volts", true );
259     _amps_out = fgGetNode( "/systems/electrical/amps", true );
260
261     if (path_n) {
262         SGPath config( globals->get_fg_root() );
263         config.append( path_n->getStringValue() );
264
265         SG_LOG( SG_ALL, SG_ALERT, "Reading electrical system model from "
266                 << config.str() );
267         try {
268             readProperties( config.str(), config_props );
269
270             if ( build() ) {
271                 enabled = true;
272             } else {
273                 SG_LOG( SG_ALL, SG_ALERT,
274                         "Detected an internal inconsistancy in the electrical");
275                 SG_LOG( SG_ALL, SG_ALERT,
276                         " system specification file.  See earlier errors for" );
277                 SG_LOG( SG_ALL, SG_ALERT,
278                         " details.");
279                 exit(-1);
280             }        
281         } catch (const sg_exception& exc) {
282             SG_LOG( SG_ALL, SG_ALERT, "Failed to load electrical system model: "
283                     << config.str() );
284         }
285
286     } else
287         SG_LOG( SG_ALL, SG_ALERT,
288                 "No electrical model specified for this model!");
289
290     delete config_props;
291 }
292
293
294 void FGElectricalSystem::bind () {
295 }
296
297
298 void FGElectricalSystem::unbind () {
299 }
300
301
302 void FGElectricalSystem::update (double dt) {
303     if ( !enabled ) {
304         _amps_out->setDoubleValue(0);
305         return;
306     }
307
308     // cout << "Updating electrical system" << endl;
309
310     unsigned int i;
311
312     // zero everything out before we start
313     for ( i = 0; i < suppliers.size(); ++i ) {
314         suppliers[i]->set_volts( 0.0 );
315         suppliers[i]->set_load_amps( 0.0 );
316     }
317     for ( i = 0; i < buses.size(); ++i ) {
318         buses[i]->set_volts( 0.0 );
319         buses[i]->set_load_amps( 0.0 );
320     }
321     for ( i = 0; i < outputs.size(); ++i ) {
322         outputs[i]->set_volts( 0.0 );
323         outputs[i]->set_load_amps( 0.0 );
324     }
325     for ( i = 0; i < connectors.size(); ++i ) {
326         connectors[i]->set_volts( 0.0 );
327         connectors[i]->set_load_amps( 0.0 );
328     }
329
330     // for each supplier, propagate the electrical current
331     for ( i = 0; i < suppliers.size(); ++i ) {
332         // cout << " Updating: " << suppliers[i]->get_name() << endl;
333         propagate( suppliers[i], 0.0, " " );
334     }
335
336     double alt_norm
337         = fgGetDouble("/systems/electrical/suppliers/alternator") / 60.0;
338
339     // impliment an extremely simplistic voltage model (assumes
340     // certain naming conventions in electrical system config)
341     double volts = 0.0;
342     if ( fgGetBool("/controls/switches/master-bat") ) {
343         volts = 24.0;
344     }
345     if ( fgGetBool("/controls/switches/master-alt") &&
346          fgGetDouble("/engines/engine[0]/rpm") > 800 )
347     {
348         double alt_contrib = 28.0;
349         if ( alt_contrib > volts ) {
350             volts = alt_contrib;
351         }
352     }
353     _volts_out->setDoubleValue( volts );
354
355     // impliment an extremely simplistic amps model (assumes certain
356     // naming conventions in the electrical system config) ... FIXME:
357     // make this more generic
358     double amps = 0.0;
359     if ( fgGetBool("/controls/switches/master-bat") ) {
360         if ( fgGetBool("/controls/switches/master-alt") &&
361              fgGetDouble("/engines/engine[0]/rpm") > 800 )
362         {
363             amps += 40.0 * alt_norm;
364         }
365         amps -= 15.0;            // normal load
366         if ( fgGetBool("/controls/switches/flashing-beacon") ) {
367             amps -= 7.5;
368         }
369         if ( fgGetBool("/controls/switches/nav-lights") ) {
370             amps -= 7.5;
371         }
372         if ( amps > 7.0 ) {
373             amps = 7.0;
374         }
375     }
376     _amps_out->setDoubleValue( amps );
377 }
378
379
380 bool FGElectricalSystem::build () {
381     SGPropertyNode *node;
382     int i;
383
384     int count = config_props->nChildren();
385     for ( i = 0; i < count; ++i ) {
386         node = config_props->getChild(i);
387         string name = node->getName();
388         // cout << name << endl;
389         if ( name == "supplier" ) {
390             FGElectricalSupplier *s =
391                 new FGElectricalSupplier( node );
392             suppliers.push_back( s );
393         } else if ( name == "bus" ) {
394             FGElectricalBus *b =
395                 new FGElectricalBus( node );
396             buses.push_back( b );
397         } else if ( name == "output" ) {
398             FGElectricalOutput *o =
399                 new FGElectricalOutput( node );
400             outputs.push_back( o );
401         } else if ( name == "connector" ) {
402             FGElectricalConnector *c =
403                 new FGElectricalConnector( node, this );
404             connectors.push_back( c );
405         } else {
406             SG_LOG( SG_ALL, SG_ALERT, "Unknown component type specified: " 
407                     << name );
408             return false;
409         }
410     }
411
412     return true;
413 }
414
415
416 // propagate the electrical current through the network, returns the
417 // total current drawn by the children of this node.
418 float FGElectricalSystem::propagate( FGElectricalComponent *node, double val,
419                                     string s ) {
420     s += " ";
421     
422     float current_amps = 0.0;
423
424     // determine the current to carry forward
425     double volts = 0.0;
426     if ( !fgGetBool("/systems/electrical/serviceable") ) {
427         volts = 0;
428     } else if ( node->get_kind() == FG_SUPPLIER ) {
429         // cout << s << " is a supplier" << endl;
430         volts = ((FGElectricalSupplier *)node)->get_output();
431     } else if ( node->get_kind() == FG_BUS ) {
432         // cout << s << " is a bus" << endl;
433         volts = val;
434     } else if ( node->get_kind() == FG_OUTPUT ) {
435         // cout << s << " is an output" << endl;
436         volts = val;
437         if ( volts > 1.0 ) {
438             // draw current if we have voltage
439             current_amps = ((FGElectricalOutput *)node)->get_output_amps();
440         }
441     } else if ( node->get_kind() == FG_CONNECTOR ) {
442         // cout << s << " is a connector" << endl;
443         if ( ((FGElectricalConnector *)node)->get_state() ) {
444             volts = val;
445         } else {
446             volts = 0.0;
447         }
448         // cout << s << "  val = " << volts << endl;
449     } else {
450         SG_LOG( SG_ALL, SG_ALERT, "unkown node type" );
451     }
452
453     if ( volts > node->get_volts() ) {
454         node->set_volts( volts );
455     }
456
457     int i;
458
459     // publish values to specified properties
460     for ( i = 0; i < node->get_num_props(); ++i ) {
461         fgSetDouble( node->get_prop(i).c_str(), node->get_volts() );
462     }
463     // cout << s << node->get_name() << " -> " << node->get_value() << endl;
464
465     // propagate to all children
466     for ( i = 0; i < node->get_num_outputs(); ++i ) {
467         current_amps += propagate( node->get_output(i), volts, s );
468     }
469
470     // if not an output node, register the downstream current draw
471     // with this node.  If volts are zero, current draw should be zero.
472     if ( node->get_kind() != FG_OUTPUT ) {
473         node->set_load_amps( current_amps );
474     }
475     // cout << s << node->get_name() << " -> " << current_amps << endl;
476
477     return current_amps;
478 }
479
480
481 // search for the named component and return a pointer to it, NULL otherwise
482 FGElectricalComponent *FGElectricalSystem::find ( const string &name ) {
483     unsigned int i;
484     string s;
485
486     // search suppliers
487     for ( i = 0; i < suppliers.size(); ++i ) {
488         s = suppliers[i]->get_name();
489         // cout <<  "    " << s << endl;
490         if ( s == name ) {
491             return suppliers[i];
492         }
493     }
494
495     // then search buses
496     for ( i = 0; i < buses.size(); ++i ) {
497         s = buses[i]->get_name();
498         // cout <<  "    " << s << endl;
499         if ( s == name ) {
500             return buses[i];
501         }
502     }
503
504     // then search outputs
505     for ( i = 0; i < outputs.size(); ++i ) {
506         s = outputs[i]->get_name();
507         // cout <<  "    " << s << endl;
508         if ( s == name ) {
509             return outputs[i];
510         }
511     }
512
513     // nothing found
514     return NULL;
515 }