1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 Module: FGPropulsion.cpp
6 Purpose: Encapsulates the set of engines and tanks associated
9 ------------- Copyright (C) 2000 Jon S. Berndt (jsb@hal-pc.org) -------------
11 This program is free software; you can redistribute it and/or modify it under
12 the terms of the GNU General Public License as published by the Free Software
13 Foundation; either version 2 of the License, or (at your option) any later
16 This program is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
18 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
21 You should have received a copy of the GNU General Public License along with
22 this program; if not, write to the Free Software Foundation, Inc., 59 Temple
23 Place - Suite 330, Boston, MA 02111-1307, USA.
25 Further information about the GNU General Public License can also be found on
26 the world wide web at http://www.gnu.org.
28 FUNCTIONAL DESCRIPTION
29 --------------------------------------------------------------------------------
30 The Propulsion class is the container for the entire propulsion system, which is
31 comprised of engines and tanks. Once the Propulsion class gets the config file,
32 it reads in information which is specific to a type of engine. Then:
34 1) The appropriate engine type instance is created
35 2) At least one tank object is created, and is linked to an engine.
37 At Run time each engines Calculate() method is called.
40 --------------------------------------------------------------------------------
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
47 #include "FGPropulsion.h"
49 #include "FGTurbine.h"
51 #include "FGElectric.h"
52 #include "FGPropertyManager.h"
56 static const char *IdSrc = "$Id$";
57 static const char *IdHdr = ID_PROPULSION;
59 extern short debug_lvl;
62 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
66 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
68 Name = "FGPropulsion";
70 numSelectedFuelTanks = numSelectedOxiTanks = 0;
71 numTanks = numEngines = 0;
72 numOxiTanks = numFuelTanks = 0;
73 ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
82 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
84 FGPropulsion::~FGPropulsion()
86 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
92 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
94 bool FGPropulsion::Run(void)
96 if (FGModel::Run()) return true;
98 double dt = State->Getdt();
100 vForces.InitMatrix();
101 vMoments.InitMatrix();
103 for (unsigned int i=0; i<numEngines; i++) {
104 Engines[i]->Calculate();
105 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
106 vMoments += Engines[i]->GetMoments(); // sum body frame moments
109 for (unsigned int i=0; i<numTanks; i++) {
110 Tanks[i]->Calculate( dt * rate );
113 if (refuel) DoRefuel( dt * rate );
118 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
120 bool FGPropulsion::GetSteadyState(void)
122 double currentThrust = 0, lastThrust=-1;
123 int steady_count,j=0;
126 vForces.InitMatrix();
127 vMoments.InitMatrix();
129 if (!FGModel::Run()) {
130 for (unsigned int i=0; i<numEngines; i++) {
131 Engines[i]->SetTrimMode(true);
134 while (!steady && j < 6000) {
135 Engines[i]->Calculate();
136 lastThrust = currentThrust;
137 currentThrust = Engines[i]->GetThrust();
138 if (fabs(lastThrust-currentThrust) < 0.0001) {
140 if (steady_count > 120) { steady=true; }
146 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
147 vMoments += Engines[i]->GetMoments(); // sum body frame moments
148 Engines[i]->SetTrimMode(false);
157 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
159 bool FGPropulsion::ICEngineStart(void)
163 vForces.InitMatrix();
164 vMoments.InitMatrix();
166 for (unsigned int i=0; i<numEngines; i++) {
167 Engines[i]->SetTrimMode(true);
169 while (!Engines[i]->GetRunning() && j < 2000) {
170 Engines[i]->Calculate();
173 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
174 vMoments += Engines[i]->GetMoments(); // sum body frame moments
175 Engines[i]->SetTrimMode(false);
180 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
182 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
184 string token, fullpath, localpath;
185 string engineFileName, engType;
187 string enginePath = FDMExec->GetEnginePath();
188 string aircraftPath = FDMExec->GetAircraftPath();
189 double xLoc, yLoc, zLoc, Pitch, Yaw;
191 bool ThrottleAdded = false;
192 FGConfigFile* Cfg_ptr = 0;
195 fullpath = enginePath + "/";
196 localpath = aircraftPath + "/Engines/";
198 fullpath = enginePath + ";";
199 localpath = aircraftPath + ";Engines;";
202 AC_cfg->GetNextConfigLine();
204 while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
206 if (token == "AC_ENGINE") { // ============ READING ENGINES
208 engineFileName = AC_cfg->GetValue("FILE");
210 // Look in the Aircraft/Engines directory first
212 FGConfigFile Local_cfg(localpath + engineFileName + ".xml");
213 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
214 if (Local_cfg.IsOpen()) {
215 Cfg_ptr = &Local_cfg;
216 if (debug_lvl > 0) cout << "\n Reading engine from file: " << localpath
217 + engineFileName + ".xml"<< endl;
219 if (Eng_cfg.IsOpen()) {
221 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
222 + engineFileName + ".xml"<< endl;
227 Cfg_ptr->GetNextConfigLine();
228 engType = Cfg_ptr->GetValue();
231 ThrottleAdded = true;
233 if (engType == "FG_ROCKET") {
234 Engines.push_back(new FGRocket(FDMExec, Cfg_ptr, numEngines));
235 } else if (engType == "FG_PISTON") {
236 Engines.push_back(new FGPiston(FDMExec, Cfg_ptr, numEngines));
237 } else if (engType == "FG_TURBINE") {
238 Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr, numEngines));
239 } else if (engType == "FG_SIMTURBINE") {
241 cerr << "The FG_SIMTURBINE engine type has been renamed to FG_TURBINE." << endl;
242 cerr << "To fix this problem, simply replace the FG_SIMTURBINE name " << endl;
243 cerr << "in your engine file to FG_TURBINE." << endl;
245 Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr, numEngines));
246 } else if (engType == "FG_ELECTRIC") {
247 Engines.push_back(new FGElectric(FDMExec, Cfg_ptr, numEngines));
249 cerr << fgred << " Unrecognized engine type: " << underon << engType
250 << underoff << " found in config file." << fgdef << endl;
254 AC_cfg->GetNextConfigLine();
255 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
257 if (token == "XLOC") { *AC_cfg >> xLoc; }
258 else if (token == "YLOC") { *AC_cfg >> yLoc; }
259 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
260 else if (token == "PITCH") { *AC_cfg >> Pitch;}
261 else if (token == "YAW") { *AC_cfg >> Yaw; }
262 else if (token.find("AC_THRUSTER") != string::npos) {
263 if (debug_lvl > 0) cout << "\n Reading thruster definition" << endl;
264 Engines.back()->LoadThruster(AC_cfg);
265 AC_cfg->GetNextConfigLine();
267 else if (token == "FEED") {
269 Engines[numEngines]->AddFeedTank(Feed);
270 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
271 } else cerr << "Unknown identifier: " << token << " in engine file: "
272 << engineFileName << endl;
276 cout << " X = " << xLoc << endl;
277 cout << " Y = " << yLoc << endl;
278 cout << " Z = " << zLoc << endl;
279 cout << " Pitch = " << Pitch << endl;
280 cout << " Yaw = " << Yaw << endl;
283 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
288 cerr << fgred << "\n Could not read engine config file: " << underon <<
289 engineFileName + ".xml" << underoff << fgdef << endl;
293 } else if (token == "AC_TANK") { // ============== READING TANKS
295 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
296 Tanks.push_back(new FGTank(AC_cfg, FDMExec));
297 switch(Tanks[numTanks]->GetType()) {
299 numSelectedFuelTanks++;
302 case FGTank::ttOXIDIZER:
303 numSelectedOxiTanks++;
310 AC_cfg->GetNextConfigLine();
313 CalculateTankInertias();
314 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
319 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
321 string FGPropulsion::GetPropulsionStrings(void)
323 string PropulsionStrings = "";
324 bool firstime = true;
326 for (unsigned int i=0;i<Engines.size();i++) {
327 if (firstime) firstime = false;
328 else PropulsionStrings += ", ";
330 PropulsionStrings += Engines[i]->GetEngineLabels();
333 return PropulsionStrings;
336 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
338 string FGPropulsion::GetPropulsionValues(void)
340 string PropulsionValues = "";
341 bool firstime = true;
343 for (unsigned int i=0;i<Engines.size();i++) {
344 if (firstime) firstime = false;
345 else PropulsionValues += ", ";
347 PropulsionValues += Engines[i]->GetEngineValues();
350 return PropulsionValues;
353 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
355 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
357 iTank = Tanks.begin();
358 vXYZtank_arm.InitMatrix();
359 while (iTank < Tanks.end()) {
360 vXYZtank_arm(eX) += (*iTank)->GetXYZ(eX)*(*iTank)->GetContents();
361 vXYZtank_arm(eY) += (*iTank)->GetXYZ(eY)*(*iTank)->GetContents();
362 vXYZtank_arm(eZ) += (*iTank)->GetXYZ(eZ)*(*iTank)->GetContents();
368 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
370 double FGPropulsion::GetTanksWeight(void)
374 iTank = Tanks.begin();
375 while (iTank < Tanks.end()) {
376 Tw += (*iTank)->GetContents();
382 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
384 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
389 if (size == 0) return tankJ;
391 tankJ = FGMatrix33();
393 for (unsigned int i=0; i<size; i++)
394 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
395 Tanks[i]->GetXYZ() );
400 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402 void FGPropulsion::SetMagnetos(int setting)
404 if (ActiveEngine < 0) {
405 for (unsigned i=0; i<Engines.size(); i++) {
406 ((FGPiston*)Engines[i])->SetMagnetos(setting);
409 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
413 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415 void FGPropulsion::SetStarter(int setting)
417 if (ActiveEngine < 0) {
418 for (unsigned i=0; i<Engines.size(); i++) {
420 Engines[i]->SetStarter(false);
422 Engines[i]->SetStarter(true);
426 Engines[ActiveEngine]->SetStarter(false);
428 Engines[ActiveEngine]->SetStarter(true);
432 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
434 void FGPropulsion::SetCutoff(int setting)
436 if (ActiveEngine < 0) {
437 for (unsigned i=0; i<Engines.size(); i++) {
439 ((FGTurbine*)Engines[i])->SetCutoff(false);
441 ((FGTurbine*)Engines[i])->SetCutoff(true);
445 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
447 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
451 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
453 void FGPropulsion::SetActiveEngine(int engine)
455 if (engine >= Engines.size() || engine < 0)
458 ActiveEngine = engine;
461 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463 double FGPropulsion::Transfer(int source, int target, double amount)
465 double shortage, overage;
470 shortage = Tanks[source]->Drain(amount);
475 overage = Tanks[target]->Fill(amount - shortage);
480 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
482 void FGPropulsion::DoRefuel(double time_slice)
484 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
485 int TanksNotFull = 0;
487 for (unsigned int i=0; i<numTanks; i++) {
488 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
492 for (unsigned int i=0; i<numTanks; i++) {
493 if (Tanks[i]->GetPctFull() < 99.99)
494 Transfer(-1, i, fillrate/TanksNotFull);
499 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
501 void FGPropulsion::bind(void)
503 typedef double (FGPropulsion::*PMF)(int) const;
504 typedef int (FGPropulsion::*iPMF)(void) const;
506 PropertyManager->Tie("propulsion/magneto_cmd", this,
507 (iPMF)0, &FGPropulsion::SetMagnetos, true);
508 PropertyManager->Tie("propulsion/starter_cmd", this,
509 (iPMF)0, &FGPropulsion::SetStarter, true);
510 PropertyManager->Tie("propulsion/cutoff_cmd", this,
511 (iPMF)0, &FGPropulsion::SetCutoff, true);
513 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
514 (PMF)&FGPropulsion::GetForces);
515 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
516 (PMF)&FGPropulsion::GetForces);
517 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
518 (PMF)&FGPropulsion::GetForces);
519 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
520 (PMF)&FGPropulsion::GetMoments);
521 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
522 (PMF)&FGPropulsion::GetMoments);
523 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
524 (PMF)&FGPropulsion::GetMoments);
526 PropertyManager->Tie("propulsion/active_engine", this,
527 (iPMF)&FGPropulsion::GetActiveEngine, &FGPropulsion::SetActiveEngine, true);
530 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
532 void FGPropulsion::unbind(void)
534 PropertyManager->Untie("propulsion/magneto_cmd");
535 PropertyManager->Untie("propulsion/starter_cmd");
536 PropertyManager->Untie("propulsion/cutoff_cmd");
537 PropertyManager->Untie("propulsion/active_engine");
538 PropertyManager->Untie("forces/fbx-prop-lbs");
539 PropertyManager->Untie("forces/fby-prop-lbs");
540 PropertyManager->Untie("forces/fbz-prop-lbs");
541 PropertyManager->Untie("moments/l-prop-lbsft");
542 PropertyManager->Untie("moments/m-prop-lbsft");
543 PropertyManager->Untie("moments/n-prop-lbsft");
546 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
547 // The bitmasked value choices are as follows:
548 // unset: In this case (the default) JSBSim would only print
549 // out the normally expected messages, essentially echoing
550 // the config files as they are read. If the environment
551 // variable is not set, debug_lvl is set to 1 internally
552 // 0: This requests JSBSim not to output any messages
554 // 1: This value explicity requests the normal JSBSim
556 // 2: This value asks for a message to be printed out when
557 // a class is instantiated
558 // 4: When this value is set, a message is displayed when a
559 // FGModel object executes its Run() method
560 // 8: When this value is set, various runtime state variables
561 // are printed out periodically
562 // 16: When set various parameters are sanity checked and
563 // a message is printed out when they go out of bounds
565 void FGPropulsion::Debug(int from)
567 if (debug_lvl <= 0) return;
569 if (debug_lvl & 1) { // Standard console startup message output
570 if (from == 0) { // Constructor
574 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
575 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
576 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
578 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
580 if (debug_lvl & 8 ) { // Runtime state variables
582 if (debug_lvl & 16) { // Sanity checking
584 if (debug_lvl & 64) {
585 if (from == 0) { // Constructor
586 cout << IdSrc << endl;
587 cout << IdHdr << endl;