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"
57 static const char *IdSrc = "$Id$";
58 static const char *IdHdr = ID_PROPULSION;
60 extern short debug_lvl;
63 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
67 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
69 Name = "FGPropulsion";
71 numSelectedFuelTanks = numSelectedOxiTanks = 0;
72 numTanks = numEngines = 0;
73 numOxiTanks = numFuelTanks = 0;
74 ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
84 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86 FGPropulsion::~FGPropulsion()
88 for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
94 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
96 bool FGPropulsion::Run(void)
100 if (FGModel::Run()) return true;
102 double dt = State->Getdt();
104 vForces.InitMatrix();
105 vMoments.InitMatrix();
107 for (i=0; i<numEngines; i++) {
108 Engines[i]->Calculate();
109 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
110 vMoments += Engines[i]->GetMoments(); // sum body frame moments
113 for (i=0; i<numTanks; i++) {
114 Tanks[i]->Calculate( dt * rate );
117 if (refuel) DoRefuel( dt * rate );
122 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
124 bool FGPropulsion::GetSteadyState(void)
126 double currentThrust = 0, lastThrust=-1;
127 int steady_count,j=0;
130 vForces.InitMatrix();
131 vMoments.InitMatrix();
133 if (!FGModel::Run()) {
134 for (unsigned int i=0; i<numEngines; i++) {
135 Engines[i]->SetTrimMode(true);
138 while (!steady && j < 6000) {
139 Engines[i]->Calculate();
140 lastThrust = currentThrust;
141 currentThrust = Engines[i]->GetThrust();
142 if (fabs(lastThrust-currentThrust) < 0.0001) {
144 if (steady_count > 120) { steady=true; }
150 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
151 vMoments += Engines[i]->GetMoments(); // sum body frame moments
152 Engines[i]->SetTrimMode(false);
161 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
163 bool FGPropulsion::ICEngineStart(void)
167 vForces.InitMatrix();
168 vMoments.InitMatrix();
170 for (unsigned int i=0; i<numEngines; i++) {
171 Engines[i]->SetTrimMode(true);
173 while (!Engines[i]->GetRunning() && j < 2000) {
174 Engines[i]->Calculate();
177 vForces += Engines[i]->GetBodyForces(); // sum body frame forces
178 vMoments += Engines[i]->GetMoments(); // sum body frame moments
179 Engines[i]->SetTrimMode(false);
184 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
186 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
188 string token, fullpath, localpath;
189 string engineFileName, engType;
191 string enginePath = FDMExec->GetEnginePath();
192 string aircraftPath = FDMExec->GetAircraftPath();
193 double xLoc, yLoc, zLoc, Pitch, Yaw;
195 bool ThrottleAdded = false;
196 FGConfigFile* Cfg_ptr = 0;
199 fullpath = enginePath + "/";
200 localpath = aircraftPath + "/Engines/";
202 fullpath = enginePath + ";";
203 localpath = aircraftPath + ";Engines;";
206 AC_cfg->GetNextConfigLine();
208 while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
210 if (token == "AC_ENGINE") { // ============ READING ENGINES
212 engineFileName = AC_cfg->GetValue("FILE");
214 // Look in the Aircraft/Engines directory first
216 FGConfigFile Local_cfg(localpath + engineFileName + ".xml");
217 FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
218 if (Local_cfg.IsOpen()) {
219 Cfg_ptr = &Local_cfg;
220 if (debug_lvl > 0) cout << "\n Reading engine from file: " << localpath
221 + engineFileName + ".xml"<< endl;
223 if (Eng_cfg.IsOpen()) {
225 if (debug_lvl > 0) cout << "\n Reading engine from file: " << fullpath
226 + engineFileName + ".xml"<< endl;
231 Cfg_ptr->GetNextConfigLine();
232 engType = Cfg_ptr->GetValue();
235 ThrottleAdded = true;
237 if (engType == "FG_ROCKET") {
238 Engines.push_back(new FGRocket(FDMExec, Cfg_ptr, numEngines));
239 } else if (engType == "FG_PISTON") {
240 Engines.push_back(new FGPiston(FDMExec, Cfg_ptr, numEngines));
241 } else if (engType == "FG_TURBINE") {
242 Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr, numEngines));
243 } else if (engType == "FG_SIMTURBINE") {
245 cerr << "The FG_SIMTURBINE engine type has been renamed to FG_TURBINE." << endl;
246 cerr << "To fix this problem, simply replace the FG_SIMTURBINE name " << endl;
247 cerr << "in your engine file to FG_TURBINE." << endl;
249 Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr, numEngines));
250 } else if (engType == "FG_ELECTRIC") {
251 Engines.push_back(new FGElectric(FDMExec, Cfg_ptr, numEngines));
253 cerr << fgred << " Unrecognized engine type: " << underon << engType
254 << underoff << " found in config file." << fgdef << endl;
258 AC_cfg->GetNextConfigLine();
259 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
261 if (token == "XLOC") { *AC_cfg >> xLoc; }
262 else if (token == "YLOC") { *AC_cfg >> yLoc; }
263 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
264 else if (token == "PITCH") { *AC_cfg >> Pitch;}
265 else if (token == "YAW") { *AC_cfg >> Yaw; }
266 else if (token.find("AC_THRUSTER") != string::npos) {
267 if (debug_lvl > 0) cout << "\n Reading thruster definition" << endl;
268 Engines.back()->LoadThruster(AC_cfg);
269 AC_cfg->GetNextConfigLine();
271 else if (token == "FEED") {
273 Engines[numEngines]->AddFeedTank(Feed);
274 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
275 } else cerr << "Unknown identifier: " << token << " in engine file: "
276 << engineFileName << endl;
280 cout << " X = " << xLoc << endl;
281 cout << " Y = " << yLoc << endl;
282 cout << " Z = " << zLoc << endl;
283 cout << " Pitch = " << Pitch << endl;
284 cout << " Yaw = " << Yaw << endl;
287 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
292 cerr << fgred << "\n Could not read engine config file: " << underon <<
293 engineFileName + ".xml" << underoff << fgdef << endl;
297 } else if (token == "AC_TANK") { // ============== READING TANKS
299 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
300 Tanks.push_back(new FGTank(AC_cfg, FDMExec));
301 switch(Tanks[numTanks]->GetType()) {
303 numSelectedFuelTanks++;
306 case FGTank::ttOXIDIZER:
307 numSelectedOxiTanks++;
314 AC_cfg->GetNextConfigLine();
317 CalculateTankInertias();
318 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
323 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
325 string FGPropulsion::GetPropulsionStrings(string delimeter)
329 string PropulsionStrings = "";
330 bool firstime = true;
333 for (i=0; i<Engines.size(); i++) {
334 if (firstime) firstime = false;
335 else PropulsionStrings += delimeter;
337 PropulsionStrings += Engines[i]->GetEngineLabels(delimeter);
339 for (i=0; i<Tanks.size(); i++) {
340 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimeter << "Fuel Tank " << i;
341 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimeter << "Oxidizer Tank " << i;
344 return PropulsionStrings;
347 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
349 string FGPropulsion::GetPropulsionValues(string delimeter)
353 string PropulsionValues = "";
354 bool firstime = true;
357 for (i=0; i<Engines.size(); i++) {
358 if (firstime) firstime = false;
359 else PropulsionValues += delimeter;
361 PropulsionValues += Engines[i]->GetEngineValues(delimeter);
363 for (i=0; i<Tanks.size(); i++) {
365 buf << Tanks[i]->GetContents();
368 return PropulsionValues;
371 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
373 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
375 iTank = Tanks.begin();
376 vXYZtank_arm.InitMatrix();
377 while (iTank < Tanks.end()) {
378 vXYZtank_arm(eX) += (*iTank)->GetXYZ(eX)*(*iTank)->GetContents();
379 vXYZtank_arm(eY) += (*iTank)->GetXYZ(eY)*(*iTank)->GetContents();
380 vXYZtank_arm(eZ) += (*iTank)->GetXYZ(eZ)*(*iTank)->GetContents();
386 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
388 double FGPropulsion::GetTanksWeight(void)
392 iTank = Tanks.begin();
393 while (iTank < Tanks.end()) {
394 Tw += (*iTank)->GetContents();
400 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
407 if (size == 0) return tankJ;
409 tankJ = FGMatrix33();
411 for (unsigned int i=0; i<size; i++)
412 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
413 Tanks[i]->GetXYZ() );
418 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
420 void FGPropulsion::SetMagnetos(int setting)
422 if (ActiveEngine < 0) {
423 for (unsigned i=0; i<Engines.size(); i++) {
424 ((FGPiston*)Engines[i])->SetMagnetos(setting);
427 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
431 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
433 void FGPropulsion::SetStarter(int setting)
435 if (ActiveEngine < 0) {
436 for (unsigned i=0; i<Engines.size(); i++) {
438 Engines[i]->SetStarter(false);
440 Engines[i]->SetStarter(true);
444 Engines[ActiveEngine]->SetStarter(false);
446 Engines[ActiveEngine]->SetStarter(true);
450 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
452 void FGPropulsion::SetCutoff(int setting)
454 if (ActiveEngine < 0) {
455 for (unsigned i=0; i<Engines.size(); i++) {
457 ((FGTurbine*)Engines[i])->SetCutoff(false);
459 ((FGTurbine*)Engines[i])->SetCutoff(true);
463 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
465 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
469 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
471 void FGPropulsion::SetActiveEngine(int engine)
473 if (engine >= Engines.size() || engine < 0)
476 ActiveEngine = engine;
479 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
481 double FGPropulsion::Transfer(int source, int target, double amount)
483 double shortage, overage;
488 shortage = Tanks[source]->Drain(amount);
493 overage = Tanks[target]->Fill(amount - shortage);
498 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
500 void FGPropulsion::DoRefuel(double time_slice)
502 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
503 int TanksNotFull = 0;
505 for (unsigned int i=0; i<numTanks; i++) {
506 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
510 for (unsigned int i=0; i<numTanks; i++) {
511 if (Tanks[i]->GetPctFull() < 99.99)
512 Transfer(-1, i, fillrate/TanksNotFull);
517 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519 void FGPropulsion::SetFuelFreeze(bool f)
522 for (unsigned int i=0; i<numEngines; i++) {
523 Engines[i]->SetFuelFreeze(f);
527 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 void FGPropulsion::bind(void)
531 typedef double (FGPropulsion::*PMF)(int) const;
532 typedef int (FGPropulsion::*iPMF)(void) const;
534 PropertyManager->Tie("propulsion/magneto_cmd", this,
535 (iPMF)0, &FGPropulsion::SetMagnetos, true);
536 PropertyManager->Tie("propulsion/starter_cmd", this,
537 (iPMF)0, &FGPropulsion::SetStarter, true);
538 PropertyManager->Tie("propulsion/cutoff_cmd", this,
539 (iPMF)0, &FGPropulsion::SetCutoff, true);
541 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
542 (PMF)&FGPropulsion::GetForces);
543 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
544 (PMF)&FGPropulsion::GetForces);
545 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
546 (PMF)&FGPropulsion::GetForces);
547 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
548 (PMF)&FGPropulsion::GetMoments);
549 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
550 (PMF)&FGPropulsion::GetMoments);
551 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
552 (PMF)&FGPropulsion::GetMoments);
554 PropertyManager->Tie("propulsion/active_engine", this,
555 (iPMF)&FGPropulsion::GetActiveEngine, &FGPropulsion::SetActiveEngine, true);
558 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
560 void FGPropulsion::unbind(void)
562 PropertyManager->Untie("propulsion/magneto_cmd");
563 PropertyManager->Untie("propulsion/starter_cmd");
564 PropertyManager->Untie("propulsion/cutoff_cmd");
565 PropertyManager->Untie("propulsion/active_engine");
566 PropertyManager->Untie("forces/fbx-prop-lbs");
567 PropertyManager->Untie("forces/fby-prop-lbs");
568 PropertyManager->Untie("forces/fbz-prop-lbs");
569 PropertyManager->Untie("moments/l-prop-lbsft");
570 PropertyManager->Untie("moments/m-prop-lbsft");
571 PropertyManager->Untie("moments/n-prop-lbsft");
574 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
575 // The bitmasked value choices are as follows:
576 // unset: In this case (the default) JSBSim would only print
577 // out the normally expected messages, essentially echoing
578 // the config files as they are read. If the environment
579 // variable is not set, debug_lvl is set to 1 internally
580 // 0: This requests JSBSim not to output any messages
582 // 1: This value explicity requests the normal JSBSim
584 // 2: This value asks for a message to be printed out when
585 // a class is instantiated
586 // 4: When this value is set, a message is displayed when a
587 // FGModel object executes its Run() method
588 // 8: When this value is set, various runtime state variables
589 // are printed out periodically
590 // 16: When set various parameters are sanity checked and
591 // a message is printed out when they go out of bounds
593 void FGPropulsion::Debug(int from)
595 if (debug_lvl <= 0) return;
597 if (debug_lvl & 1) { // Standard console startup message output
598 if (from == 0) { // Constructor
602 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
603 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
604 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
606 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
608 if (debug_lvl & 8 ) { // Runtime state variables
610 if (debug_lvl & 16) { // Sanity checking
612 if (debug_lvl & 64) {
613 if (from == 0) { // Constructor
614 cout << IdSrc << endl;
615 cout << IdHdr << endl;