]> git.mxchange.org Git - flightgear.git/blob - src/FDM/JSBSim/FGPropulsion.cpp
JSBSim updates. This update changes the file format, so an update of the base
[flightgear.git] / src / FDM / JSBSim / 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, tanks, and thrusters associated
7                with this aircraft
8
9  ------------- Copyright (C) 2000  Jon S. Berndt (jsb@hal-pc.org) -------------
10
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
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 General Public License for more
19  details.
20
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.
24
25  Further information about the GNU 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, tanks, and "thrusters" (the device that transforms the
32 engine power into a force that acts on the aircraft, such as a nozzle or
33 propeller). Once the Propulsion class gets the config file, it reads in
34 information which is specific to a type of engine. Then:
35
36 1) The appropriate engine type instance is created
37 2) A thruster object is instantiated, and is linked to the engine
38 3) At least one tank object is created, and is linked to an engine.
39
40 At Run time each engines Calculate() method is called to return the excess power
41 generated during that iteration. The drag from the previous iteration is sub-
42 tracted to give the excess power available for thrust this pass. That quantity
43 is passed to the thrusters associated with a particular engine - perhaps with a
44 scaling mechanism (gearing?) to allow the engine to give its associated thrust-
45 ers specific distributed portions of the excess power.
46
47 HISTORY
48 --------------------------------------------------------------------------------
49 08/20/00   JSB   Created
50
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 INCLUDES
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
54
55 #include "FGPropulsion.h"
56 #include "FGPropertyManager.h"
57
58
59 static const char *IdSrc = "$Id$";
60 static const char *IdHdr = ID_PROPULSION;
61
62 extern short debug_lvl;
63
64 #if defined (__APPLE__)
65 /* Not all systems have the gcvt function */
66 inline char* gcvt (double value, int ndigits, char *buf) {
67     /* note that this is not exactly what gcvt is supposed to do! */
68     snprintf (buf, ndigits+1, "%f", value);
69     return buf;
70 }
71 #endif
72
73
74 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
75 CLASS IMPLEMENTATION
76 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
77
78 FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
79 {
80   Name = "FGPropulsion";
81   numSelectedFuelTanks = numSelectedOxiTanks = 0;
82   numTanks = numEngines = numThrusters = 0;
83   numOxiTanks = numFuelTanks = 0;
84   dt = 0.0;
85   ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
86   bind();
87   Debug(0);
88 }
89
90 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
91
92 FGPropulsion::~FGPropulsion()
93 {
94   for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
95   Engines.clear();
96   unbind();
97   Debug(1);
98 }
99
100 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
101
102 bool FGPropulsion::Run(void)
103 {
104   double PowerAvailable;
105   dt = State->Getdt();
106
107   vForces.InitMatrix();
108   vMoments.InitMatrix();
109
110   if (!FGModel::Run()) {
111     for (unsigned int i=0; i<numEngines; i++) {
112       Thrusters[i]->SetdeltaT(dt*rate);
113       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
114       Thrusters[i]->Calculate(PowerAvailable);
115       vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
116       vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
117     }
118     return false;
119   } else {
120     return true;
121   }
122 }
123
124 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
125
126 bool FGPropulsion::GetSteadyState(void)
127 {
128   double PowerAvailable;
129   double currentThrust = 0, lastThrust=-1;
130   dt = State->Getdt();
131   int steady_count,j=0;
132   bool steady=false;
133
134   vForces.InitMatrix();
135   vMoments.InitMatrix();
136
137   if (!FGModel::Run()) {
138     for (unsigned int i=0; i<numEngines; i++) {
139       Engines[i]->SetTrimMode(true);
140       Thrusters[i]->SetdeltaT(dt*rate);
141       steady=false;
142       steady_count=0;
143       while (!steady && j < 6000) {
144         PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
145         lastThrust = currentThrust;
146         currentThrust = Thrusters[i]->Calculate(PowerAvailable);
147         if (fabs(lastThrust-currentThrust) < 0.0001) {
148           steady_count++;
149           if (steady_count > 120) { steady=true; }
150         } else {
151           steady_count=0;
152         }
153         j++;
154       }
155       vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
156       vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
157       Engines[i]->SetTrimMode(false);
158     }
159
160     return false;
161   } else {
162     return true;
163   }
164 }
165
166 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
167
168 bool FGPropulsion::ICEngineStart(void)
169 {
170   double PowerAvailable;
171   int j;
172   dt = State->Getdt();
173
174   vForces.InitMatrix();
175   vMoments.InitMatrix();
176
177   for (unsigned int i=0; i<numEngines; i++) {
178     Engines[i]->SetTrimMode(true);
179     Thrusters[i]->SetdeltaT(dt*rate);
180     j=0;
181     while (!Engines[i]->GetRunning() && j < 2000) {
182       PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
183       Thrusters[i]->Calculate(PowerAvailable);
184       j++;
185     }
186     vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
187     vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
188     Engines[i]->SetTrimMode(false);
189   }
190   return true;
191 }
192
193 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
194
195 bool FGPropulsion::Load(FGConfigFile* AC_cfg)
196 {
197   string token, fullpath;
198   string engineFileName, engType;
199   string thrusterFileName, thrType;
200   string parameter;
201   string enginePath = FDMExec->GetEnginePath();
202   double xLoc, yLoc, zLoc, Pitch, Yaw;
203   double P_Factor = 0, Sense = 0.0;
204   int Feed;
205   bool ThrottleAdded = false;
206
207 # ifndef macintosh
208       fullpath = enginePath + "/";
209 # else
210       fullpath = enginePath + ";";
211 # endif
212
213   AC_cfg->GetNextConfigLine();
214
215   while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
216
217     if (token == "AC_ENGINE") {                   // ============ READING ENGINES
218
219       engineFileName = AC_cfg->GetValue("FILE");
220
221       if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath
222                                                 + engineFileName + ".xml"<< endl;
223       FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
224
225       if (Eng_cfg.IsOpen()) {
226         Eng_cfg.GetNextConfigLine();
227         engType = Eng_cfg.GetValue();
228         cout << engType << endl;
229
230         FCS->AddThrottle();
231         ThrottleAdded = true;
232
233         if (engType == "FG_ROCKET") {
234           Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
235         } else if (engType == "FG_PISTON") {
236           Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
237         } else if (engType == "FG_TURBINE") {
238           Engines.push_back(new FGTurbine(FDMExec, &Eng_cfg));
239         } else {
240           cerr << fgred << "    Unrecognized engine type: " << underon << engType
241                     << underoff << " found in config file." << fgdef << endl;
242           return false;
243         }
244
245         AC_cfg->GetNextConfigLine();
246         while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
247           *AC_cfg >> token;
248           if      (token == "XLOC")  { *AC_cfg >> xLoc; }
249           else if (token == "YLOC")  { *AC_cfg >> yLoc; }
250           else if (token == "ZLOC")  { *AC_cfg >> zLoc; }
251           else if (token == "PITCH") { *AC_cfg >> Pitch;}
252           else if (token == "YAW")   { *AC_cfg >> Yaw;}
253           else if (token == "FEED")  {
254             *AC_cfg >> Feed;
255             Engines[numEngines]->AddFeedTank(Feed);
256             if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;
257           } else cerr << "Unknown identifier: " << token << " in engine file: "
258                                                         << engineFileName << endl;
259         }
260
261         if (debug_lvl > 0)  {
262           cout << "      X = " << xLoc << endl;
263           cout << "      Y = " << yLoc << endl;
264           cout << "      Z = " << zLoc << endl;
265           cout << "      Pitch = " << Pitch << endl;
266           cout << "      Yaw = " << Yaw << endl;
267         }
268
269         Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
270         Engines[numEngines]->SetEngineNumber(numEngines);
271         numEngines++;
272
273       } else {
274
275         cerr << fgred << "\n  Could not read engine config file: " << underon <<
276                     fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
277         return false;
278       }
279
280     } else if (token == "AC_TANK") {              // ============== READING TANKS
281
282       if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
283       Tanks.push_back(new FGTank(AC_cfg));
284       switch(Tanks[numTanks]->GetType()) {
285       case FGTank::ttFUEL:
286         numSelectedFuelTanks++;
287         numFuelTanks++;
288         break;
289       case FGTank::ttOXIDIZER:
290         numSelectedOxiTanks++;
291         numOxiTanks++;
292         break;
293       }
294
295       numTanks++;
296
297     } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
298
299       thrusterFileName = AC_cfg->GetValue("FILE");
300
301       if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
302                                     fullpath + thrusterFileName + ".xml" << endl;
303       FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
304
305       if (Thruster_cfg.IsOpen()) {
306         Thruster_cfg.GetNextConfigLine();
307         thrType = Thruster_cfg.GetValue();
308
309         if (thrType == "FG_PROPELLER") {
310           Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
311         } else if (thrType == "FG_NOZZLE") {
312           Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg ));
313         } else if (thrType == "FG_DIRECT") {
314           Thrusters.push_back(new FGThruster( FDMExec, &Thruster_cfg) );
315         }  
316
317         AC_cfg->GetNextConfigLine();
318         while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
319           *AC_cfg >> token;
320           if (token == "XLOC") *AC_cfg >> xLoc;
321           else if (token == "YLOC") *AC_cfg >> yLoc;
322           else if (token == "ZLOC") *AC_cfg >> zLoc;
323           else if (token == "PITCH") *AC_cfg >> Pitch;
324           else if (token == "YAW") *AC_cfg >> Yaw;
325           else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
326           else if (token == "SENSE")   *AC_cfg >> Sense;
327           else cerr << "Unknown identifier: " << token << " in engine file: "
328                                                         << engineFileName << endl;
329         }
330
331         Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
332         Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
333         if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
334           ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
335           if (debug_lvl > 0) cout << "      P-Factor: " << P_Factor << endl;
336           ((FGPropeller*)Thrusters[numThrusters])->SetSense(fabs(Sense)/Sense);
337           if (debug_lvl > 0) cout << "      Sense: " << Sense <<  endl;
338         }
339         Thrusters[numThrusters]->SetdeltaT(dt*rate);
340         Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
341         numThrusters++;
342
343       } else {
344         cerr << "Could not read thruster config file: " << fullpath
345                                                 + thrusterFileName + ".xml" << endl;
346         return false;
347       }
348
349     }
350     AC_cfg->GetNextConfigLine();
351   }
352
353   if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
354
355   return true;
356 }
357
358 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
359
360 string FGPropulsion::GetPropulsionStrings(void)
361 {
362   string PropulsionStrings = "";
363   bool firstime = true;
364   char buffer[5];
365
366   for (unsigned int i=0;i<Engines.size();i++) {
367     if (firstime)  firstime = false;
368     else           PropulsionStrings += ", ";
369
370     sprintf(buffer, "%d", i);
371
372     switch(Engines[i]->GetType()) {
373     case FGEngine::etPiston:
374       PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
375       break;
376     case FGEngine::etRocket:
377       PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
378       break;
379     case FGEngine::etTurbine:
380       break;
381     default:
382       PropulsionStrings += "INVALID ENGINE TYPE";
383       break;
384     }
385
386     PropulsionStrings += ", ";
387
388     FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
389     switch(Thrusters[i]->GetType()) {
390     case FGThruster::ttNozzle:
391       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
392       break;
393     case FGThruster::ttRotor:
394       break;
395     case FGThruster::ttPropeller:
396       PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
397       PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Roll[" + buffer + "], ");
398       PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Pitch[" + buffer + "], ");
399       PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Yaw[" + buffer + "], ");
400       PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
401       if (Propeller->IsVPitch())
402         PropulsionStrings += (Thrusters[i]->GetName() + "_Pitch[" + buffer + "], ");
403       PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
404       break;
405     default:
406       PropulsionStrings += "INVALID THRUSTER TYPE";
407       break;
408     }
409   }
410
411   return PropulsionStrings;
412 }
413
414 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
415
416 string FGPropulsion::GetPropulsionValues(void)
417 {
418   char buff[20];
419   string PropulsionValues = "";
420   bool firstime = true;
421
422   for (unsigned int i=0;i<Engines.size();i++) {
423     if (firstime)  firstime = false;
424     else           PropulsionValues += ", ";
425
426     switch(Engines[i]->GetType()) {
427     case FGEngine::etPiston:
428       PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
429       break;
430     case FGEngine::etRocket:
431       PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
432       break;
433     case FGEngine::etTurbine:
434       break;
435     }
436
437     PropulsionValues += ", ";
438
439     switch(Thrusters[i]->GetType()) {
440     case FGThruster::ttNozzle:
441       PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
442       break;
443     case FGThruster::ttRotor:
444       break;
445     case FGThruster::ttPropeller:
446       FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
447       FGColumnVector3 vPFactor = Propeller->GetPFactor();
448       PropulsionValues += string(gcvt(Propeller->GetTorque(), 10, buff)) + ", ";
449       PropulsionValues += string(gcvt(vPFactor(eRoll), 10, buff)) + ", ";
450       PropulsionValues += string(gcvt(vPFactor(ePitch), 10, buff)) + ", ";
451       PropulsionValues += string(gcvt(vPFactor(eYaw), 10, buff)) + ", ";
452       PropulsionValues += string(gcvt(Propeller->GetThrust(), 10, buff)) + ", ";
453       if (Propeller->IsVPitch())
454         PropulsionValues += string(gcvt(Propeller->GetPitch(), 10, buff)) + ", ";
455       PropulsionValues += string(gcvt(Propeller->GetRPM(), 10, buff));
456       break;
457     }
458   }
459
460   return PropulsionValues;
461 }
462
463 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
464
465 FGColumnVector3& FGPropulsion::GetTanksMoment(void)
466 {
467   iTank = Tanks.begin();
468   vXYZtank.InitMatrix();
469   while (iTank < Tanks.end()) {
470     vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
471     vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
472     vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
473     iTank++;
474   }
475   return vXYZtank;
476 }
477
478 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
479
480 double FGPropulsion::GetTanksWeight(void)
481 {
482   double Tw = 0.0;
483
484   iTank = Tanks.begin();
485   while (iTank < Tanks.end()) {
486     Tw += (*iTank)->GetContents();
487     iTank++;
488   }
489   return Tw;
490 }
491
492 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493
494 double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
495 {
496   double I = 0.0;
497   iTank = Tanks.begin();
498   while (iTank < Tanks.end()) {
499     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
500     iTank++;
501   }
502   return I;
503 }
504
505 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
506
507 double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
508 {
509   double I = 0.0;
510   iTank = Tanks.begin();
511   while (iTank < Tanks.end()) {
512     I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
513     iTank++;
514   }
515   return I;
516 }
517
518 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
519
520 double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
521 {
522   double I = 0.0;
523   iTank = Tanks.begin();
524   while (iTank < Tanks.end()) {
525     I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
526     iTank++;
527   }
528   return I;
529 }
530
531 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
532
533 double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
534 {
535   double I = 0.0;
536   iTank = Tanks.begin();
537   while (iTank < Tanks.end()) {
538     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
539     iTank++;
540   }
541   return I;
542 }
543
544 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545
546 double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
547 {
548   double I = 0.0;
549   iTank = Tanks.begin();
550   while (iTank != Tanks.end()) {
551     I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
552     iTank++;
553   }
554   return I;
555 }
556
557 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558
559 void FGPropulsion::SetMagnetos(int setting)
560 {
561   if (ActiveEngine == -1) {
562     for (unsigned i=0; i<Engines.size(); i++) {
563       Engines[i]->SetMagnetos(setting);
564     }
565   } else {
566     Engines[ActiveEngine]->SetMagnetos(setting);
567   }
568 }
569
570 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571
572 void FGPropulsion::SetStarter(int setting)
573 {
574   if (ActiveEngine == -1) {
575     for (unsigned i=0; i<Engines.size(); i++) {
576       Engines[i]->SetStarter(setting);
577     }
578   } else {
579     if (setting == 0)
580       Engines[ActiveEngine]->SetStarter(false);
581     else
582       Engines[ActiveEngine]->SetStarter(true);
583   }
584 }
585
586 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587
588 void FGPropulsion::SetActiveEngine(int engine)
589 {
590   if ( unsigned(engine) > Engines.size())
591     ActiveEngine = -1;
592   else
593     ActiveEngine = engine;
594 }
595
596 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597
598 void FGPropulsion::bind(void)
599 {
600   typedef double (FGPropulsion::*PMF)(int) const;
601   typedef int (FGPropulsion::*iPMF)(void) const;
602   /* PropertyManager->Tie("propulsion/num-engines", this,
603                        &FGPropulsion::GetNumEngines);
604   PropertyManager->Tie("propulsion/num-tanks", this,
605                        &FGPropulsion::GetNumTanks); */
606
607   PropertyManager->Tie("propulsion/magneto_cmd", this,
608                        (iPMF)0,
609                        &FGPropulsion::SetMagnetos,
610                        true);
611   PropertyManager->Tie("propulsion/starter_cmd", this,
612                        (iPMF)0,
613                        &FGPropulsion::SetStarter,
614                        true);
615   PropertyManager->Tie("propulsion/active_engine", this,
616                        (iPMF)0,
617                        &FGPropulsion::SetActiveEngine,
618                        true);
619
620   PropertyManager->Tie("propulsion/num-sel-fuel-tanks", this,
621                        &FGPropulsion::GetnumSelectedFuelTanks);
622   PropertyManager->Tie("propulsion/num-sel-ox-tanks", this,
623                        &FGPropulsion::GetnumSelectedOxiTanks);
624   PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
625                        (PMF)&FGPropulsion::GetForces);
626   PropertyManager->Tie("forces/fby-prop-lbs", this,2,
627                        (PMF)&FGPropulsion::GetForces);
628   PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
629                        (PMF)&FGPropulsion::GetForces);
630   PropertyManager->Tie("moments/l-prop-lbsft", this,1,
631                        (PMF)&FGPropulsion::GetMoments);
632   PropertyManager->Tie("moments/m-prop-lbsft", this,2,
633                        (PMF)&FGPropulsion::GetMoments);
634   PropertyManager->Tie("moments/n-prop-lbsft", this,3,
635                        (PMF)&FGPropulsion::GetMoments);
636   //PropertyManager->Tie("propulsion/tanks-weight-lbs", this,
637   //                     &FGPropulsion::GetTanksWeight);
638 }
639
640 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
641
642 void FGPropulsion::unbind(void)
643 {
644   /* PropertyManager->Untie("propulsion/num-engines");
645   PropertyManager->Untie("propulsion/num-tanks"); */
646   PropertyManager->Untie("propulsion/num-sel-fuel-tanks");
647   PropertyManager->Untie("propulsion/num-sel-ox-tanks");
648   PropertyManager->Untie("propulsion/magneto_cmd");
649   PropertyManager->Untie("propulsion/starter_cmd");
650   PropertyManager->Untie("propulsion/active_engine");
651   PropertyManager->Untie("forces/fbx-prop-lbs");
652   PropertyManager->Untie("forces/fby-prop-lbs");
653   PropertyManager->Untie("forces/fbz-prop-lbs");
654   PropertyManager->Untie("moments/l-prop-lbsft");
655   PropertyManager->Untie("moments/m-prop-lbsft");
656   PropertyManager->Untie("moments/n-prop-lbsft");
657   //PropertyManager->Untie("propulsion/tanks-weight-lbs");
658 }
659
660 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
661 //    The bitmasked value choices are as follows:
662 //    unset: In this case (the default) JSBSim would only print
663 //       out the normally expected messages, essentially echoing
664 //       the config files as they are read. If the environment
665 //       variable is not set, debug_lvl is set to 1 internally
666 //    0: This requests JSBSim not to output any messages
667 //       whatsoever.
668 //    1: This value explicity requests the normal JSBSim
669 //       startup messages
670 //    2: This value asks for a message to be printed out when
671 //       a class is instantiated
672 //    4: When this value is set, a message is displayed when a
673 //       FGModel object executes its Run() method
674 //    8: When this value is set, various runtime state variables
675 //       are printed out periodically
676 //    16: When set various parameters are sanity checked and
677 //       a message is printed out when they go out of bounds
678
679 void FGPropulsion::Debug(int from)
680 {
681   if (debug_lvl <= 0) return;
682
683   if (debug_lvl & 1) { // Standard console startup message output
684     if (from == 0) { // Constructor
685
686     }
687   }
688   if (debug_lvl & 2 ) { // Instantiation/Destruction notification
689     if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
690     if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
691   }
692   if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
693   }
694   if (debug_lvl & 8 ) { // Runtime state variables
695   }
696   if (debug_lvl & 16) { // Sanity checking
697   }
698   if (debug_lvl & 64) {
699     if (from == 0) { // Constructor
700       cout << IdSrc << endl;
701       cout << IdHdr << endl;
702     }
703   }
704 }
705