]> git.mxchange.org Git - flightgear.git/blobdiff - 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
index 1a2dba8c7d226b12450a06d9dceab085056f6a26..3ee7b2058a4fe06c846bcef67cef536c4f6455ef 100644 (file)
-/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
- Module:       FGPropulsion.cpp\r
- Author:       Jon S. Berndt\r
- Date started: 08/20/00\r
- Purpose:      Encapsulates the set of engines, tanks, and thrusters associated\r
-               with this aircraft\r
-\r
- ------------- Copyright (C) 2000  Jon S. Berndt (jsb@hal-pc.org) -------------\r
-\r
- This program is free software; you can redistribute it and/or modify it under\r
- the terms of the GNU General Public License as published by the Free Software\r
- Foundation; either version 2 of the License, or (at your option) any later\r
- version.\r
-\r
- This program is distributed in the hope that it will be useful, but WITHOUT\r
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\r
- FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\r
- details.\r
-\r
- You should have received a copy of the GNU General Public License along with\r
- this program; if not, write to the Free Software Foundation, Inc., 59 Temple\r
- Place - Suite 330, Boston, MA  02111-1307, USA.\r
-\r
- Further information about the GNU General Public License can also be found on\r
- the world wide web at http://www.gnu.org.\r
-\r
-FUNCTIONAL DESCRIPTION\r
---------------------------------------------------------------------------------\r
-The Propulsion class is the container for the entire propulsion system, which is\r
-comprised of engines, tanks, and "thrusters" (the device that transforms the\r
-engine power into a force that acts on the aircraft, such as a nozzle or\r
-propeller). Once the Propulsion class gets the config file, it reads in\r
-information which is specific to a type of engine. Then:\r
-\r
-1) The appropriate engine type instance is created\r
-2) A thruster object is instantiated, and is linked to the engine\r
-3) At least one tank object is created, and is linked to an engine.\r
-\r
-At Run time each engines Calculate() method is called to return the excess power\r
-generated during that iteration. The drag from the previous iteration is sub-\r
-tracted to give the excess power available for thrust this pass. That quantity\r
-is passed to the thrusters associated with a particular engine - perhaps with a\r
-scaling mechanism (gearing?) to allow the engine to give its associated thrust-\r
-ers specific distributed portions of the excess power.\r
-\r
-HISTORY\r
---------------------------------------------------------------------------------\r
-08/20/00   JSB   Created\r
-\r
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-INCLUDES\r
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/\r
-\r
-#include "FGPropulsion.h"\r
-#include "FGPropertyManager.h"\r
-\r
-\r
-static const char *IdSrc = "$Id$";\r
-static const char *IdHdr = ID_PROPULSION;\r
-\r
-extern short debug_lvl;\r
-\r
-#if defined (__APPLE__)\r
-/* Not all systems have the gcvt function */\r
-inline char* gcvt (double value, int ndigits, char *buf) {\r
-    /* note that this is not exactly what gcvt is supposed to do! */\r
-    snprintf (buf, ndigits+1, "%f", value);\r
-    return buf;\r
-}\r
-#endif\r
-\r
-\r
-/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-CLASS IMPLEMENTATION\r
-%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/\r
-\r
-FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)\r
-{\r
-  Name = "FGPropulsion";\r
-  numSelectedFuelTanks = numSelectedOxiTanks = 0;\r
-  numTanks = numEngines = numThrusters = 0;\r
-  numOxiTanks = numFuelTanks = 0;\r
-  dt = 0.0;\r
-  ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...\r
-  bind();\r
-  Debug(0);\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-FGPropulsion::~FGPropulsion()\r
-{\r
-  for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];\r
-  Engines.clear();\r
-  unbind();\r
-  Debug(1);\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-bool FGPropulsion::Run(void)\r
-{\r
-  double PowerAvailable;\r
-  dt = State->Getdt();\r
-\r
-  vForces.InitMatrix();\r
-  vMoments.InitMatrix();\r
-\r
-  if (!FGModel::Run()) {\r
-    for (unsigned int i=0; i<numEngines; i++) {\r
-      Thrusters[i]->SetdeltaT(dt*rate);\r
-      PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());\r
-      Thrusters[i]->Calculate(PowerAvailable);\r
-      vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces\r
-      vMoments += Thrusters[i]->GetMoments();     // sum body frame moments\r
-    }\r
-    return false;\r
-  } else {\r
-    return true;\r
-  }\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-bool FGPropulsion::GetSteadyState(void)\r
-{\r
-  double PowerAvailable;\r
-  double currentThrust = 0, lastThrust=-1;\r
-  dt = State->Getdt();\r
-  int steady_count,j=0;\r
-  bool steady=false;\r
-\r
-  vForces.InitMatrix();\r
-  vMoments.InitMatrix();\r
-\r
-  if (!FGModel::Run()) {\r
-    for (unsigned int i=0; i<numEngines; i++) {\r
-      Engines[i]->SetTrimMode(true);\r
-      Thrusters[i]->SetdeltaT(dt*rate);\r
-      steady=false;\r
-      steady_count=0;\r
-      while (!steady && j < 6000) {\r
-        PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());\r
-        lastThrust = currentThrust;\r
-        currentThrust = Thrusters[i]->Calculate(PowerAvailable);\r
-        if (fabs(lastThrust-currentThrust) < 0.0001) {\r
-          steady_count++;\r
-          if (steady_count > 120) { steady=true; }\r
-        } else {\r
-          steady_count=0;\r
-        }\r
-        j++;    \r
-      }\r
-      vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces\r
-      vMoments += Thrusters[i]->GetMoments();     // sum body frame moments\r
-      Engines[i]->SetTrimMode(false);\r
-    }\r
-\r
-    return false;\r
-  } else {\r
-    return true;\r
-  }\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-bool FGPropulsion::ICEngineStart(void)\r
-{\r
-  double PowerAvailable;\r
-  int j;\r
-  dt = State->Getdt();\r
-\r
-  vForces.InitMatrix();\r
-  vMoments.InitMatrix();\r
-    \r
-  for (unsigned int i=0; i<numEngines; i++) {\r
-    Engines[i]->SetTrimMode(true);\r
-    Thrusters[i]->SetdeltaT(dt*rate);\r
-    j=0;\r
-    while (!Engines[i]->GetRunning() && j < 2000) {\r
-      PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());\r
-      Thrusters[i]->Calculate(PowerAvailable);\r
-      j++;    \r
-    }\r
-    vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces\r
-    vMoments += Thrusters[i]->GetMoments();     // sum body frame moments\r
-    Engines[i]->SetTrimMode(false);\r
-  }\r
-  return true;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-bool FGPropulsion::Load(FGConfigFile* AC_cfg)\r
-{\r
-  string token, fullpath;\r
-  string engineFileName, engType;\r
-  string thrusterFileName, thrType;\r
-  string parameter;\r
-  string enginePath = FDMExec->GetEnginePath();\r
-  double xLoc, yLoc, zLoc, Pitch, Yaw;\r
-  double P_Factor = 0, Sense = 0.0;\r
-  int Feed;\r
-  bool ThrottleAdded = false;\r
-\r
-# ifndef macintosh\r
-      fullpath = enginePath + "/";\r
-# else\r
-      fullpath = enginePath + ";";\r
-# endif\r
-\r
-  AC_cfg->GetNextConfigLine();\r
-\r
-  while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {\r
-\r
-    if (token == "AC_ENGINE") {                   // ============ READING ENGINES\r
-\r
-      engineFileName = AC_cfg->GetValue("FILE");\r
-\r
-      if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath\r
-                                                + engineFileName + ".xml"<< endl;\r
-      FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");\r
-\r
-      if (Eng_cfg.IsOpen()) {\r
-        Eng_cfg.GetNextConfigLine();\r
-        engType = Eng_cfg.GetValue();\r
-\r
-        FCS->AddThrottle();\r
-        ThrottleAdded = true;\r
-\r
-        if (engType == "FG_ROCKET") {\r
-          Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));\r
-        } else if (engType == "FG_PISTON") {\r
-          Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));\r
-        } else if (engType == "FG_TURBOJET") {\r
-          Engines.push_back(new FGTurboJet(FDMExec, &Eng_cfg));\r
-        } else if (engType == "FG_TURBOSHAFT") {\r
-          Engines.push_back(new FGTurboShaft(FDMExec, &Eng_cfg));\r
-        } else if (engType == "FG_TURBOPROP") {\r
-          Engines.push_back(new FGTurboProp(FDMExec, &Eng_cfg));\r
-        } else {\r
-          cerr << fgred << "    Unrecognized engine type: " << underon << engType\r
-                    << underoff << " found in config file." << fgdef << endl;\r
-          return false;\r
-        }\r
-\r
-        AC_cfg->GetNextConfigLine();\r
-        while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {\r
-          *AC_cfg >> token;\r
-          if      (token == "XLOC")  { *AC_cfg >> xLoc; }\r
-          else if (token == "YLOC")  { *AC_cfg >> yLoc; }\r
-          else if (token == "ZLOC")  { *AC_cfg >> zLoc; }\r
-          else if (token == "PITCH") { *AC_cfg >> Pitch;}\r
-          else if (token == "YAW")   { *AC_cfg >> Yaw;}\r
-          else if (token == "FEED")  {\r
-            *AC_cfg >> Feed;\r
-            Engines[numEngines]->AddFeedTank(Feed);\r
-            if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;\r
-          } else cerr << "Unknown identifier: " << token << " in engine file: "\r
-                                                        << engineFileName << endl;\r
-        }\r
-\r
-        if (debug_lvl > 0)  {\r
-          cout << "      X = " << xLoc << endl;\r
-          cout << "      Y = " << yLoc << endl;\r
-          cout << "      Z = " << zLoc << endl;\r
-          cout << "      Pitch = " << Pitch << endl;\r
-          cout << "      Yaw = " << Yaw << endl;\r
-        }\r
-\r
-        Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);\r
-        Engines[numEngines]->SetEngineNumber(numEngines);\r
-        numEngines++;\r
-\r
-      } else {\r
-\r
-        cerr << fgred << "\n  Could not read engine config file: " << underon <<\r
-                    fullpath + engineFileName + ".xml" << underoff << fgdef << endl;\r
-        return false;\r
-      }\r
-\r
-    } else if (token == "AC_TANK") {              // ============== READING TANKS\r
-\r
-      if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;\r
-      Tanks.push_back(new FGTank(AC_cfg));\r
-      switch(Tanks[numTanks]->GetType()) {\r
-      case FGTank::ttFUEL:\r
-        numSelectedFuelTanks++;\r
-        numFuelTanks++;\r
-        break;\r
-      case FGTank::ttOXIDIZER:\r
-        numSelectedOxiTanks++;\r
-        numOxiTanks++;\r
-        break;\r
-      }\r
-\r
-      numTanks++;\r
-\r
-    } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS\r
-\r
-      thrusterFileName = AC_cfg->GetValue("FILE");\r
-\r
-      if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<\r
-                                    fullpath + thrusterFileName + ".xml" << endl;\r
-      FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");\r
-\r
-      if (Thruster_cfg.IsOpen()) {\r
-        Thruster_cfg.GetNextConfigLine();\r
-        thrType = Thruster_cfg.GetValue();\r
-\r
-        if (thrType == "FG_PROPELLER") {\r
-          Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));\r
-        } else if (thrType == "FG_NOZZLE") {\r
-          Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg));\r
-        }\r
-\r
-        AC_cfg->GetNextConfigLine();\r
-        while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {\r
-          *AC_cfg >> token;\r
-          if (token == "XLOC") *AC_cfg >> xLoc;\r
-          else if (token == "YLOC") *AC_cfg >> yLoc;\r
-          else if (token == "ZLOC") *AC_cfg >> zLoc;\r
-          else if (token == "PITCH") *AC_cfg >> Pitch;\r
-          else if (token == "YAW") *AC_cfg >> Yaw;\r
-          else if (token == "P_FACTOR") *AC_cfg >> P_Factor;\r
-          else if (token == "SENSE")   *AC_cfg >> Sense;\r
-          else cerr << "Unknown identifier: " << token << " in engine file: "\r
-                                                        << engineFileName << endl;\r
-        }\r
-\r
-        Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);\r
-        Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);\r
-        if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {\r
-          ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);\r
-          if (debug_lvl > 0) cout << "      P-Factor: " << P_Factor << endl;\r
-          ((FGPropeller*)Thrusters[numThrusters])->SetSense(fabs(Sense)/Sense);\r
-          if (debug_lvl > 0) cout << "      Sense: " << Sense <<  endl;\r
-        }\r
-        Thrusters[numThrusters]->SetdeltaT(dt*rate);\r
-        Thrusters[numThrusters]->SetThrusterNumber(numThrusters);\r
-        numThrusters++;\r
-\r
-      } else {\r
-        cerr << "Could not read thruster config file: " << fullpath\r
-                                                + thrusterFileName + ".xml" << endl;\r
-        return false;\r
-      }\r
-\r
-    }\r
-    AC_cfg->GetNextConfigLine();\r
-  }\r
-\r
-  if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle\r
-\r
-  return true;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-string FGPropulsion::GetPropulsionStrings(void)\r
-{\r
-  string PropulsionStrings = "";\r
-  bool firstime = true;\r
-  char buffer[5];\r
-\r
-  for (unsigned int i=0;i<Engines.size();i++) {\r
-    if (firstime)  firstime = false;\r
-    else           PropulsionStrings += ", ";\r
-\r
-    sprintf(buffer, "%d", i);\r
-\r
-    switch(Engines[i]->GetType()) {\r
-    case FGEngine::etPiston:\r
-      PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");\r
-      break;\r
-    case FGEngine::etRocket:\r
-      PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");\r
-      break;\r
-    case FGEngine::etTurboJet:\r
-    case FGEngine::etTurboProp:\r
-    case FGEngine::etTurboShaft:\r
-      break;\r
-    default:\r
-      PropulsionStrings += "INVALID ENGINE TYPE";\r
-      break;\r
-    }\r
-\r
-    PropulsionStrings += ", ";\r
-\r
-    FGPropeller* Propeller = (FGPropeller*)Thrusters[i];\r
-    switch(Thrusters[i]->GetType()) {\r
-    case FGThruster::ttNozzle:\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");\r
-      break;\r
-    case FGThruster::ttRotor:\r
-      break;\r
-    case FGThruster::ttPropeller:\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Roll[" + buffer + "], ");\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Pitch[" + buffer + "], ");\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Yaw[" + buffer + "], ");\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");\r
-      if (Propeller->IsVPitch())\r
-        PropulsionStrings += (Thrusters[i]->GetName() + "_Pitch[" + buffer + "], ");\r
-      PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");\r
-      break;\r
-    default:\r
-      PropulsionStrings += "INVALID THRUSTER TYPE";\r
-      break;\r
-    }    \r
-  }\r
-\r
-  return PropulsionStrings;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-string FGPropulsion::GetPropulsionValues(void)\r
-{\r
-  char buff[20];\r
-  string PropulsionValues = "";\r
-  bool firstime = true;\r
-\r
-  for (unsigned int i=0;i<Engines.size();i++) {\r
-    if (firstime)  firstime = false;\r
-    else           PropulsionValues += ", ";\r
-\r
-    switch(Engines[i]->GetType()) {\r
-    case FGEngine::etPiston:\r
-      PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));\r
-      break;\r
-    case FGEngine::etRocket:\r
-      PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));\r
-      break;\r
-    case FGEngine::etTurboJet:\r
-    case FGEngine::etTurboProp:\r
-    case FGEngine::etTurboShaft:\r
-      break;\r
-    }\r
-\r
-    PropulsionValues += ", ";\r
-\r
-    switch(Thrusters[i]->GetType()) {\r
-    case FGThruster::ttNozzle:\r
-      PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));\r
-      break;\r
-    case FGThruster::ttRotor:\r
-      break;\r
-    case FGThruster::ttPropeller:\r
-      FGPropeller* Propeller = (FGPropeller*)Thrusters[i];\r
-      FGColumnVector3 vPFactor = Propeller->GetPFactor();\r
-      PropulsionValues += string(gcvt(Propeller->GetTorque(), 10, buff)) + ", ";\r
-      PropulsionValues += string(gcvt(vPFactor(eRoll), 10, buff)) + ", ";\r
-      PropulsionValues += string(gcvt(vPFactor(ePitch), 10, buff)) + ", ";\r
-      PropulsionValues += string(gcvt(vPFactor(eYaw), 10, buff)) + ", ";\r
-      PropulsionValues += string(gcvt(Propeller->GetThrust(), 10, buff)) + ", ";\r
-      if (Propeller->IsVPitch())\r
-        PropulsionValues += string(gcvt(Propeller->GetPitch(), 10, buff)) + ", ";\r
-      PropulsionValues += string(gcvt(Propeller->GetRPM(), 10, buff));\r
-      break;\r
-    }\r
-  }\r
-\r
-  return PropulsionValues;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-FGColumnVector3& FGPropulsion::GetTanksMoment(void)\r
-{\r
-  iTank = Tanks.begin();\r
-  vXYZtank.InitMatrix();\r
-  while (iTank < Tanks.end()) {\r
-    vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();\r
-    vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();\r
-    vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();\r
-    iTank++;\r
-  }\r
-  return vXYZtank;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksWeight(void)\r
-{\r
-  double Tw = 0.0;\r
-\r
-  iTank = Tanks.begin();\r
-  while (iTank < Tanks.end()) {\r
-    Tw += (*iTank)->GetContents();\r
-    iTank++;\r
-  }\r
-  return Tw;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)\r
-{\r
-  double I = 0.0;\r
-  iTank = Tanks.begin();\r
-  while (iTank < Tanks.end()) {\r
-    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());\r
-    iTank++;\r
-  }\r
-  return I;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)\r
-{\r
-  double I = 0.0;\r
-  iTank = Tanks.begin();\r
-  while (iTank < Tanks.end()) {\r
-    I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());\r
-    iTank++;\r
-  }\r
-  return I;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)\r
-{\r
-  double I = 0.0;\r
-  iTank = Tanks.begin();\r
-  while (iTank < Tanks.end()) {\r
-    I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());\r
-    iTank++;\r
-  }\r
-  return I;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)\r
-{\r
-  double I = 0.0;\r
-  iTank = Tanks.begin();\r
-  while (iTank < Tanks.end()) {\r
-    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());\r
-    iTank++;\r
-  }\r
-  return I;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)\r
-{\r
-  double I = 0.0;\r
-  iTank = Tanks.begin();\r
-  while (iTank != Tanks.end()) {\r
-    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());\r
-    iTank++;\r
-  }\r
-  return I;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-void FGPropulsion::SetMagnetos(int setting)\r
-{\r
-  if (ActiveEngine == -1) {\r
-    for (unsigned i=0; i<Engines.size(); i++) {\r
-      Engines[i]->SetMagnetos(setting);\r
-    }\r
-  } else {\r
-    Engines[ActiveEngine]->SetMagnetos(setting);\r
-  }\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-void FGPropulsion::SetStarter(int setting)\r
-{\r
-  if (ActiveEngine == -1) {\r
-    for (unsigned i=0; i<Engines.size(); i++) {\r
-      Engines[i]->SetStarter(setting);\r
-    }\r
-  } else {\r
-    if (setting == 0)\r
-      Engines[ActiveEngine]->SetStarter(false);\r
-    else\r
-      Engines[ActiveEngine]->SetStarter(true);\r
-  }\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-void FGPropulsion::SetActiveEngine(int engine)\r
-{\r
-  if ( unsigned(engine) > Engines.size())\r
-    ActiveEngine = -1;\r
-  else\r
-    ActiveEngine = engine;\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-void FGPropulsion::bind(void)\r
-{\r
-  typedef double (FGPropulsion::*PMF)(int) const;\r
-  typedef int (FGPropulsion::*iPMF)(void) const;\r
-  /* PropertyManager->Tie("propulsion/num-engines", this,\r
-                       &FGPropulsion::GetNumEngines);\r
-  PropertyManager->Tie("propulsion/num-tanks", this,\r
-                       &FGPropulsion::GetNumTanks); */\r
-\r
-  PropertyManager->Tie("propulsion/magneto_cmd", this,\r
-                       (iPMF)0,\r
-                       &FGPropulsion::SetMagnetos,\r
-                       true);\r
-  PropertyManager->Tie("propulsion/starter_cmd", this,\r
-                       (iPMF)0,\r
-                       &FGPropulsion::SetStarter,\r
-                       true);\r
-  PropertyManager->Tie("propulsion/active_engine", this,\r
-                       (iPMF)0,\r
-                       &FGPropulsion::SetActiveEngine,\r
-                       true);\r
-\r
-  PropertyManager->Tie("propulsion/num-sel-fuel-tanks", this,\r
-                       &FGPropulsion::GetnumSelectedFuelTanks);\r
-  PropertyManager->Tie("propulsion/num-sel-ox-tanks", this,\r
-                       &FGPropulsion::GetnumSelectedOxiTanks);\r
-  PropertyManager->Tie("forces/fbx-prop-lbs", this,1,\r
-                       (PMF)&FGPropulsion::GetForces);\r
-  PropertyManager->Tie("forces/fby-prop-lbs", this,2,\r
-                       (PMF)&FGPropulsion::GetForces);\r
-  PropertyManager->Tie("forces/fbz-prop-lbs", this,3,\r
-                       (PMF)&FGPropulsion::GetForces);\r
-  PropertyManager->Tie("moments/l-prop-lbsft", this,1,\r
-                       (PMF)&FGPropulsion::GetMoments);\r
-  PropertyManager->Tie("moments/m-prop-lbsft", this,2,\r
-                       (PMF)&FGPropulsion::GetMoments);\r
-  PropertyManager->Tie("moments/n-prop-lbsft", this,3,\r
-                       (PMF)&FGPropulsion::GetMoments);\r
-  //PropertyManager->Tie("propulsion/tanks-weight-lbs", this,\r
-  //                     &FGPropulsion::GetTanksWeight);\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-\r
-void FGPropulsion::unbind(void)\r
-{\r
-  /* PropertyManager->Untie("propulsion/num-engines");\r
-  PropertyManager->Untie("propulsion/num-tanks"); */\r
-  PropertyManager->Untie("propulsion/num-sel-fuel-tanks");\r
-  PropertyManager->Untie("propulsion/num-sel-ox-tanks");\r
-  PropertyManager->Untie("propulsion/magneto_cmd");\r
-  PropertyManager->Untie("propulsion/starter_cmd");\r
-  PropertyManager->Untie("propulsion/active_engine");\r
-  PropertyManager->Untie("forces/fbx-prop-lbs");\r
-  PropertyManager->Untie("forces/fby-prop-lbs");\r
-  PropertyManager->Untie("forces/fbz-prop-lbs");\r
-  PropertyManager->Untie("moments/l-prop-lbsft");\r
-  PropertyManager->Untie("moments/m-prop-lbsft");\r
-  PropertyManager->Untie("moments/n-prop-lbsft");\r
-  //PropertyManager->Untie("propulsion/tanks-weight-lbs");\r
-}\r
-\r
-//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\r
-//    The bitmasked value choices are as follows:\r
-//    unset: In this case (the default) JSBSim would only print\r
-//       out the normally expected messages, essentially echoing\r
-//       the config files as they are read. If the environment\r
-//       variable is not set, debug_lvl is set to 1 internally\r
-//    0: This requests JSBSim not to output any messages\r
-//       whatsoever.\r
-//    1: This value explicity requests the normal JSBSim\r
-//       startup messages\r
-//    2: This value asks for a message to be printed out when\r
-//       a class is instantiated\r
-//    4: When this value is set, a message is displayed when a\r
-//       FGModel object executes its Run() method\r
-//    8: When this value is set, various runtime state variables\r
-//       are printed out periodically\r
-//    16: When set various parameters are sanity checked and\r
-//       a message is printed out when they go out of bounds\r
-\r
-void FGPropulsion::Debug(int from)\r
-{\r
-  if (debug_lvl <= 0) return;\r
-\r
-  if (debug_lvl & 1) { // Standard console startup message output\r
-    if (from == 0) { // Constructor\r
-\r
-    }\r
-  }\r
-  if (debug_lvl & 2 ) { // Instantiation/Destruction notification\r
-    if (from == 0) cout << "Instantiated: FGPropulsion" << endl;\r
-    if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;\r
-  }\r
-  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects\r
-  }\r
-  if (debug_lvl & 8 ) { // Runtime state variables\r
-  }\r
-  if (debug_lvl & 16) { // Sanity checking\r
-  }\r
-  if (debug_lvl & 64) {\r
-    if (from == 0) { // Constructor\r
-      cout << IdSrc << endl;\r
-      cout << IdHdr << endl;\r
-    }\r
-  }\r
-}\r
-\r
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+ Module:       FGPropulsion.cpp
+ Author:       Jon S. Berndt
+ Date started: 08/20/00
+ Purpose:      Encapsulates the set of engines, tanks, and thrusters associated
+               with this aircraft
+
+ ------------- Copyright (C) 2000  Jon S. Berndt (jsb@hal-pc.org) -------------
+
+ This program is free software; you can redistribute it and/or modify it under
+ the terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 2 of the License, or (at your option) any later
+ version.
+
+ This program is distributed in the hope that it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
+ details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+ Place - Suite 330, Boston, MA  02111-1307, USA.
+
+ Further information about the GNU General Public License can also be found on
+ the world wide web at http://www.gnu.org.
+
+FUNCTIONAL DESCRIPTION
+--------------------------------------------------------------------------------
+The Propulsion class is the container for the entire propulsion system, which is
+comprised of engines, tanks, and "thrusters" (the device that transforms the
+engine power into a force that acts on the aircraft, such as a nozzle or
+propeller). Once the Propulsion class gets the config file, it reads in
+information which is specific to a type of engine. Then:
+
+1) The appropriate engine type instance is created
+2) A thruster object is instantiated, and is linked to the engine
+3) At least one tank object is created, and is linked to an engine.
+
+At Run time each engines Calculate() method is called to return the excess power
+generated during that iteration. The drag from the previous iteration is sub-
+tracted to give the excess power available for thrust this pass. That quantity
+is passed to the thrusters associated with a particular engine - perhaps with a
+scaling mechanism (gearing?) to allow the engine to give its associated thrust-
+ers specific distributed portions of the excess power.
+
+HISTORY
+--------------------------------------------------------------------------------
+08/20/00   JSB   Created
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+INCLUDES
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+#include "FGPropulsion.h"
+#include "FGPropertyManager.h"
+
+
+static const char *IdSrc = "$Id$";
+static const char *IdHdr = ID_PROPULSION;
+
+extern short debug_lvl;
+
+#if defined (__APPLE__)
+/* Not all systems have the gcvt function */
+inline char* gcvt (double value, int ndigits, char *buf) {
+    /* note that this is not exactly what gcvt is supposed to do! */
+    snprintf (buf, ndigits+1, "%f", value);
+    return buf;
+}
+#endif
+
+
+/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+CLASS IMPLEMENTATION
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
+
+FGPropulsion::FGPropulsion(FGFDMExec* exec) : FGModel(exec)
+{
+  Name = "FGPropulsion";
+  numSelectedFuelTanks = numSelectedOxiTanks = 0;
+  numTanks = numEngines = numThrusters = 0;
+  numOxiTanks = numFuelTanks = 0;
+  dt = 0.0;
+  ActiveEngine = -1; // -1: ALL, 0: Engine 1, 1: Engine 2 ...
+  bind();
+  Debug(0);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FGPropulsion::~FGPropulsion()
+{
+  for (unsigned int i=0; i<Engines.size(); i++) delete Engines[i];
+  Engines.clear();
+  unbind();
+  Debug(1);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bool FGPropulsion::Run(void)
+{
+  double PowerAvailable;
+  dt = State->Getdt();
+
+  vForces.InitMatrix();
+  vMoments.InitMatrix();
+
+  if (!FGModel::Run()) {
+    for (unsigned int i=0; i<numEngines; i++) {
+      Thrusters[i]->SetdeltaT(dt*rate);
+      PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
+      Thrusters[i]->Calculate(PowerAvailable);
+      vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
+      vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
+    }
+    return false;
+  } else {
+    return true;
+  }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bool FGPropulsion::GetSteadyState(void)
+{
+  double PowerAvailable;
+  double currentThrust = 0, lastThrust=-1;
+  dt = State->Getdt();
+  int steady_count,j=0;
+  bool steady=false;
+
+  vForces.InitMatrix();
+  vMoments.InitMatrix();
+
+  if (!FGModel::Run()) {
+    for (unsigned int i=0; i<numEngines; i++) {
+      Engines[i]->SetTrimMode(true);
+      Thrusters[i]->SetdeltaT(dt*rate);
+      steady=false;
+      steady_count=0;
+      while (!steady && j < 6000) {
+        PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
+        lastThrust = currentThrust;
+        currentThrust = Thrusters[i]->Calculate(PowerAvailable);
+        if (fabs(lastThrust-currentThrust) < 0.0001) {
+          steady_count++;
+          if (steady_count > 120) { steady=true; }
+        } else {
+          steady_count=0;
+        }
+        j++;
+      }
+      vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
+      vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
+      Engines[i]->SetTrimMode(false);
+    }
+
+    return false;
+  } else {
+    return true;
+  }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bool FGPropulsion::ICEngineStart(void)
+{
+  double PowerAvailable;
+  int j;
+  dt = State->Getdt();
+
+  vForces.InitMatrix();
+  vMoments.InitMatrix();
+
+  for (unsigned int i=0; i<numEngines; i++) {
+    Engines[i]->SetTrimMode(true);
+    Thrusters[i]->SetdeltaT(dt*rate);
+    j=0;
+    while (!Engines[i]->GetRunning() && j < 2000) {
+      PowerAvailable = Engines[i]->Calculate(Thrusters[i]->GetPowerRequired());
+      Thrusters[i]->Calculate(PowerAvailable);
+      j++;
+    }
+    vForces  += Thrusters[i]->GetBodyForces();  // sum body frame forces
+    vMoments += Thrusters[i]->GetMoments();     // sum body frame moments
+    Engines[i]->SetTrimMode(false);
+  }
+  return true;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+bool FGPropulsion::Load(FGConfigFile* AC_cfg)
+{
+  string token, fullpath;
+  string engineFileName, engType;
+  string thrusterFileName, thrType;
+  string parameter;
+  string enginePath = FDMExec->GetEnginePath();
+  double xLoc, yLoc, zLoc, Pitch, Yaw;
+  double P_Factor = 0, Sense = 0.0;
+  int Feed;
+  bool ThrottleAdded = false;
+
+# ifndef macintosh
+      fullpath = enginePath + "/";
+# else
+      fullpath = enginePath + ";";
+# endif
+
+  AC_cfg->GetNextConfigLine();
+
+  while ((token = AC_cfg->GetValue()) != string("/PROPULSION")) {
+
+    if (token == "AC_ENGINE") {                   // ============ READING ENGINES
+
+      engineFileName = AC_cfg->GetValue("FILE");
+
+      if (debug_lvl > 0) cout << "\n    Reading engine from file: " << fullpath
+                                                + engineFileName + ".xml"<< endl;
+      FGConfigFile Eng_cfg(fullpath + engineFileName + ".xml");
+
+      if (Eng_cfg.IsOpen()) {
+        Eng_cfg.GetNextConfigLine();
+        engType = Eng_cfg.GetValue();
+        cout << engType << endl;
+
+        FCS->AddThrottle();
+        ThrottleAdded = true;
+
+        if (engType == "FG_ROCKET") {
+          Engines.push_back(new FGRocket(FDMExec, &Eng_cfg));
+        } else if (engType == "FG_PISTON") {
+          Engines.push_back(new FGPiston(FDMExec, &Eng_cfg));
+        } else if (engType == "FG_TURBINE") {
+          Engines.push_back(new FGTurbine(FDMExec, &Eng_cfg));
+        } else {
+          cerr << fgred << "    Unrecognized engine type: " << underon << engType
+                    << underoff << " found in config file." << fgdef << endl;
+          return false;
+        }
+
+        AC_cfg->GetNextConfigLine();
+        while ((token = AC_cfg->GetValue()) != string("/AC_ENGINE")) {
+          *AC_cfg >> token;
+          if      (token == "XLOC")  { *AC_cfg >> xLoc; }
+          else if (token == "YLOC")  { *AC_cfg >> yLoc; }
+          else if (token == "ZLOC")  { *AC_cfg >> zLoc; }
+          else if (token == "PITCH") { *AC_cfg >> Pitch;}
+          else if (token == "YAW")   { *AC_cfg >> Yaw;}
+          else if (token == "FEED")  {
+            *AC_cfg >> Feed;
+            Engines[numEngines]->AddFeedTank(Feed);
+            if (debug_lvl > 0) cout << "      Feed tank: " << Feed << endl;
+          } else cerr << "Unknown identifier: " << token << " in engine file: "
+                                                        << engineFileName << endl;
+        }
+
+        if (debug_lvl > 0)  {
+          cout << "      X = " << xLoc << endl;
+          cout << "      Y = " << yLoc << endl;
+          cout << "      Z = " << zLoc << endl;
+          cout << "      Pitch = " << Pitch << endl;
+          cout << "      Yaw = " << Yaw << endl;
+        }
+
+        Engines[numEngines]->SetPlacement(xLoc, yLoc, zLoc, Pitch, Yaw);
+        Engines[numEngines]->SetEngineNumber(numEngines);
+        numEngines++;
+
+      } else {
+
+        cerr << fgred << "\n  Could not read engine config file: " << underon <<
+                    fullpath + engineFileName + ".xml" << underoff << fgdef << endl;
+        return false;
+      }
+
+    } else if (token == "AC_TANK") {              // ============== READING TANKS
+
+      if (debug_lvl > 0) cout << "\n    Reading tank definition" << endl;
+      Tanks.push_back(new FGTank(AC_cfg));
+      switch(Tanks[numTanks]->GetType()) {
+      case FGTank::ttFUEL:
+        numSelectedFuelTanks++;
+        numFuelTanks++;
+        break;
+      case FGTank::ttOXIDIZER:
+        numSelectedOxiTanks++;
+        numOxiTanks++;
+        break;
+      }
+
+      numTanks++;
+
+    } else if (token == "AC_THRUSTER") {          // ========== READING THRUSTERS
+
+      thrusterFileName = AC_cfg->GetValue("FILE");
+
+      if (debug_lvl > 0) cout << "\n    Reading thruster from file: " <<
+                                    fullpath + thrusterFileName + ".xml" << endl;
+      FGConfigFile Thruster_cfg(fullpath + thrusterFileName + ".xml");
+
+      if (Thruster_cfg.IsOpen()) {
+        Thruster_cfg.GetNextConfigLine();
+        thrType = Thruster_cfg.GetValue();
+
+        if (thrType == "FG_PROPELLER") {
+          Thrusters.push_back(new FGPropeller(FDMExec, &Thruster_cfg));
+        } else if (thrType == "FG_NOZZLE") {
+          Thrusters.push_back(new FGNozzle(FDMExec, &Thruster_cfg ));
+        } else if (thrType == "FG_DIRECT") {
+          Thrusters.push_back(new FGThruster( FDMExec, &Thruster_cfg) );
+        }  
+
+        AC_cfg->GetNextConfigLine();
+        while ((token = AC_cfg->GetValue()) != string("/AC_THRUSTER")) {
+          *AC_cfg >> token;
+          if (token == "XLOC") *AC_cfg >> xLoc;
+          else if (token == "YLOC") *AC_cfg >> yLoc;
+          else if (token == "ZLOC") *AC_cfg >> zLoc;
+          else if (token == "PITCH") *AC_cfg >> Pitch;
+          else if (token == "YAW") *AC_cfg >> Yaw;
+          else if (token == "P_FACTOR") *AC_cfg >> P_Factor;
+          else if (token == "SENSE")   *AC_cfg >> Sense;
+          else cerr << "Unknown identifier: " << token << " in engine file: "
+                                                        << engineFileName << endl;
+        }
+
+        Thrusters[numThrusters]->SetLocation(xLoc, yLoc, zLoc);
+        Thrusters[numThrusters]->SetAnglesToBody(0, Pitch, Yaw);
+        if (thrType == "FG_PROPELLER" && P_Factor > 0.001) {
+          ((FGPropeller*)Thrusters[numThrusters])->SetPFactor(P_Factor);
+          if (debug_lvl > 0) cout << "      P-Factor: " << P_Factor << endl;
+          ((FGPropeller*)Thrusters[numThrusters])->SetSense(fabs(Sense)/Sense);
+          if (debug_lvl > 0) cout << "      Sense: " << Sense <<  endl;
+        }
+        Thrusters[numThrusters]->SetdeltaT(dt*rate);
+        Thrusters[numThrusters]->SetThrusterNumber(numThrusters);
+        numThrusters++;
+
+      } else {
+        cerr << "Could not read thruster config file: " << fullpath
+                                                + thrusterFileName + ".xml" << endl;
+        return false;
+      }
+
+    }
+    AC_cfg->GetNextConfigLine();
+  }
+
+  if (!ThrottleAdded) FCS->AddThrottle(); // need to have at least one throttle
+
+  return true;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+string FGPropulsion::GetPropulsionStrings(void)
+{
+  string PropulsionStrings = "";
+  bool firstime = true;
+  char buffer[5];
+
+  for (unsigned int i=0;i<Engines.size();i++) {
+    if (firstime)  firstime = false;
+    else           PropulsionStrings += ", ";
+
+    sprintf(buffer, "%d", i);
+
+    switch(Engines[i]->GetType()) {
+    case FGEngine::etPiston:
+      PropulsionStrings += (Engines[i]->GetName() + "_PwrAvail[" + buffer + "]");
+      break;
+    case FGEngine::etRocket:
+      PropulsionStrings += (Engines[i]->GetName() + "_ChamberPress[" + buffer + "]");
+      break;
+    case FGEngine::etTurbine:
+      break;
+    default:
+      PropulsionStrings += "INVALID ENGINE TYPE";
+      break;
+    }
+
+    PropulsionStrings += ", ";
+
+    FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
+    switch(Thrusters[i]->GetType()) {
+    case FGThruster::ttNozzle:
+      PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "]");
+      break;
+    case FGThruster::ttRotor:
+      break;
+    case FGThruster::ttPropeller:
+      PropulsionStrings += (Thrusters[i]->GetName() + "_Torque[" + buffer + "], ");
+      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Roll[" + buffer + "], ");
+      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Pitch[" + buffer + "], ");
+      PropulsionStrings += (Thrusters[i]->GetName() + "_PFactor_Yaw[" + buffer + "], ");
+      PropulsionStrings += (Thrusters[i]->GetName() + "_Thrust[" + buffer + "], ");
+      if (Propeller->IsVPitch())
+        PropulsionStrings += (Thrusters[i]->GetName() + "_Pitch[" + buffer + "], ");
+      PropulsionStrings += (Thrusters[i]->GetName() + "_RPM[" + buffer + "]");
+      break;
+    default:
+      PropulsionStrings += "INVALID THRUSTER TYPE";
+      break;
+    }
+  }
+
+  return PropulsionStrings;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+string FGPropulsion::GetPropulsionValues(void)
+{
+  char buff[20];
+  string PropulsionValues = "";
+  bool firstime = true;
+
+  for (unsigned int i=0;i<Engines.size();i++) {
+    if (firstime)  firstime = false;
+    else           PropulsionValues += ", ";
+
+    switch(Engines[i]->GetType()) {
+    case FGEngine::etPiston:
+      PropulsionValues += (string(gcvt(((FGPiston*)Engines[i])->GetPowerAvailable(), 10, buff)));
+      break;
+    case FGEngine::etRocket:
+      PropulsionValues += (string(gcvt(((FGRocket*)Engines[i])->GetChamberPressure(), 10, buff)));
+      break;
+    case FGEngine::etTurbine:
+      break;
+    }
+
+    PropulsionValues += ", ";
+
+    switch(Thrusters[i]->GetType()) {
+    case FGThruster::ttNozzle:
+      PropulsionValues += (string(gcvt(((FGNozzle*)Thrusters[i])->GetThrust(), 10, buff)));
+      break;
+    case FGThruster::ttRotor:
+      break;
+    case FGThruster::ttPropeller:
+      FGPropeller* Propeller = (FGPropeller*)Thrusters[i];
+      FGColumnVector3 vPFactor = Propeller->GetPFactor();
+      PropulsionValues += string(gcvt(Propeller->GetTorque(), 10, buff)) + ", ";
+      PropulsionValues += string(gcvt(vPFactor(eRoll), 10, buff)) + ", ";
+      PropulsionValues += string(gcvt(vPFactor(ePitch), 10, buff)) + ", ";
+      PropulsionValues += string(gcvt(vPFactor(eYaw), 10, buff)) + ", ";
+      PropulsionValues += string(gcvt(Propeller->GetThrust(), 10, buff)) + ", ";
+      if (Propeller->IsVPitch())
+        PropulsionValues += string(gcvt(Propeller->GetPitch(), 10, buff)) + ", ";
+      PropulsionValues += string(gcvt(Propeller->GetRPM(), 10, buff));
+      break;
+    }
+  }
+
+  return PropulsionValues;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+FGColumnVector3& FGPropulsion::GetTanksMoment(void)
+{
+  iTank = Tanks.begin();
+  vXYZtank.InitMatrix();
+  while (iTank < Tanks.end()) {
+    vXYZtank(eX) += (*iTank)->GetX()*(*iTank)->GetContents();
+    vXYZtank(eY) += (*iTank)->GetY()*(*iTank)->GetContents();
+    vXYZtank(eZ) += (*iTank)->GetZ()*(*iTank)->GetContents();
+    iTank++;
+  }
+  return vXYZtank;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksWeight(void)
+{
+  double Tw = 0.0;
+
+  iTank = Tanks.begin();
+  while (iTank < Tanks.end()) {
+    Tw += (*iTank)->GetContents();
+    iTank++;
+  }
+  return Tw;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksIxx(const FGColumnVector3& vXYZcg)
+{
+  double I = 0.0;
+  iTank = Tanks.begin();
+  while (iTank < Tanks.end()) {
+    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetX() - vXYZcg(eX)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
+    iTank++;
+  }
+  return I;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksIyy(const FGColumnVector3& vXYZcg)
+{
+  double I = 0.0;
+  iTank = Tanks.begin();
+  while (iTank < Tanks.end()) {
+    I += ((*iTank)->GetY() - vXYZcg(eY))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
+    iTank++;
+  }
+  return I;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksIzz(const FGColumnVector3& vXYZcg)
+{
+  double I = 0.0;
+  iTank = Tanks.begin();
+  while (iTank < Tanks.end()) {
+    I += ((*iTank)->GetZ() - vXYZcg(eZ))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
+    iTank++;
+  }
+  return I;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksIxz(const FGColumnVector3& vXYZcg)
+{
+  double I = 0.0;
+  iTank = Tanks.begin();
+  while (iTank < Tanks.end()) {
+    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetZ() - vXYZcg(eZ)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
+    iTank++;
+  }
+  return I;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+double FGPropulsion::GetTanksIxy(const FGColumnVector3& vXYZcg)
+{
+  double I = 0.0;
+  iTank = Tanks.begin();
+  while (iTank != Tanks.end()) {
+    I += ((*iTank)->GetX() - vXYZcg(eX))*((*iTank)->GetY() - vXYZcg(eY)) * (*iTank)->GetContents()/(144.0*Inertial->gravity());
+    iTank++;
+  }
+  return I;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropulsion::SetMagnetos(int setting)
+{
+  if (ActiveEngine == -1) {
+    for (unsigned i=0; i<Engines.size(); i++) {
+      Engines[i]->SetMagnetos(setting);
+    }
+  } else {
+    Engines[ActiveEngine]->SetMagnetos(setting);
+  }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropulsion::SetStarter(int setting)
+{
+  if (ActiveEngine == -1) {
+    for (unsigned i=0; i<Engines.size(); i++) {
+      Engines[i]->SetStarter(setting);
+    }
+  } else {
+    if (setting == 0)
+      Engines[ActiveEngine]->SetStarter(false);
+    else
+      Engines[ActiveEngine]->SetStarter(true);
+  }
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropulsion::SetActiveEngine(int engine)
+{
+  if ( unsigned(engine) > Engines.size())
+    ActiveEngine = -1;
+  else
+    ActiveEngine = engine;
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropulsion::bind(void)
+{
+  typedef double (FGPropulsion::*PMF)(int) const;
+  typedef int (FGPropulsion::*iPMF)(void) const;
+  /* PropertyManager->Tie("propulsion/num-engines", this,
+                       &FGPropulsion::GetNumEngines);
+  PropertyManager->Tie("propulsion/num-tanks", this,
+                       &FGPropulsion::GetNumTanks); */
+
+  PropertyManager->Tie("propulsion/magneto_cmd", this,
+                       (iPMF)0,
+                       &FGPropulsion::SetMagnetos,
+                       true);
+  PropertyManager->Tie("propulsion/starter_cmd", this,
+                       (iPMF)0,
+                       &FGPropulsion::SetStarter,
+                       true);
+  PropertyManager->Tie("propulsion/active_engine", this,
+                       (iPMF)0,
+                       &FGPropulsion::SetActiveEngine,
+                       true);
+
+  PropertyManager->Tie("propulsion/num-sel-fuel-tanks", this,
+                       &FGPropulsion::GetnumSelectedFuelTanks);
+  PropertyManager->Tie("propulsion/num-sel-ox-tanks", this,
+                       &FGPropulsion::GetnumSelectedOxiTanks);
+  PropertyManager->Tie("forces/fbx-prop-lbs", this,1,
+                       (PMF)&FGPropulsion::GetForces);
+  PropertyManager->Tie("forces/fby-prop-lbs", this,2,
+                       (PMF)&FGPropulsion::GetForces);
+  PropertyManager->Tie("forces/fbz-prop-lbs", this,3,
+                       (PMF)&FGPropulsion::GetForces);
+  PropertyManager->Tie("moments/l-prop-lbsft", this,1,
+                       (PMF)&FGPropulsion::GetMoments);
+  PropertyManager->Tie("moments/m-prop-lbsft", this,2,
+                       (PMF)&FGPropulsion::GetMoments);
+  PropertyManager->Tie("moments/n-prop-lbsft", this,3,
+                       (PMF)&FGPropulsion::GetMoments);
+  //PropertyManager->Tie("propulsion/tanks-weight-lbs", this,
+  //                     &FGPropulsion::GetTanksWeight);
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+void FGPropulsion::unbind(void)
+{
+  /* PropertyManager->Untie("propulsion/num-engines");
+  PropertyManager->Untie("propulsion/num-tanks"); */
+  PropertyManager->Untie("propulsion/num-sel-fuel-tanks");
+  PropertyManager->Untie("propulsion/num-sel-ox-tanks");
+  PropertyManager->Untie("propulsion/magneto_cmd");
+  PropertyManager->Untie("propulsion/starter_cmd");
+  PropertyManager->Untie("propulsion/active_engine");
+  PropertyManager->Untie("forces/fbx-prop-lbs");
+  PropertyManager->Untie("forces/fby-prop-lbs");
+  PropertyManager->Untie("forces/fbz-prop-lbs");
+  PropertyManager->Untie("moments/l-prop-lbsft");
+  PropertyManager->Untie("moments/m-prop-lbsft");
+  PropertyManager->Untie("moments/n-prop-lbsft");
+  //PropertyManager->Untie("propulsion/tanks-weight-lbs");
+}
+
+//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+//    The bitmasked value choices are as follows:
+//    unset: In this case (the default) JSBSim would only print
+//       out the normally expected messages, essentially echoing
+//       the config files as they are read. If the environment
+//       variable is not set, debug_lvl is set to 1 internally
+//    0: This requests JSBSim not to output any messages
+//       whatsoever.
+//    1: This value explicity requests the normal JSBSim
+//       startup messages
+//    2: This value asks for a message to be printed out when
+//       a class is instantiated
+//    4: When this value is set, a message is displayed when a
+//       FGModel object executes its Run() method
+//    8: When this value is set, various runtime state variables
+//       are printed out periodically
+//    16: When set various parameters are sanity checked and
+//       a message is printed out when they go out of bounds
+
+void FGPropulsion::Debug(int from)
+{
+  if (debug_lvl <= 0) return;
+
+  if (debug_lvl & 1) { // Standard console startup message output
+    if (from == 0) { // Constructor
+
+    }
+  }
+  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
+    if (from == 0) cout << "Instantiated: FGPropulsion" << endl;
+    if (from == 1) cout << "Destroyed:    FGPropulsion" << endl;
+  }
+  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
+  }
+  if (debug_lvl & 8 ) { // Runtime state variables
+  }
+  if (debug_lvl & 16) { // Sanity checking
+  }
+  if (debug_lvl & 64) {
+    if (from == 0) { // Constructor
+      cout << IdSrc << endl;
+      cout << IdHdr << endl;
+    }
+  }
+}
+