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