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