JSBSim Flight Dynamics Model  1.0 (02 March 2017)
An Open Source Flight Dynamics and Control Software Library in C++
FGPropulsion.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGPropulsion.cpp
4  Author: Jon S. Berndt
5  Date started: 08/20/00
6  Purpose: Encapsulates the set of engines and tanks associated
7  with this aircraft
8 
9  ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------
10 
11  This program is free software; you can redistribute it and/or modify it under
12  the terms of the GNU Lesser General Public License as published by the Free Software
13  Foundation; either version 2 of the License, or (at your option) any later
14  version.
15 
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 Lesser General Public License for more
19  details.
20 
21  You should have received a copy of the GNU Lesser 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.
24 
25  Further information about the GNU Lesser General Public License can also be found on
26  the world wide web at http://www.gnu.org.
27 
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:
33 
34 1) The appropriate engine type instance is created
35 2) At least one tank object is created, and is linked to an engine.
36 
37 At Run time each engines Calculate() method is called.
38 
39 HISTORY
40 --------------------------------------------------------------------------------
41 08/20/00 JSB Created
42 
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44 INCLUDES
45 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
46 
47 #include <iostream>
48 #include <sstream>
49 #include <cstdlib>
50 #include <iomanip>
51 
52 #include "FGFDMExec.h"
53 #include "FGPropulsion.h"
54 #include "models/FGMassBalance.h"
55 #include "models/propulsion/FGRocket.h"
56 #include "models/propulsion/FGTurbine.h"
57 #include "models/propulsion/FGPiston.h"
58 #include "models/propulsion/FGElectric.h"
59 #include "models/propulsion/FGTurboProp.h"
60 #include "models/propulsion/FGTank.h"
61 #include "input_output/FGModelLoader.h"
62 #include "math/FGColumnVector3.h"
63 
64 using namespace std;
65 
66 namespace JSBSim {
67 
68 IDENT(IdSrc,"$Id: FGPropulsion.cpp,v 1.88 2017/02/25 14:23:19 bcoconni Exp $");
69 IDENT(IdHdr,ID_PROPULSION);
70 
71 extern short debug_lvl;
72 
73 
74 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 CLASS IMPLEMENTATION
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
77 
78 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
79 {
80  Name = "FGPropulsion";
81 
82  numSelectedFuelTanks = numSelectedOxiTanks = 0;
83  numTanks = numEngines = 0;
84  numOxiTanks = numFuelTanks = 0;
85  ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
86  tankJ.InitMatrix();
87  DumpRate = 0.0;
88  RefuelRate = 6000.0;
89  FuelFreeze = false;
90  IsBound =
91  HavePistonEngine =
92  HaveTurbineEngine =
93  HaveRocketEngine =
94  HaveTurboPropEngine =
95  HaveElectricEngine = false;
96 
97  Debug(0);
98 }
99 
100 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101 
103 {
104  for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
105  Engines.clear();
106  for (unsigned int i=0; i<Tanks.size(); i++) delete Tanks[i];
107  Tanks.clear();
108  Debug(1);
109 }
110 
111 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
112 
113 bool FGPropulsion::InitModel(void)
114 {
115  bool result = true;
116 
117  if (!FGModel::InitModel()) return false;
118 
119  vForces.InitMatrix();
120  vMoments.InitMatrix();
121 
122  for (unsigned int i=0; i<numTanks; i++) Tanks[i]->ResetToIC();
123  TotalFuelQuantity = 0.0;
124  TotalOxidizerQuantity = 0.0;
125  refuel = dump = false;
126 
127  for (unsigned int i=0; i<numEngines; i++)
128  Engines[i]->ResetToIC();
129 
130  return result;
131 }
132 
133 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
134 
135 bool FGPropulsion::Run(bool Holding)
136 {
137  unsigned int i;
138 
139  if (FGModel::Run(Holding)) return true;
140  if (Holding) return false;
141 
142  RunPreFunctions();
143 
144  vForces.InitMatrix();
145  vMoments.InitMatrix();
146 
147  for (i=0; i<numEngines; i++) {
148  Engines[i]->Calculate();
149  ConsumeFuel(Engines[i]);
150  vForces += Engines[i]->GetBodyForces(); // sum body frame forces
151  vMoments += Engines[i]->GetMoments(); // sum body frame moments
152  }
153 
154  TotalFuelQuantity = 0.0;
155  TotalOxidizerQuantity = 0.0;
156  for (i=0; i<numTanks; i++) {
157  Tanks[i]->Calculate( in.TotalDeltaT, in.TAT_c);
158  switch (Tanks[i]->GetType()) {
159  case FGTank::ttFUEL:
160  TotalFuelQuantity += Tanks[i]->GetContents();
161  break;
162  case FGTank::ttOXIDIZER:
163  TotalOxidizerQuantity += Tanks[i]->GetContents();
164  break;
165  default:
166  break;
167  }
168  }
169 
170  if (refuel.node() && refuel) DoRefuel( in.TotalDeltaT );
171  if (dump.node() && dump) DumpFuel( in.TotalDeltaT );
172 
173  RunPostFunctions();
174 
175  return false;
176 }
177 
178 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
179 //
180 // The engine can tell us how much fuel it needs, but it is up to the propulsion
181 // subsystem manager class FGPropulsion to manage fuel flow amongst tanks. Engines
182 // May burn fuel from more than one tank at a time, and may burn from one tank
183 // before another - that is, may burn from one tank until the tank is depleted,
184 // then burn from the next highest priority tank. This can be accompished
185 // by defining a fuel management system, but this way of specifying priorities
186 // is more automatic from a user perspective.
187 
188 void FGPropulsion::ConsumeFuel(FGEngine* engine)
189 {
190  if (FuelFreeze) return;
191  if (FDMExec->GetTrimStatus()) return;
192 
193  unsigned int TanksWithFuel=0, CurrentFuelTankPriority=1;
194  unsigned int TanksWithOxidizer=0, CurrentOxidizerTankPriority=1;
195  vector <int> FeedListFuel, FeedListOxi;
196  bool Starved = true; // Initially set Starved to true. Set to false in code below.
197  bool hasOxTanks = false;
198 
199  // For this engine,
200  // 1) Count how many fuel tanks with the current priority level have fuel
201  // 2) If there none, then try next lower priority (higher number) - that is,
202  // increment CurrentPriority.
203  // 3) Build the feed list.
204  // 4) Do the same for oxidizer tanks, if needed.
205 
206  // Process fuel tanks, if any
207  while ((TanksWithFuel == 0) && (CurrentFuelTankPriority <= numTanks)) {
208  for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
209  unsigned int TankId = engine->GetSourceTank(i);
210  FGTank* Tank = Tanks[TankId];
211  unsigned int TankPriority = Tank->GetPriority();
212  if (TankPriority != 0) {
213  switch(Tank->GetType()) {
214  case FGTank::ttFUEL:
215  if ((Tank->GetContents() > 0.0) && Tank->GetSelected() && (TankPriority == CurrentFuelTankPriority)) {
216  TanksWithFuel++;
217  Starved = false;
218  FeedListFuel.push_back(TankId);
219  }
220  break;
221  case FGTank::ttOXIDIZER:
222  // Skip this here (done below)
223  break;
224  }
225  }
226  }
227  if (TanksWithFuel == 0) CurrentFuelTankPriority++; // No tanks at this priority, try next priority
228  }
229 
230  bool FuelStarved = Starved;
231  Starved = true;
232 
233  // Process Oxidizer tanks, if any
234  if (engine->GetType() == FGEngine::etRocket) {
235  while ((TanksWithOxidizer == 0) && (CurrentOxidizerTankPriority <= numTanks)) {
236  for (unsigned int i=0; i<engine->GetNumSourceTanks(); i++) {
237  unsigned int TankId = engine->GetSourceTank(i);
238  FGTank* Tank = Tanks[TankId];
239  unsigned int TankPriority = Tank->GetPriority();
240  if (TankPriority != 0) {
241  switch(Tank->GetType()) {
242  case FGTank::ttFUEL:
243  // Skip this here (done above)
244  break;
245  case FGTank::ttOXIDIZER:
246  hasOxTanks = true;
247  if (Tank->GetContents() > 0.0 && Tank->GetSelected() && TankPriority == CurrentOxidizerTankPriority) {
248  TanksWithOxidizer++;
249  if (TanksWithFuel > 0) Starved = false;
250  FeedListOxi.push_back(TankId);
251  }
252  break;
253  }
254  }
255  }
256  if (TanksWithOxidizer == 0) CurrentOxidizerTankPriority++; // No tanks at this priority, try next priority
257  }
258  }
259 
260  bool OxiStarved = Starved;
261 
262  engine->SetStarved(FuelStarved || (hasOxTanks && OxiStarved)); // Tanks can be refilled, so be sure to reset engine Starved flag here.
263 
264  // No fuel or fuel/oxidizer found at any priority!
265 // if (Starved) return;
266  if (FuelStarved || (hasOxTanks && OxiStarved)) return;
267 
268  double FuelToBurn = engine->CalcFuelNeed(); // How much fuel does this engine need?
269  double FuelNeededPerTank = FuelToBurn / TanksWithFuel; // Determine fuel needed per tank.
270  for (unsigned int i=0; i<FeedListFuel.size(); i++) {
271  Tanks[FeedListFuel[i]]->Drain(FuelNeededPerTank);
272  }
273 
274  if (engine->GetType() == FGEngine::etRocket) {
275  double OxidizerToBurn = engine->CalcOxidizerNeed(); // How much fuel does this engine need?
276  double OxidizerNeededPerTank = 0;
277  if (TanksWithOxidizer > 0) OxidizerNeededPerTank = OxidizerToBurn / TanksWithOxidizer; // Determine fuel needed per tank.
278  for (unsigned int i=0; i<FeedListOxi.size(); i++) {
279  Tanks[FeedListOxi[i]]->Drain(OxidizerNeededPerTank);
280  }
281  }
282 
283 }
284 
285 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
286 
288 {
289  double currentThrust = 0, lastThrust = -1;
290  int steady_count = 0, j = 0;
291  bool steady = false;
292  bool TrimMode = FDMExec->GetTrimStatus();
293  double TimeStep = FDMExec->GetDeltaT();
294 
295  vForces.InitMatrix();
296  vMoments.InitMatrix();
297 
298  if (!FGModel::Run(false)) {
299  FDMExec->SetTrimStatus(true);
300  // This is a time marching algorithm so it needs a non-zero time step to
301  // reach a steady state.
302  in.TotalDeltaT = 0.5;
303 
304  for (unsigned int i=0; i<numEngines; i++) {
305  steady=false;
306  steady_count=0;
307  j=0;
308  while (!steady && j < 6000) {
309  Engines[i]->Calculate();
310  lastThrust = currentThrust;
311  currentThrust = Engines[i]->GetThrust();
312  if (fabs(lastThrust-currentThrust) < 0.0001) {
313  steady_count++;
314  if (steady_count > 120) {
315  steady=true;
316  }
317  } else {
318  steady_count=0;
319  }
320  j++;
321  }
322  vForces += Engines[i]->GetBodyForces(); // sum body frame forces
323  vMoments += Engines[i]->GetMoments(); // sum body frame moments
324  }
325 
326  FDMExec->SetTrimStatus(TrimMode);
327  in.TotalDeltaT = TimeStep;
328 
329  return false;
330  } else {
331  return true;
332  }
333 }
334 
335 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
336 
338 {
339  if (n >= 0) { // A specific engine is supposed to be initialized
340 
341  if (n >= (int)GetNumEngines() ) {
342  throw(string("Tried to initialize a non-existent engine!"));
343  }
344 
345  in.ThrottleCmd[n] = in.ThrottlePos[n] = 1; // Set the throttle command and position
346  in.MixtureCmd[n] = in.MixturePos[n] = 1; // Set the mixture command and position
347 
348  GetEngine(n)->InitRunning();
349  GetSteadyState();
350 
351  } else if (n < 0) { // -1 refers to "All Engines"
352 
353  for (unsigned int i=0; i<GetNumEngines(); i++) {
354  in.ThrottleCmd[i] = in.ThrottlePos[i] = 1; // Set the throttle command and position
355  in.MixtureCmd[i] = in.MixturePos[i] = 1; // Set the mixture command and position
356  GetEngine(i)->InitRunning();
357  }
358 
359  GetSteadyState();
360  }
361 }
362 
363 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
364 
366 {
367  FGModelLoader ModelLoader(this);
368 
369  Debug(2);
370  ReadingEngine = false;
371  double FuelDensity = 6.0;
372 
373  Name = "Propulsion Model: " + el->GetAttributeValue("name");
374 
375  // Perform base class Pre-Load
376  if (!FGModel::Load(el))
377  return false;
378 
379  // Process tank definitions first to establish the number of fuel tanks
380 
381  Element* tank_element = el->FindElement("tank");
382  while (tank_element) {
383  Tanks.push_back(new FGTank(FDMExec, tank_element, numTanks));
384  if (Tanks.back()->GetType() == FGTank::ttFUEL) {
385  FuelDensity = Tanks[numFuelTanks]->GetDensity();
386  numFuelTanks++;
387  }
388  else if (Tanks.back()->GetType() == FGTank::ttOXIDIZER) numOxiTanks++;
389  else {cerr << "Unknown tank type specified." << endl; return false;}
390  numTanks++;
391  tank_element = el->FindNextElement("tank");
392  }
393  numSelectedFuelTanks = numFuelTanks;
394  numSelectedOxiTanks = numOxiTanks;
395 
396  ReadingEngine = true;
397  Element* engine_element = el->FindElement("engine");
398  while (engine_element) {
399  if (!ModelLoader.Open(engine_element)) return false;
400 
401  try {
402  // Locate the thruster definition
403  Element* thruster_element = engine_element->FindElement("thruster");
404  if (!thruster_element || !ModelLoader.Open(thruster_element))
405  throw("No thruster definition supplied with engine definition.");
406 
407  if (engine_element->FindElement("piston_engine")) {
408  HavePistonEngine = true;
409  if (!IsBound) bind();
410  Element *element = engine_element->FindElement("piston_engine");
411  Engines.push_back(new FGPiston(FDMExec, element, numEngines, in));
412  } else if (engine_element->FindElement("turbine_engine")) {
413  HaveTurbineEngine = true;
414  if (!IsBound) bind();
415  Element *element = engine_element->FindElement("turbine_engine");
416  Engines.push_back(new FGTurbine(FDMExec, element, numEngines, in));
417  } else if (engine_element->FindElement("turboprop_engine")) {
418  HaveTurboPropEngine = true;
419  if (!IsBound) bind();
420  Element *element = engine_element->FindElement("turboprop_engine");
421  Engines.push_back(new FGTurboProp(FDMExec, element, numEngines, in));
422  } else if (engine_element->FindElement("rocket_engine")) {
423  HaveRocketEngine = true;
424  if (!IsBound) bind();
425  Element *element = engine_element->FindElement("rocket_engine");
426  Engines.push_back(new FGRocket(FDMExec, element, numEngines, in));
427  } else if (engine_element->FindElement("electric_engine")) {
428  HaveElectricEngine = true;
429  if (!IsBound) bind();
430  Element *element = engine_element->FindElement("electric_engine");
431  Engines.push_back(new FGElectric(FDMExec, element, numEngines, in));
432  } else {
433  cerr << engine_element->ReadFrom() << " Unknown engine type" << endl;
434  return false;
435  }
436  } catch (std::string& str) {
437  cerr << endl << fgred << str << reset << endl;
438  return false;
439  }
440 
441  numEngines++;
442 
443  engine_element = el->FindNextElement("engine");
444  }
445 
446  CalculateTankInertias();
447 
448  if (el->FindElement("dump-rate"))
449  DumpRate = el->FindElementValueAsNumberConvertTo("dump-rate", "LBS/MIN");
450  if (el->FindElement("refuel-rate"))
451  RefuelRate = el->FindElementValueAsNumberConvertTo("refuel-rate", "LBS/MIN");
452 
453  unsigned int i;
454  for (i=0; i<Engines.size(); i++) {
455  Engines[i]->SetFuelDensity(FuelDensity);
456  }
457 
458 
459  PostLoad(el, PropertyManager);
460 
461  return true;
462 }
463 
464 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
465 
466 SGPath FGPropulsion::FindFullPathName(const SGPath& path) const
467 {
468  if (!ReadingEngine) return FGModel::FindFullPathName(path);
469 
470  SGPath name = CheckPathName(FDMExec->GetFullAircraftPath()/string("Engines"),
471  path);
472  if (!name.isNull()) return name;
473 
474  return CheckPathName(FDMExec->GetEnginePath(), path);
475 }
476 
477 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
478 
479 string FGPropulsion::GetPropulsionStrings(const string& delimiter) const
480 {
481  unsigned int i;
482 
483  string PropulsionStrings = "";
484  bool firstime = true;
485  stringstream buf;
486 
487  for (i=0; i<Engines.size(); i++) {
488  if (firstime) firstime = false;
489  else PropulsionStrings += delimiter;
490 
491  PropulsionStrings += Engines[i]->GetEngineLabels(delimiter);
492  }
493  for (i=0; i<Tanks.size(); i++) {
494  if (Tanks[i]->GetType() == FGTank::ttFUEL) buf << delimiter << "Fuel Tank " << i;
495  else if (Tanks[i]->GetType() == FGTank::ttOXIDIZER) buf << delimiter << "Oxidizer Tank " << i;
496  }
497 
498  PropulsionStrings += buf.str();
499  buf.str("");
500 
501  return PropulsionStrings;
502 }
503 
504 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
505 
506 string FGPropulsion::GetPropulsionValues(const string& delimiter) const
507 {
508  unsigned int i;
509 
510  string PropulsionValues = "";
511  bool firstime = true;
512  stringstream buf;
513 
514  for (i=0; i<Engines.size(); i++) {
515  if (firstime) firstime = false;
516  else PropulsionValues += delimiter;
517 
518  PropulsionValues += Engines[i]->GetEngineValues(delimiter);
519  }
520  for (i=0; i<Tanks.size(); i++) {
521  buf << delimiter;
522  buf << Tanks[i]->GetContents();
523  }
524 
525  PropulsionValues += buf.str();
526  buf.str("");
527 
528  return PropulsionValues;
529 }
530 
531 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
532 
533 string FGPropulsion::GetPropulsionTankReport()
534 {
535  string out="";
536  stringstream outstream;
537 
538  /*const FGMatrix33& mTkI =*/ CalculateTankInertias();
539 
540  for (unsigned int i=0; i<numTanks; i++)
541  {
542  FGTank* tank = Tanks[i];
543  string tankname="";
544  if (tank->GetType() == FGTank::ttFUEL && tank->GetGrainType() != FGTank::gtUNKNOWN) {
545  tankname = "Solid Fuel";
546  } else if (tank->GetType() == FGTank::ttFUEL) {
547  tankname = "Fuel";
548  } else if (tank->GetType() == FGTank::ttOXIDIZER) {
549  tankname = "Oxidizer";
550  } else {
551  tankname = "(Unknown tank type)";
552  }
553  outstream << highint << left << setw(4) << i << setw(30) << tankname << normint
554  << right << setw(10) << tank->GetContents() << setw(8) << tank->GetXYZ(eX)
555  << setw(8) << tank->GetXYZ(eY) << setw(8) << tank->GetXYZ(eZ)
556  << setw(12) << tank->GetIxx() << setw(12) << tank->GetIyy()
557  << setw(12) << tank->GetIzz() << endl;
558  }
559  return outstream.str();
560 }
561 
562 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
563 
564 const FGColumnVector3& FGPropulsion::GetTanksMoment(void)
565 {
566  vXYZtank_arm.InitMatrix();
567  for (unsigned int i=0; i<Tanks.size(); i++) {
568  vXYZtank_arm += Tanks[i]->GetXYZ() * Tanks[i]->GetContents();
569  }
570  return vXYZtank_arm;
571 }
572 
573 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
574 
575 double FGPropulsion::GetTanksWeight(void) const
576 {
577  double Tw = 0.0;
578 
579  for (unsigned int i=0; i<Tanks.size(); i++) Tw += Tanks[i]->GetContents();
580 
581  return Tw;
582 }
583 
584 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585 
586 const FGMatrix33& FGPropulsion::CalculateTankInertias(void)
587 {
588  size_t size = Tanks.size();
589 
590  if (size == 0) return tankJ;
591 
592  tankJ = FGMatrix33();
593 
594  for (unsigned int i=0; i<size; i++) {
595  tankJ += FDMExec->GetMassBalance()->GetPointmassInertia( lbtoslug * Tanks[i]->GetContents(),
596  Tanks[i]->GetXYZ());
597  tankJ(1,1) += Tanks[i]->GetIxx();
598  tankJ(2,2) += Tanks[i]->GetIyy();
599  tankJ(3,3) += Tanks[i]->GetIzz();
600  }
601 
602  return tankJ;
603 }
604 
605 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
606 
607 void FGPropulsion::SetMagnetos(int setting)
608 {
609  if (ActiveEngine < 0) {
610  for (unsigned i=0; i<Engines.size(); i++) {
611  // ToDo: first need to make sure the engine Type is really appropriate:
612  // do a check to see if it is of type Piston. This should be done for
613  // all of this kind of possibly across-the-board settings.
614  if (Engines[i]->GetType() == FGEngine::etPiston)
615  ((FGPiston*)Engines[i])->SetMagnetos(setting);
616  }
617  } else {
618  ((FGPiston*)Engines[ActiveEngine])->SetMagnetos(setting);
619  }
620 }
621 
622 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
623 
624 void FGPropulsion::SetStarter(int setting)
625 {
626  if (ActiveEngine < 0) {
627  for (unsigned i=0; i<Engines.size(); i++) {
628  if (setting == 0)
629  Engines[i]->SetStarter(false);
630  else
631  Engines[i]->SetStarter(true);
632  }
633  } else {
634  if (setting == 0)
635  Engines[ActiveEngine]->SetStarter(false);
636  else
637  Engines[ActiveEngine]->SetStarter(true);
638  }
639 }
640 
641 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
642 
643 void FGPropulsion::SetCutoff(int setting)
644 {
645  bool bsetting = setting == 0 ? false : true;
646 
647  if (ActiveEngine < 0) {
648  for (unsigned i=0; i<Engines.size(); i++) {
649  switch (Engines[i]->GetType()) {
650  case FGEngine::etTurbine:
651  ((FGTurbine*)Engines[i])->SetCutoff(bsetting);
652  break;
653  case FGEngine::etTurboprop:
654  ((FGTurboProp*)Engines[i])->SetCutoff(bsetting);
655  break;
656  default:
657  break;
658  }
659  }
660  } else {
661  switch (Engines[ActiveEngine]->GetType()) {
662  case FGEngine::etTurbine:
663  ((FGTurbine*)Engines[ActiveEngine])->SetCutoff(bsetting);
664  break;
665  case FGEngine::etTurboprop:
666  ((FGTurboProp*)Engines[ActiveEngine])->SetCutoff(bsetting);
667  break;
668  default:
669  break;
670  }
671  }
672 }
673 
674 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
675 
676 void FGPropulsion::SetActiveEngine(int engine)
677 {
678  if (engine >= (int)Engines.size() || engine < 0)
679  ActiveEngine = -1;
680  else
681  ActiveEngine = engine;
682 }
683 
684 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
685 
686 double FGPropulsion::Transfer(int source, int target, double amount)
687 {
688  double shortage, overage;
689 
690  if (source == -1) {
691  shortage = 0.0;
692  } else {
693  shortage = Tanks[source]->Drain(amount);
694  }
695  if (target == -1) {
696  overage = 0.0;
697  } else {
698  overage = Tanks[target]->Fill(amount - shortage);
699  }
700  return overage;
701 }
702 
703 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
704 
705 void FGPropulsion::DoRefuel(double time_slice)
706 {
707  unsigned int i;
708 
709  double fillrate = RefuelRate / 60.0 * time_slice;
710  int TanksNotFull = 0;
711 
712  for (i=0; i<numTanks; i++) {
713  if (Tanks[i]->GetPctFull() < 99.99) ++TanksNotFull;
714  }
715 
716  // adds fuel equally to all tanks that are not full
717  if (TanksNotFull) {
718  for (i=0; i<numTanks; i++) {
719  if (Tanks[i]->GetPctFull() < 99.99)
720  Transfer(-1, i, fillrate/TanksNotFull);
721  }
722  }
723 }
724 
725 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
726 
727 void FGPropulsion::DumpFuel(double time_slice)
728 {
729  unsigned int i;
730  int TanksDumping = 0;
731 
732  for (i=0; i<numTanks; i++) {
733  if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) ++TanksDumping;
734  }
735 
736  if (TanksDumping == 0) return;
737 
738  double dump_rate_per_tank = DumpRate / 60.0 * time_slice / TanksDumping;
739 
740  for (i=0; i<numTanks; i++) {
741  if (Tanks[i]->GetContents() > Tanks[i]->GetStandpipe()) {
742  Transfer(i, -1, dump_rate_per_tank);
743  }
744  }
745 }
746 
747 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 
749 void FGPropulsion::SetFuelFreeze(bool f)
750 {
751  FuelFreeze = f;
752  for (unsigned int i=0; i<numEngines; i++) {
753  Engines[i]->SetFuelFreeze(f);
754  }
755 }
756 
757 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
758 
759 void FGPropulsion::bind(void)
760 {
761  typedef double (FGPropulsion::*PMF)(int) const;
762  typedef int (FGPropulsion::*iPMF)(void) const;
763 
764  IsBound = true;
765  PropertyManager->Tie("propulsion/set-running", this, (iPMF)0, &FGPropulsion::InitRunning, false);
766  if (HaveTurbineEngine || HaveTurboPropEngine) {
767  PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
768  PropertyManager->Tie("propulsion/cutoff_cmd", this, (iPMF)0, &FGPropulsion::SetCutoff, false);
769  }
770 
771  if (HavePistonEngine) {
772  PropertyManager->Tie("propulsion/starter_cmd", this, (iPMF)0, &FGPropulsion::SetStarter, false);
773  PropertyManager->Tie("propulsion/magneto_cmd", this, (iPMF)0, &FGPropulsion::SetMagnetos, false);
774  }
775 
776  PropertyManager->Tie("propulsion/active_engine", this, (iPMF)&FGPropulsion::GetActiveEngine,
777  &FGPropulsion::SetActiveEngine, true);
778  PropertyManager->Tie("forces/fbx-prop-lbs", this, eX, (PMF)&FGPropulsion::GetForces);
779  PropertyManager->Tie("forces/fby-prop-lbs", this, eY, (PMF)&FGPropulsion::GetForces);
780  PropertyManager->Tie("forces/fbz-prop-lbs", this, eZ, (PMF)&FGPropulsion::GetForces);
781  PropertyManager->Tie("moments/l-prop-lbsft", this, eX, (PMF)&FGPropulsion::GetMoments);
782  PropertyManager->Tie("moments/m-prop-lbsft", this, eY, (PMF)&FGPropulsion::GetMoments);
783  PropertyManager->Tie("moments/n-prop-lbsft", this, eZ, (PMF)&FGPropulsion::GetMoments);
784  TotalFuelQuantity = PropertyManager->CreatePropertyObject<double>("propulsion/total-fuel-lbs");
785  TotalOxidizerQuantity = PropertyManager->CreatePropertyObject<double>("propulsion/total-oxidizer-lbs");
786  refuel = PropertyManager->CreatePropertyObject<bool>("propulsion/refuel");
787  dump = PropertyManager->CreatePropertyObject<bool>("propulsion/fuel_dump");
788 }
789 
790 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
791 // The bitmasked value choices are as follows:
792 // unset: In this case (the default) JSBSim would only print
793 // out the normally expected messages, essentially echoing
794 // the config files as they are read. If the environment
795 // variable is not set, debug_lvl is set to 1 internally
796 // 0: This requests JSBSim not to output any messages
797 // whatsoever.
798 // 1: This value explicity requests the normal JSBSim
799 // startup messages
800 // 2: This value asks for a message to be printed out when
801 // a class is instantiated
802 // 4: When this value is set, a message is displayed when a
803 // FGModel object executes its Run() method
804 // 8: When this value is set, various runtime state variables
805 // are printed out periodically
806 // 16: When set various parameters are sanity checked and
807 // a message is printed out when they go out of bounds
808 
809 void FGPropulsion::Debug(int from)
810 {
811  if (debug_lvl <= 0) return;
812 
813  if (debug_lvl & 1) { // Standard console startup message output
814  if (from == 2) { // Loader
815  cout << endl << " Propulsion:" << endl;
816  }
817  }
818  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
819  if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
820  if (from == 1) cout << "Destroyed: FGPropulsion" << endl;
821  }
822  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
823  }
824  if (debug_lvl & 8 ) { // Runtime state variables
825  }
826  if (debug_lvl & 16) { // Sanity checking
827  }
828  if (debug_lvl & 64) {
829  if (from == 0) { // Constructor
830  cout << IdSrc << endl;
831  cout << IdHdr << endl;
832  }
833  }
834 }
835 }
bool GetSelected(void) const
If the tank is set to supply fuel, this function returns true.
Definition: FGTank.h:242
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it. ...
const SGPath & GetFullAircraftPath(void)
Retrieves the full aircraft path name.
Definition: FGFDMExec.h:397
static char reset[5]
resets text properties
Definition: FGJSBBase.h:131
This class models a turbine engine.
Definition: FGTurbine.h:162
STL namespace.
FGEngine * GetEngine(unsigned int index) const
Retrieves an engine object pointer from the list of engines.
Definition: FGPropulsion.h:140
Element * FindElement(const std::string &el="")
Searches for a specified element.
Models a generic rocket engine.
Definition: FGRocket.h:132
static char normint[6]
normal intensity text
Definition: FGJSBBase.h:129
virtual double CalcFuelNeed(void)
The fuel need is calculated based on power levels and flow rate for that power level.
Definition: FGEngine.cpp:103
~FGPropulsion()
Destructor.
bool Load(Element *el)
Loads the propulsion system (engine[s] and tank[s]).
Turboprop engine model.
Definition: FGTurboProp.h:94
static char fgred[6]
red text
Definition: FGJSBBase.h:141
Models a fuel tank.
Definition: FGTank.h:200
virtual bool Run(bool Holding)
Runs the model; called by the Executive.
Definition: FGModel.cpp:92
void Tie(const std::string &name, bool *pointer, bool useDefault=true)
Tie a property to an external bool variable.
Propulsion management class.
Definition: FGPropulsion.h:106
Base class for all scheduled JSBSim models.
Definition: FGModel.h:74
double GetContents(void) const
Gets the contents of the tank.
Definition: FGTank.h:258
int GetType(void) const
Retrieves the type of tank: Fuel or Oxidizer.
Definition: FGTank.h:235
FGMatrix33 GetPointmassInertia(double mass_sl, const FGColumnVector3 &r) const
Computes the inertia contribution of a pointmass.
This class implements a 3 element column vector.
std::string ReadFrom(void) const
Return a string that contains a description of the location where the current XML element was read fr...
bool GetSteadyState(void)
Loops the engines until thrust output steady (used for trimming)
Base class for all engines.
Definition: FGEngine.h:121
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
static char highint[5]
highlights text
Definition: FGJSBBase.h:125
void InitRunning(int n)
Sets up the engines as running.
Models a Supercharged Piston engine.
Definition: FGPiston.h:221
Handles matrix math operations.
Definition: FGMatrix33.h:92
void InitMatrix(void)
Initialize the matrix.
Definition: FGMatrix33.cpp:257
double GetDeltaT(void) const
Returns the simulation delta T.
Definition: FGFDMExec.h:536
Models an electric motor.
Definition: FGElectric.h:74
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:189
virtual bool Load(Element *el)
Loads this model.
Definition: FGModel.cpp:113
const SGPath & GetEnginePath(void)
Retrieves the engine path.
Definition: FGFDMExec.h:391
bool Run(bool Holding)
Executes the propulsion model.
FGMassBalance * GetMassBalance(void)
Returns the FGAircraft pointer.
Definition: FGFDMExec.h:355
unsigned int GetNumEngines(void) const
Retrieves the number of engines defined for the aircraft.
Definition: FGPropulsion.h:134