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 Engines.back()->SetEngineFileName(engineFileName);
260 AC_cfg->GetNextConfigLine();
261 while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
263 if (token == "XLOC") { *AC_cfg >> xLoc; }
264 else if (token == "YLOC") { *AC_cfg >> yLoc; }
265 else if (token == "ZLOC") { *AC_cfg >> zLoc; }
266 else if (token == "PITCH") { *AC_cfg >> Pitch;}
267 else if (token == "YAW") { *AC_cfg >> Yaw; }
268 else if (token.find("AC_THRUSTER") != string::npos) {
269 if (debug_lvl > 0) cout << "\n Reading thruster definition" << endl;
270 Engines.back()->LoadThruster(AC_cfg);
271 AC_cfg->GetNextConfigLine();
273 else if (token == "FEED") {
275 Engines[numEngines]->AddFeedTank(Feed);
276 if (debug_lvl > 0) cout << " Feed tank: " << Feed << endl;
277 } else cerr << "Unknown identifier: " << token << " in engine file: "
278 << engineFileName << endl;
282 cout << " X = " << xLoc << endl;
283 cout << " Y = " << yLoc << endl;
284 cout << " Z = " << zLoc << endl;
285 cout << " Pitch = " << Pitch << endl;
286 cout << " Yaw = " << Yaw << endl;
289 Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
294 cerr << fgred << "\n Could not read engine config file: " << underon <<
295 engineFileName + ".xml" << underoff << fgdef << endl;
299 } else if (token == "AC_TANK") { // ============== READING TANKS
301 if (debug_lvl > 0) cout << "\n Reading tank definition" << endl;
302 Tanks.push_back(new FGTank(AC_cfg, FDMExec));
303 switch(Tanks[numTanks]->GetType()) {
305 numSelectedFuelTanks++;
308 case FGTank::ttOXIDIZER:
309 numSelectedOxiTanks++;
316 AC_cfg->GetNextConfigLine();
319 CalculateTankInertias();
320 if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
325 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
327 string FGPropulsion::GetPropulsionStrings(string delimeter)
331 string PropulsionStrings = "";
332 bool firstime = true;
335 for (i=0; i<Engines.size(); i++) {
336 if (firstime) firstime = false;
337 else PropulsionStrings += delimeter;
339 PropulsionStrings += Engines[i]->GetEngineLabels(delimeter);
341 for (i=0; i<Tanks.size(); i++) {
342 if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimeter << "Fuel Tank " << i;
343 else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimeter << "Oxidizer Tank " << i;
346 return PropulsionStrings;
349 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
351 string FGPropulsion::GetPropulsionValues(string delimeter)
355 string PropulsionValues = "";
356 bool firstime = true;
359 for (i=0; i<Engines.size(); i++) {
360 if (firstime) firstime = false;
361 else PropulsionValues += delimeter;
363 PropulsionValues += Engines[i]->GetEngineValues(delimeter);
365 for (i=0; i<Tanks.size(); i++) {
367 buf << Tanks[i]->GetContents();
370 return PropulsionValues;
373 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
375 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
377 iTank = Tanks.begin();
378 vXYZtank_arm.InitMatrix();
379 while (iTank < Tanks.end()) {
380 vXYZtank_arm(eX) += (*iTank)->GetXYZ(eX)*(*iTank)->GetContents();
381 vXYZtank_arm(eY) += (*iTank)->GetXYZ(eY)*(*iTank)->GetContents();
382 vXYZtank_arm(eZ) += (*iTank)->GetXYZ(eZ)*(*iTank)->GetContents();
388 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
390 double FGPropulsion::GetTanksWeight(void)
394 iTank = Tanks.begin();
395 while (iTank < Tanks.end()) {
396 Tw += (*iTank)->GetContents();
402 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
404 FGMatrix33& FGPropulsion::CalculateTankInertias(void)
409 if (size == 0) return tankJ;
411 tankJ = FGMatrix33();
413 for (unsigned int i=0; i<size; i++)
414 tankJ += MassBalance->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
415 Tanks[i]->GetXYZ() );
420 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
422 void FGPropulsion::SetMagnetos(int setting)
424 if (ActiveEngine < 0) {
425 for (unsigned i=0; i<Engines.size(); i++) {
426 ((FGPiston*)Engines[i])->SetMagnetos(setting);
429 ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
433 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435 void FGPropulsion::SetStarter(int setting)
437 if (ActiveEngine < 0) {
438 for (unsigned i=0; i<Engines.size(); i++) {
440 Engines[i]->SetStarter(false);
442 Engines[i]->SetStarter(true);
446 Engines[ActiveEngine]->SetStarter(false);
448 Engines[ActiveEngine]->SetStarter(true);
452 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 void FGPropulsion::SetCutoff(int setting)
456 if (ActiveEngine < 0) {
457 for (unsigned i=0; i<Engines.size(); i++) {
459 ((FGTurbine*)Engines[i])->SetCutoff(false);
461 ((FGTurbine*)Engines[i])->SetCutoff(true);
465 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(false);
467 ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(true);
471 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
473 void FGPropulsion::SetActiveEngine(int engine)
475 if (engine >= Engines.size() || engine < 0)
478 ActiveEngine = engine;
481 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
483 double FGPropulsion::Transfer(int source, int target, double amount)
485 double shortage, overage;
490 shortage = Tanks[source]->Drain(amount);
495 overage = Tanks[target]->Fill(amount - shortage);
500 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
502 void FGPropulsion::DoRefuel(double time_slice)
504 double fillrate = 100 * time_slice; // 100 lbs/sec = 6000 lbs/min
505 int TanksNotFull = 0;
507 for (unsigned int i=0; i<numTanks; i++) {
508 if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
512 for (unsigned int i=0; i<numTanks; i++) {
513 if (Tanks[i]->GetPctFull() < 99.99)
514 Transfer(-1, i, fillrate/TanksNotFull);
519 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521 void FGPropulsion::SetFuelFreeze(bool f)
524 for (unsigned int i=0; i<numEngines; i++) {
525 Engines[i]->SetFuelFreeze(f);
529 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
531 void FGPropulsion::bind(void)
533 typedef double (FGPropulsion::*PMF)(int) const;
534 typedef int (FGPropulsion::*iPMF)(void) const;
536 PropertyManager->Tie("propulsion/magneto_cmd", this,
537 (iPMF)0, &FGPropulsion::SetMagnetos, true);
538 PropertyManager->Tie("propulsion/starter_cmd", this,
539 (iPMF)0, &FGPropulsion::SetStarter, true);
540 PropertyManager->Tie("propulsion/cutoff_cmd", this,
541 (iPMF)0, &FGPropulsion::SetCutoff, true);
543 PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
544 (PMF)&FGPropulsion::GetForces);
545 PropertyManager->Tie("forces/fby-prop-lbs", this,2,
546 (PMF)&FGPropulsion::GetForces);
547 PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
548 (PMF)&FGPropulsion::GetForces);
549 PropertyManager->Tie("moments/l-prop-lbsft", this,1,
550 (PMF)&FGPropulsion::GetMoments);
551 PropertyManager->Tie("moments/m-prop-lbsft", this,2,
552 (PMF)&FGPropulsion::GetMoments);
553 PropertyManager->Tie("moments/n-prop-lbsft", this,3,
554 (PMF)&FGPropulsion::GetMoments);
556 PropertyManager->Tie("propulsion/active_engine", this,
557 (iPMF)&FGPropulsion::GetActiveEngine, &FGPropulsion::SetActiveEngine, true);
560 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
562 void FGPropulsion::unbind(void)
564 PropertyManager->Untie("propulsion/magneto_cmd");
565 PropertyManager->Untie("propulsion/starter_cmd");
566 PropertyManager->Untie("propulsion/cutoff_cmd");
567 PropertyManager->Untie("propulsion/active_engine");
568 PropertyManager->Untie("forces/fbx-prop-lbs");
569 PropertyManager->Untie("forces/fby-prop-lbs");
570 PropertyManager->Untie("forces/fbz-prop-lbs");
571 PropertyManager->Untie("moments/l-prop-lbsft");
572 PropertyManager->Untie("moments/m-prop-lbsft");
573 PropertyManager->Untie("moments/n-prop-lbsft");
576 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
577 // The bitmasked value choices are as follows:
578 // unset: In this case (the default) JSBSim would only print
579 // out the normally expected messages, essentially echoing
580 // the config files as they are read. If the environment
581 // variable is not set, debug_lvl is set to 1 internally
582 // 0: This requests JSBSim not to output any messages
584 // 1: This value explicity requests the normal JSBSim
586 // 2: This value asks for a message to be printed out when
587 // a class is instantiated
588 // 4: When this value is set, a message is displayed when a
589 // FGModel object executes its Run() method
590 // 8: When this value is set, various runtime state variables
591 // are printed out periodically
592 // 16: When set various parameters are sanity checked and
593 // a message is printed out when they go out of bounds
595 void FGPropulsion::Debug(int from)
597 if (debug_lvl <= 0) return;
599 if (debug_lvl & 1) { // Standard console startup message output
600 if (from == 0) { // Constructor
604 if (debug_lvl & 2 ) { // Instantiation/Destruction notification
605 if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
606 if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
608 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
610 if (debug_lvl & 8 ) { // Runtime state variables
612 if (debug_lvl & 16) { // Sanity checking
614 if (debug_lvl & 64) {
615 if (from == 0) { // Constructor
616 cout << IdSrc << endl;
617 cout << IdHdr << endl;