]> git.mxchange.org Git - flightgear.git/blob - src/Systems/electrical.cxx
8e6cbc2b90d42eee8eeb325056a40ea0f37c986d
[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 FGElectricalConnector::FGElectricalConnector ( SGPropertyNode *node,
136                                                FGElectricalSystem *es ) {
137     kind = FG_CONNECTOR;
138     name = "connector";
139     int i;
140     for ( i = 0; i < node->nChildren(); ++i ) {
141         SGPropertyNode *child = node->getChild(i);
142         string cname = child->getName();
143         string cval = child->getStringValue();
144         // cout << "  " << cname << " = " << cval << endl;
145         if ( cname == "input" ) {
146             FGElectricalComponent *s = es->find( child->getStringValue() );
147             if ( s != NULL ) {
148                 add_input( s );
149                 if ( s->get_kind() == FG_SUPPLIER ) {
150                     s->add_output( this );
151                 } else if ( s->get_kind() == FG_BUS ) {
152                     s->add_output( this );
153                 } else {
154                     SG_LOG( SG_ALL, SG_ALERT,
155                             "Attempt to connect to something that can't provide an output: " 
156                             << child->getStringValue() );
157                 }
158             } else {
159                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
160                         << child->getStringValue() );
161             }
162         } else if ( cname == "output" ) {
163             FGElectricalComponent *s = es->find( child->getStringValue() );
164             if ( s != NULL ) {
165                 add_output( s );
166                 if ( s->get_kind() == FG_BUS ) {
167                     s->add_input( this );
168                 } else if ( s->get_kind() == FG_OUTPUT ) {
169                     s->add_input( this );
170                 } else {
171                     SG_LOG( SG_ALL, SG_ALERT,
172                             "Attempt to connect to something that can't provide an input: " 
173                             << child->getStringValue() );
174                 }
175             } else {
176                 SG_LOG( SG_ALL, SG_ALERT, "Can't find named source: " 
177                         << child->getStringValue() );
178             }
179         } else if ( cname == "switch" ) {
180             // set default value of switch to true
181             // cout << "Switch = " << child->getStringValue() << endl;
182             fgSetBool( child->getStringValue(), true );
183             FGElectricalSwitch s( fgGetNode(child->getStringValue(), true),
184                                   100.0f,
185                                   false );
186             add_switch( s );
187         }
188     }
189
190     // do a 2nd pass to pick up starting switch value if specified
191     for ( i = 0; i < node->nChildren(); ++i ) {
192         SGPropertyNode *child = node->getChild(i);
193         string cname = child->getName();
194         string cval = child->getStringValue();
195         // cout << "  " << cname << " = " << cval << endl;
196         if ( cname == "initial-state" ) {
197             if ( cval == "off" ) {
198                 set_switches( false );
199             } else {
200                 set_switches( true );
201             }
202         }
203     }
204 }  
205
206
207 // set all switches to the specified state
208 void FGElectricalConnector::set_switches( bool state ) {
209     // cout << "setting switch state to " << state << endl;
210     for ( unsigned int i = 0; i < switches.size(); ++i ) {
211         switches[i].set_state( state );
212     }
213 }
214
215
216 // return true if all switches are true, false otherwise.  A connector
217 // could have multiple switches, but they all need to be true(closed)
218 // for current to get through.
219 bool FGElectricalConnector::get_state() {
220     unsigned int i;
221     for ( i = 0; i < switches.size(); ++i ) {
222         if ( ! switches[i].get_state() ) {
223             return false;
224         }
225     }
226
227     return true;
228 }
229
230
231 FGElectricalSystem::FGElectricalSystem () :
232     enabled(false)
233 {
234 }
235
236
237 FGElectricalSystem::~FGElectricalSystem () {
238 }
239
240
241 void FGElectricalSystem::init () {
242     config_props = new SGPropertyNode;
243
244     SGPropertyNode *path_n = fgGetNode("/sim/systems/electrical/path");
245     _volts_out = fgGetNode( "/systems/electrical/volts", true );
246     _amps_out = fgGetNode( "/systems/electrical/amps", true );
247
248     if (path_n) {
249         SGPath config( globals->get_fg_root() );
250         config.append( path_n->getStringValue() );
251
252         SG_LOG( SG_ALL, SG_ALERT, "Reading electrical system model from "
253                 << config.str() );
254         try {
255             readProperties( config.str(), config_props );
256
257             if ( build() ) {
258                 enabled = true;
259             } else {
260                 SG_LOG( SG_ALL, SG_ALERT,
261                         "Detected an internal inconsistancy in the electrical");
262                 SG_LOG( SG_ALL, SG_ALERT,
263                         " system specification file.  See earlier errors for" );
264                 SG_LOG( SG_ALL, SG_ALERT,
265                         " details.");
266                 exit(-1);
267             }        
268         } catch (const sg_exception& exc) {
269             SG_LOG( SG_ALL, SG_ALERT, "Failed to load electrical system model: "
270                     << config.str() );
271         }
272
273     } else
274         SG_LOG( SG_ALL, SG_ALERT,
275                 "No electrical model specified for this model!");
276
277     delete config_props;
278 }
279
280
281 void FGElectricalSystem::bind () {
282 }
283
284
285 void FGElectricalSystem::unbind () {
286 }
287
288
289 void FGElectricalSystem::update (double dt) {
290     if ( !enabled ) {
291         _amps_out->setDoubleValue(0);
292         return;
293     }
294
295     // cout << "Updating electrical system" << endl;
296
297     unsigned int i;
298
299     // zero everything out before we start
300     for ( i = 0; i < suppliers.size(); ++i ) {
301         suppliers[i]->set_volts( 0.0 );
302         suppliers[i]->set_load_amps( 0.0 );
303     }
304     for ( i = 0; i < buses.size(); ++i ) {
305         buses[i]->set_volts( 0.0 );
306         buses[i]->set_load_amps( 0.0 );
307     }
308     for ( i = 0; i < outputs.size(); ++i ) {
309         outputs[i]->set_volts( 0.0 );
310         outputs[i]->set_load_amps( 0.0 );
311     }
312     for ( i = 0; i < connectors.size(); ++i ) {
313         connectors[i]->set_volts( 0.0 );
314         connectors[i]->set_load_amps( 0.0 );
315     }
316
317     // for each supplier, propagate the electrical current
318     for ( i = 0; i < suppliers.size(); ++i ) {
319         // cout << " Updating: " << suppliers[i]->get_name() << endl;
320         propagate( suppliers[i], 0.0, " " );
321     }
322
323     double alt_norm
324         = fgGetDouble("/systems/electrical/suppliers/alternator") / 60.0;
325
326     // impliment an extremely simplistic voltage model (assumes
327     // certain naming conventions in electrical system config)
328     double volts = 0.0;
329     if ( fgGetBool("/controls/switches/master-bat") ) {
330         volts = 24.0;
331     }
332     if ( fgGetBool("/controls/switches/master-alt") &&
333          fgGetDouble("/engines/engine[0]/rpm") > 800 )
334     {
335         double alt_contrib = 28.0;
336         if ( alt_contrib > volts ) {
337             volts = alt_contrib;
338         }
339     }
340     _volts_out->setDoubleValue( volts );
341
342     // impliment an extremely simplistic amps model (assumes certain
343     // naming conventions in the electrical system config) ... FIXME:
344     // make this more generic
345     double amps = 0.0;
346     if ( fgGetBool("/controls/switches/master-bat") ) {
347         if ( fgGetBool("/controls/switches/master-alt") &&
348              fgGetDouble("/engines/engine[0]/rpm") > 800 )
349         {
350             amps += 40.0 * alt_norm;
351         }
352         amps -= 15.0;            // normal load
353         if ( fgGetBool("/controls/switches/flashing-beacon") ) {
354             amps -= 7.5;
355         }
356         if ( fgGetBool("/controls/switches/nav-lights") ) {
357             amps -= 7.5;
358         }
359         if ( amps > 7.0 ) {
360             amps = 7.0;
361         }
362     }
363     _amps_out->setDoubleValue( amps );
364 }
365
366
367 bool FGElectricalSystem::build () {
368     SGPropertyNode *node;
369     int i;
370
371     int count = config_props->nChildren();
372     for ( i = 0; i < count; ++i ) {
373         node = config_props->getChild(i);
374         string name = node->getName();
375         // cout << name << endl;
376         if ( name == "supplier" ) {
377             FGElectricalSupplier *s =
378                 new FGElectricalSupplier( node );
379             suppliers.push_back( s );
380         } else if ( name == "bus" ) {
381             FGElectricalBus *b =
382                 new FGElectricalBus( node );
383             buses.push_back( b );
384         } else if ( name == "output" ) {
385             FGElectricalOutput *o =
386                 new FGElectricalOutput( node );
387             outputs.push_back( o );
388         } else if ( name == "connector" ) {
389             FGElectricalConnector *c =
390                 new FGElectricalConnector( node, this );
391             connectors.push_back( c );
392         } else {
393             SG_LOG( SG_ALL, SG_ALERT, "Unknown component type specified: " 
394                     << name );
395             return false;
396         }
397     }
398
399     return true;
400 }
401
402
403 // propagate the electrical current through the network, returns the
404 // total current drawn by the children of this node.
405 float FGElectricalSystem::propagate( FGElectricalComponent *node, double val,
406                                     string s ) {
407     s += " ";
408     
409     float current_amps = 0.0;
410
411     // determine the current to carry forward
412     double volts = 0.0;
413     if ( !fgGetBool("/systems/electrical/serviceable") ) {
414         volts = 0;
415     } else if ( node->get_kind() == FG_SUPPLIER ) {
416         // cout << s << " is a supplier" << endl;
417         volts = ((FGElectricalSupplier *)node)->get_output();
418     } else if ( node->get_kind() == FG_BUS ) {
419         // cout << s << " is a bus" << endl;
420         volts = val;
421     } else if ( node->get_kind() == FG_OUTPUT ) {
422         // cout << s << " is an output" << endl;
423         volts = val;
424         if ( volts > 1.0 ) {
425             // draw current if we have voltage
426             current_amps = ((FGElectricalOutput *)node)->get_output_amps();
427         }
428     } else if ( node->get_kind() == FG_CONNECTOR ) {
429         // cout << s << " is a connector" << endl;
430         if ( ((FGElectricalConnector *)node)->get_state() ) {
431             volts = val;
432         } else {
433             volts = 0.0;
434         }
435         // cout << s << "  val = " << volts << endl;
436     } else {
437         SG_LOG( SG_ALL, SG_ALERT, "unkown node type" );
438     }
439
440     if ( volts > node->get_volts() ) {
441         node->set_volts( volts );
442     }
443
444     int i;
445
446     // publish values to specified properties
447     for ( i = 0; i < node->get_num_props(); ++i ) {
448         fgSetDouble( node->get_prop(i).c_str(), node->get_volts() );
449     }
450     // cout << s << node->get_name() << " -> " << node->get_value() << endl;
451
452     // propagate to all children
453     for ( i = 0; i < node->get_num_outputs(); ++i ) {
454         current_amps += propagate( node->get_output(i), volts, s );
455     }
456
457     // if not an output node, register the downstream current draw
458     // with this node.  If volts are zero, current draw should be zero.
459     if ( node->get_kind() != FG_OUTPUT ) {
460         node->set_load_amps( current_amps );
461     }
462     // cout << s << node->get_name() << " -> " << current_amps << endl;
463
464     return current_amps;
465 }
466
467
468 // search for the named component and return a pointer to it, NULL otherwise
469 FGElectricalComponent *FGElectricalSystem::find ( const string &name ) {
470     unsigned int i;
471     string s;
472
473     // search suppliers
474     for ( i = 0; i < suppliers.size(); ++i ) {
475         s = suppliers[i]->get_name();
476         // cout <<  "    " << s << endl;
477         if ( s == name ) {
478             return suppliers[i];
479         }
480     }
481
482     // then search buses
483     for ( i = 0; i < buses.size(); ++i ) {
484         s = buses[i]->get_name();
485         // cout <<  "    " << s << endl;
486         if ( s == name ) {
487             return buses[i];
488         }
489     }
490
491     // then search outputs
492     for ( i = 0; i < outputs.size(); ++i ) {
493         s = outputs[i]->get_name();
494         // cout <<  "    " << s << endl;
495         if ( s == name ) {
496             return outputs[i];
497         }
498     }
499
500     // nothing found
501     return NULL;
502 }