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));
235 } else if (engType == "FG_PISTON") {
236 Engines.push_back(new FGPiston(FDMExec, Cfg_ptr));
237 } else if (engType == "FG_TURBINE") {
238 Engines.push_back(new FGTurbine(FDMExec, Cfg_ptr));
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));
246 } else if (engType == "FG_ELECTRIC") {
247 Engines.push_back(new FGElectric(FDMExec, Cfg_ptr));
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);
284 Engines[numEngines]->SetEngineNumber(numEngines);
289 cerr << fgred << "\n Could not read engine config file: " << underon <<
290 engineFileName + ".xml" << underoff << fgdef << endl;
294 } else if (token == "AC_TANK") { // ============== READING TANKS
296 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
297 Tanks.push_back(new FGTank(AC_cfg, FDMExec));
298 switch(Tanks[numTanks]->GetType()) {
300 numSelectedFuelTanks++;
303 case FGTank::ttOXIDIZER:
304 numSelectedOxiTanks++;
311 AC_cfg->GetNextConfigLine();
314 CalculateTankInertias();
315 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
320 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
322 string FGPropulsion::GetPropulsionStrings(void)
324 string PropulsionStrings = "";
325 bool firstime = true;
327 for (unsigned int i=0;i<Engines.size();i++) {
328 if (firstime) firstime = false;
329 else PropulsionStrings += ", ";
331 PropulsionStrings += Engines[i]->GetEngineLabels() + ", ";
334 return PropulsionStrings;
337 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 string FGPropulsion::GetPropulsionValues(void)
341 string PropulsionValues = "";
342 bool firstime = true;
344 for (unsigned int i=0;i<Engines.size();i++) {
345 if (firstime) firstime = false;
346 else PropulsionValues += ", ";
348 PropulsionValues += Engines[i]->GetEngineValues() + ", ";
351 return PropulsionValues;
354 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
356 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
358 iTank = Tanks.begin();
359 vXYZtank_arm.InitMatrix();
360 while (iTank < Tanks.end()) {
361 vXYZtank_arm(eX) += (*iTank)->GetXYZ(eX)*(*iTank)->GetContents();
362 vXYZtank_arm(eY) += (*iTank)->GetXYZ(eY)*(*iTank)->GetContents();
363 vXYZtank_arm(eZ) += (*iTank)->GetXYZ(eZ)*(*iTank)->GetContents();
369 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
371 double FGPropulsion::GetTanksWeight(void)
375 iTank = Tanks.begin();
376 while (iTank < Tanks.end()) {
377 Tw += (*iTank)->GetContents();
383 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
385 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
390 if (size == 0) return tankJ;
392 tankJ = FGMatrix33();
394 for (unsigned int i=0; i<size; i++)
395 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
396 Tanks[i]->GetXYZ() );
401 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
403 void FGPropulsion::SetMagnetos(int setting)
405 if (ActiveEngine < 0) {
406 for (unsigned i=0; i<Engines.size(); i++) {
407 ((FGPiston*)Engines[i])->SetMagnetos(setting);
410 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
414 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
416 void FGPropulsion::SetStarter(int setting)
418 if (ActiveEngine < 0) {
419 for (unsigned i=0; i<Engines.size(); i++) {
421 Engines[i]->SetStarter(false);
423 Engines[i]->SetStarter(true);
427 Engines[ActiveEngine]->SetStarter(false);
429 Engines[ActiveEngine]->SetStarter(true);
433 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435 void FGPropulsion::SetCutoff(int setting)
437 if (ActiveEngine < 0) {
438 for (unsigned i=0; i<Engines.size(); i++) {
440 ((FGTurbine*)Engines[i])->SetCutoff(false);
442 ((FGTurbine*)Engines[i])->SetCutoff(true);
446 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
448 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
452 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 void FGPropulsion::SetActiveEngine(int engine)
456 if (engine >= Engines.size() || engine < 0)
459 ActiveEngine = engine;
462 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464 double FGPropulsion::Transfer(int source, int target, double amount)
466 double shortage, overage;
471 shortage = Tanks[source]->Drain(amount);
476 overage = Tanks[target]->Fill(amount - shortage);
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 void FGPropulsion::DoRefuel(double time_slice)
485 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
486 int TanksNotFull = 0;
488 for (unsigned int i=0; i<numTanks; i++) {
489 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
493 for (unsigned int i=0; i<numTanks; i++) {
494 if (Tanks[i]->GetPctFull() < 99.99)
495 Transfer(-1, i, fillrate/TanksNotFull);
500 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
502 void FGPropulsion::bind(void)
504 typedef double (FGPropulsion::*PMF)(int) const;
505 typedef int (FGPropulsion::*iPMF)(void) const;
507 PropertyManager->Tie("propulsion/magneto_cmd", this,
508 (iPMF)0, &FGPropulsion::SetMagnetos, true);
509 PropertyManager->Tie("propulsion/starter_cmd", this,
510 (iPMF)0, &FGPropulsion::SetStarter, true);
511 PropertyManager->Tie("propulsion/cutoff_cmd", this,
512 (iPMF)0, &FGPropulsion::SetCutoff, true);
514 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
515 (PMF)&FGPropulsion::GetForces);
516 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
517 (PMF)&FGPropulsion::GetForces);
518 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
519 (PMF)&FGPropulsion::GetForces);
520 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
521 (PMF)&FGPropulsion::GetMoments);
522 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
523 (PMF)&FGPropulsion::GetMoments);
524 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
525 (PMF)&FGPropulsion::GetMoments);
527 PropertyManager->Tie("propulsion/active_engine", this,
528 (iPMF)&FGPropulsion::GetActiveEngine, &FGPropulsion::SetActiveEngine, true);
531 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
533 void FGPropulsion::unbind(void)
535 PropertyManager->Untie("propulsion/magneto_cmd");
536 PropertyManager->Untie("propulsion/starter_cmd");
537 PropertyManager->Untie("propulsion/cutoff_cmd");
538 PropertyManager->Untie("propulsion/active_engine");
539 PropertyManager->Untie("forces/fbx-prop-lbs");
540 PropertyManager->Untie("forces/fby-prop-lbs");
541 PropertyManager->Untie("forces/fbz-prop-lbs");
542 PropertyManager->Untie("moments/l-prop-lbsft");
543 PropertyManager->Untie("moments/m-prop-lbsft");
544 PropertyManager->Untie("moments/n-prop-lbsft");
547 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
548 // The bitmasked value choices are as follows:
549 // unset: In this case (the default) JSBSim would only print
550 // out the normally expected messages, essentially echoing
551 // the config files as they are read. If the environment
552 // variable is not set, debug_lvl is set to 1 internally
553 // 0: This requests JSBSim not to output any messages
555 // 1: This value explicity requests the normal JSBSim
557 // 2: This value asks for a message to be printed out when
558 // a class is instantiated
559 // 4: When this value is set, a message is displayed when a
560 // FGModel object executes its Run() method
561 // 8: When this value is set, various runtime state variables
562 // are printed out periodically
563 // 16: When set various parameters are sanity checked and
564 // a message is printed out when they go out of bounds
566 void FGPropulsion::Debug(int from)
568 if (debug_lvl <= 0) return;
570 if (debug_lvl & 1) { // Standard console startup message output
571 if (from == 0) { // Constructor
575 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
576 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
577 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
579 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
581 if (debug_lvl & 8 ) { // Runtime state variables
583 if (debug_lvl & 16) { // Sanity checking
585 if (debug_lvl & 64) {
586 if (from == 0) { // Constructor
587 cout << IdSrc << endl;
588 cout << IdHdr << endl;