![]() |
JSBSim Flight Dynamics Model 1.0 (23 February 2013)
An Open Source Flight Dynamics and Control Software Library in C++
|
00001 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00002 00003 Module: FGRocket.cpp 00004 Author: Jon S. Berndt 00005 Date started: 09/12/2000 00006 Purpose: This module models a rocket engine 00007 00008 ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) -------------- 00009 00010 This program is free software; you can redistribute it and/or modify it under 00011 the terms of the GNU Lesser General Public License as published by the Free Software 00012 Foundation; either version 2 of the License, or (at your option) any later 00013 version. 00014 00015 This program is distributed in the hope that it will be useful, but WITHOUT 00016 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 00017 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 00018 details. 00019 00020 You should have received a copy of the GNU Lesser General Public License along with 00021 this program; if not, write to the Free Software Foundation, Inc., 59 Temple 00022 Place - Suite 330, Boston, MA 02111-1307, USA. 00023 00024 Further information about the GNU Lesser General Public License can also be found on 00025 the world wide web at http://www.gnu.org. 00026 00027 FUNCTIONAL DESCRIPTION 00028 -------------------------------------------------------------------------------- 00029 00030 This class descends from the FGEngine class and models a rocket engine based on 00031 parameters given in the engine config file for this class 00032 00033 HISTORY 00034 -------------------------------------------------------------------------------- 00035 09/12/2000 JSB Created 00036 00037 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00038 INCLUDES 00039 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 00040 00041 #include <iostream> 00042 #include <sstream> 00043 #include "FGRocket.h" 00044 #include "FGThruster.h" 00045 00046 using namespace std; 00047 00048 namespace JSBSim { 00049 00050 static const char *IdSrc = "$Id: FGRocket.cpp,v 1.29 2013/01/12 21:11:59 jberndt Exp $"; 00051 static const char *IdHdr = ID_ROCKET; 00052 00053 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00054 CLASS IMPLEMENTATION 00055 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ 00056 00057 FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number, struct Inputs& input) 00058 : FGEngine(exec, el, engine_number, input), isp_function(0L) 00059 { 00060 Type = etRocket; 00061 Element* thrust_table_element = 0; 00062 ThrustTable = 0L; 00063 BurnTime = 0.0; 00064 previousFuelNeedPerTank = 0.0; 00065 previousOxiNeedPerTank = 0.0; 00066 PropellantFlowRate = 0.0; 00067 TotalPropellantExpended = 0.0; 00068 FuelFlowRate = FuelExpended = 0.0; 00069 OxidizerFlowRate = OxidizerExpended = 0.0; 00070 SLOxiFlowMax = SLFuelFlowMax = PropFlowMax = 0.0; 00071 MxR = 0.0; 00072 BuildupTime = 0.0; 00073 It = ItVac = 0.0; 00074 ThrustVariation = 0.0; 00075 TotalIspVariation = 0.0; 00076 VacThrust = 0.0; 00077 Flameout = false; 00078 00079 // Defaults 00080 MinThrottle = 0.0; 00081 MaxThrottle = 1.0; 00082 00083 string base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); 00084 00085 std::stringstream strEngineNumber; 00086 strEngineNumber << EngineNumber; 00087 00088 Element* isp_el = el->FindElement("isp"); 00089 Element* isp_func_el=0; 00090 00091 bindmodel(); // Bind model properties first, since they might be needed in functions. 00092 00093 // Specific impulse may be specified as a constant value or as a function - perhaps as a function of mixture ratio. 00094 if (isp_el) { 00095 isp_func_el = isp_el->FindElement("function"); 00096 if (isp_func_el) { 00097 isp_function = new FGFunction(exec->GetPropertyManager(),isp_func_el, strEngineNumber.str()); 00098 } else { 00099 Isp = el->FindElementValueAsNumber("isp"); 00100 } 00101 } else { 00102 throw("Specific Impulse <isp> must be specified for a rocket engine"); 00103 } 00104 00105 if (el->FindElement("builduptime")) 00106 BuildupTime = el->FindElementValueAsNumber("builduptime"); 00107 if (el->FindElement("maxthrottle")) 00108 MaxThrottle = el->FindElementValueAsNumber("maxthrottle"); 00109 if (el->FindElement("minthrottle")) 00110 MinThrottle = el->FindElementValueAsNumber("minthrottle"); 00111 00112 if (el->FindElement("slfuelflowmax")) { 00113 SLFuelFlowMax = el->FindElementValueAsNumberConvertTo("slfuelflowmax", "LBS/SEC"); 00114 if (el->FindElement("sloxiflowmax")) { 00115 SLOxiFlowMax = el->FindElementValueAsNumberConvertTo("sloxiflowmax", "LBS/SEC"); 00116 } 00117 PropFlowMax = SLOxiFlowMax + SLFuelFlowMax; 00118 MxR = SLOxiFlowMax/SLFuelFlowMax; 00119 } else if (el->FindElement("propflowmax")) { 00120 PropFlowMax = el->FindElementValueAsNumberConvertTo("propflowmax", "LBS/SEC"); 00121 // Mixture ratio may be specified here, but it can also be specified as a function or via property 00122 if (el->FindElement("mixtureratio")) { 00123 MxR = el->FindElementValueAsNumber("mixtureratio"); 00124 } 00125 } 00126 00127 if (isp_function) Isp = isp_function->GetValue(); // cause Isp function to be executed if present. 00128 // If there is a thrust table element, this is a solid propellant engine. 00129 thrust_table_element = el->FindElement("thrust_table"); 00130 if (thrust_table_element) { 00131 ThrustTable = new FGTable(PropertyManager, thrust_table_element); 00132 Element* variation_element = el->FindElement("variation"); 00133 if (variation_element) { 00134 if (variation_element->FindElement("thrust")) { 00135 ThrustVariation = variation_element->FindElementValueAsNumber("thrust"); 00136 } 00137 if (variation_element->FindElement("total_isp")) { 00138 TotalIspVariation = variation_element->FindElementValueAsNumber("total_isp"); 00139 } 00140 } 00141 } 00142 00143 00144 Debug(0); 00145 } 00146 00147 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00148 00149 FGRocket::~FGRocket(void) 00150 { 00151 delete ThrustTable; 00152 Debug(1); 00153 } 00154 00155 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00156 00157 void FGRocket::Calculate(void) 00158 { 00159 if (FDMExec->IntegrationSuspended()) return; 00160 00161 RunPreFunctions(); 00162 00163 PropellantFlowRate = (FuelExpended + OxidizerExpended)/in.TotalDeltaT; 00164 TotalPropellantExpended += FuelExpended + OxidizerExpended; 00165 // If Isp has been specified as a function, override the value of Isp to that, otherwise 00166 // assume a constant value is given. 00167 if (isp_function) Isp = isp_function->GetValue(); 00168 00169 // If there is a thrust table, it is a function of propellant burned. The 00170 // engine is started when the throttle is advanced to 1.0. After that, it 00171 // burns without regard to throttle setting. 00172 00173 if (ThrustTable != 0L) { // Thrust table given -> Solid fuel used 00174 00175 if ((in.ThrottlePos[EngineNumber] == 1 || BurnTime > 0.0 ) && !Starved) { 00176 00177 VacThrust = ThrustTable->GetValue(TotalPropellantExpended) 00178 * (ThrustVariation + 1) 00179 * (TotalIspVariation + 1); 00180 if (BurnTime <= BuildupTime && BuildupTime > 0.0) { 00181 VacThrust *= sin((BurnTime/BuildupTime)*M_PI/2.0); 00182 // VacThrust *= (1-cos((BurnTime/BuildupTime)*M_PI))/2.0; // 1 - cos approach 00183 } 00184 BurnTime += in.TotalDeltaT; // Increment burn time 00185 } else { 00186 VacThrust = 0.0; 00187 } 00188 00189 } else { // liquid fueled rocket assumed 00190 00191 if (in.ThrottlePos[EngineNumber] < MinThrottle || Starved) { // Combustion not supported 00192 00193 PctPower = 0.0; // desired thrust 00194 Flameout = true; 00195 VacThrust = 0.0; 00196 00197 } else { // Calculate thrust 00198 00199 // PctPower = Throttle / MaxThrottle; // Min and MaxThrottle range from 0.0 to 1.0, normally. 00200 00201 PctPower = in.ThrottlePos[EngineNumber]; 00202 Flameout = false; 00203 VacThrust = Isp * PropellantFlowRate; 00204 00205 } 00206 00207 } // End thrust calculations 00208 00209 LoadThrusterInputs(); 00210 It += Thruster->Calculate(VacThrust) * in.TotalDeltaT; 00211 ItVac += VacThrust * in.TotalDeltaT; 00212 00213 RunPostFunctions(); 00214 } 00215 00216 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00217 // 00218 // The FuelFlowRate can be affected by the TotalIspVariation value (settable 00219 // in a config file or via properties). The TotalIspVariation parameter affects 00220 // thrust, but the thrust determines fuel flow rate, so it must be adjusted 00221 // for Total Isp Variation. 00222 00223 double FGRocket::CalcFuelNeed(void) 00224 { 00225 if (ThrustTable != 0L) { // Thrust table given - infers solid fuel 00226 FuelFlowRate = VacThrust/Isp; // This calculates wdot (weight flow rate in lbs/sec) 00227 FuelFlowRate /= (1 + TotalIspVariation); 00228 } else { 00229 SLFuelFlowMax = PropFlowMax / (1 + MxR); 00230 FuelFlowRate = SLFuelFlowMax * PctPower; 00231 } 00232 00233 FuelExpended = FuelFlowRate * in.TotalDeltaT; // For this time step ... 00234 return FuelExpended; 00235 } 00236 00237 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00238 00239 double FGRocket::CalcOxidizerNeed(void) 00240 { 00241 SLOxiFlowMax = PropFlowMax * MxR / (1 + MxR); 00242 OxidizerFlowRate = SLOxiFlowMax * PctPower; 00243 OxidizerExpended = OxidizerFlowRate * in.TotalDeltaT; 00244 return OxidizerExpended; 00245 } 00246 00247 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00248 00249 string FGRocket::GetEngineLabels(const string& delimiter) 00250 { 00251 std::ostringstream buf; 00252 00253 buf << Name << " Total Impulse (engine " << EngineNumber << " in psf)" << delimiter 00254 << Name << " Total Vacuum Impulse (engine " << EngineNumber << " in psf)" << delimiter 00255 << Thruster->GetThrusterLabels(EngineNumber, delimiter); 00256 00257 return buf.str(); 00258 } 00259 00260 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00261 00262 string FGRocket::GetEngineValues(const string& delimiter) 00263 { 00264 std::ostringstream buf; 00265 00266 buf << It << delimiter 00267 << ItVac << delimiter 00268 << Thruster->GetThrusterValues(EngineNumber, delimiter); 00269 00270 return buf.str(); 00271 } 00272 00273 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00274 // This function should tie properties to rocket engine specific properties 00275 // that are not bound in the base class (FGEngine) code. 00276 // 00277 void FGRocket::bindmodel() 00278 { 00279 string property_name, base_property_name; 00280 base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); 00281 00282 property_name = base_property_name + "/total-impulse"; 00283 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalImpulse); 00284 property_name = base_property_name + "/vacuum-thrust_lbs"; 00285 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetVacThrust); 00286 00287 if (ThrustTable) { // Solid rocket motor 00288 property_name = base_property_name + "/thrust-variation_pct"; 00289 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetThrustVariation, 00290 &FGRocket::SetThrustVariation); 00291 property_name = base_property_name + "/total-isp-variation_pct"; 00292 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalIspVariation, 00293 &FGRocket::SetTotalIspVariation); 00294 } else { // Liquid rocket motor 00295 property_name = base_property_name + "/oxi-flow-rate-pps"; 00296 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetOxiFlowRate); 00297 property_name = base_property_name + "/mixture-ratio"; 00298 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetMixtureRatio, 00299 &FGRocket::SetMixtureRatio); 00300 property_name = base_property_name + "/isp"; 00301 PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetIsp, 00302 &FGRocket::SetIsp); 00303 } 00304 } 00305 00306 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00307 // The bitmasked value choices are as follows: 00308 // unset: In this case (the default) JSBSim would only print 00309 // out the normally expected messages, essentially echoing 00310 // the config files as they are read. If the environment 00311 // variable is not set, debug_lvl is set to 1 internally 00312 // 0: This requests JSBSim not to output any messages 00313 // whatsoever. 00314 // 1: This value explicity requests the normal JSBSim 00315 // startup messages 00316 // 2: This value asks for a message to be printed out when 00317 // a class is instantiated 00318 // 4: When this value is set, a message is displayed when a 00319 // FGModel object executes its Run() method 00320 // 8: When this value is set, various runtime state variables 00321 // are printed out periodically 00322 // 16: When set various parameters are sanity checked and 00323 // a message is printed out when they go out of bounds 00324 00325 void FGRocket::Debug(int from) 00326 { 00327 if (debug_lvl <= 0) return; 00328 00329 if (debug_lvl & 1) { // Standard console startup message output 00330 if (from == 0) { // Constructor 00331 cout << " Engine Name: " << Name << endl; 00332 cout << " Vacuum Isp = " << Isp << endl; 00333 cout << " Maximum Throttle = " << MaxThrottle << endl; 00334 cout << " Minimum Throttle = " << MinThrottle << endl; 00335 cout << " Fuel Flow (max) = " << SLFuelFlowMax << endl; 00336 cout << " Oxidizer Flow (max) = " << SLOxiFlowMax << endl; 00337 if (SLFuelFlowMax > 0) 00338 cout << " Mixture ratio = " << SLOxiFlowMax/SLFuelFlowMax << endl; 00339 } 00340 } 00341 if (debug_lvl & 2 ) { // Instantiation/Destruction notification 00342 if (from == 0) cout << "Instantiated: FGRocket" << endl; 00343 if (from == 1) cout << "Destroyed: FGRocket" << endl; 00344 } 00345 if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects 00346 } 00347 if (debug_lvl & 8 ) { // Runtime state variables 00348 } 00349 if (debug_lvl & 16) { // Sanity checking 00350 } 00351 if (debug_lvl & 64) { 00352 if (from == 0) { // Constructor 00353 cout << IdSrc << endl; 00354 cout << IdHdr << endl; 00355 } 00356 } 00357 } 00358 }