JSBSim Flight Dynamics Model  1.2.0 (05 Nov 2023)
An Open Source Flight Dynamics and Control Software Library in C++
FGPiston.cpp
1 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 
3  Module: FGPiston.cpp
4  Author: Jon S. Berndt, JSBSim framework
5  Dave Luff, Piston engine model
6  Ronald Jensen, Piston engine model
7  Date started: 09/12/2000
8  Purpose: This module models a Piston engine
9 
10  ------------- Copyright (C) 2000 Jon S. Berndt (jon@jsbsim.org) --------------
11 
12  This program is free software; you can redistribute it and/or modify it under
13  the terms of the GNU Lesser General Public License as published by the Free Software
14  Foundation; either version 2 of the License, or (at your option) any later
15  version.
16 
17  This program is distributed in the hope that it will be useful, but WITHOUT
18  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19  FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
20  details.
21 
22  You should have received a copy of the GNU Lesser General Public License along with
23  this program; if not, write to the Free Software Foundation, Inc., 59 Temple
24  Place - Suite 330, Boston, MA 02111-1307, USA.
25 
26  Further information about the GNU Lesser General Public License can also be found on
27  the world wide web at http://www.gnu.org.
28 
29 FUNCTIONAL DESCRIPTION
30 --------------------------------------------------------------------------------
31 
32 This class descends from the FGEngine class and models a Piston engine based on
33 parameters given in the engine config file for this class
34 
35 HISTORY
36 --------------------------------------------------------------------------------
37 09/12/2000 JSB Created
38 
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40 INCLUDES
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
42 
43 #include <iostream>
44 #include <sstream>
45 
46 #include "FGFDMExec.h"
47 #include "FGPiston.h"
48 #include "FGPropeller.h"
49 #include "input_output/FGXMLElement.h"
50 
51 using namespace std;
52 
53 namespace JSBSim {
54 
55 /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 CLASS IMPLEMENTATION
57 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/
58 
59 FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number, struct Inputs& input)
60  : FGEngine(engine_number, input),
61  R_air(287.3), // Gas constant for air J/Kg/K
62  calorific_value_fuel(47.3e6), // J/Kg
63  Cp_air(1005), // Specific heat (constant pressure) J/Kg/K
64  Cp_fuel(1700),
65  standard_pressure(101320.73)
66 {
67  Load(exec, el);
68 
69  Element *table_element;
70  auto PropertyManager = exec->GetPropertyManager();
71 
72  // Defaults and initializations
73 
74  Type = etPiston;
75 
76  // These items are read from the configuration file
77  // Defaults are from a Lycoming O-360, more or less
78 
79  Cycles = 4;
80  IdleRPM = 600;
81  MaxRPM = 2800;
82  Displacement = 360;
83  SparkFailDrop = 1.0;
84  MaxHP = 200;
85  MinManifoldPressure_inHg = 6.5;
86  MaxManifoldPressure_inHg = 28.5;
87  ManifoldPressureLag=1.0;
88  ISFC = -1;
89  volumetric_efficiency = 0.85;
90  Bore = 5.125;
91  Stroke = 4.375;
92  Cylinders = 4;
93  CylinderHeadMass = 2; //kg
94  CompressionRatio = 8.5;
95  Z_airbox = -999;
96  Ram_Air_Factor = 1;
97  PeakMeanPistonSpeed_fps = 100;
98  FMEPDynamic= 18400;
99  FMEPStatic = 46500;
100  Cooling_Factor = 0.5144444;
101  StaticFriction_HP = 1.5;
102  StarterGain = 1.;
103  StarterTorque = -1.;
104  StarterRPM = -1.;
105 
106  // These are internal program variables
107 
108  Lookup_Combustion_Efficiency = 0;
109  Mixture_Efficiency_Correlation = 0;
110  crank_counter = 0;
111  Magnetos = 0;
112  minMAP = 21950;
113  maxMAP = 96250;
114 
115  ResetToIC();
116 
117  // Supercharging
118  BoostSpeeds = 0; // Default to no supercharging
119  BoostSpeed = 0;
120  Boosted = false;
121  BoostOverride = 0;
122  BoostManual = 0;
123  bBoostOverride = false;
124  bTakeoffBoost = false;
125  TakeoffBoost = 0.0; // Default to no extra takeoff-boost
126  BoostLossFactor = 0.0; // Default to free boost
127 
128  int i;
129  for (i=0; i<FG_MAX_BOOST_SPEEDS; i++) {
130  RatedBoost[i] = 0.0;
131  RatedPower[i] = 0.0;
132  RatedAltitude[i] = 0.0;
133  BoostMul[i] = 1.0;
134  RatedMAP[i] = 100000;
135  RatedRPM[i] = 2500;
136  TakeoffMAP[i] = 100000;
137  }
138  for (i=0; i<FG_MAX_BOOST_SPEEDS-1; i++) {
139  BoostSwitchAltitude[i] = 0.0;
140  BoostSwitchPressure[i] = 0.0;
141  }
142 
143  // Read inputs from engine data file where present.
144 
145  if (el->FindElement("minmp"))
146  MinManifoldPressure_inHg = el->FindElementValueAsNumberConvertTo("minmp","INHG");
147  if (el->FindElement("maxmp"))
148  MaxManifoldPressure_inHg = el->FindElementValueAsNumberConvertTo("maxmp","INHG");
149  if (el->FindElement("man-press-lag"))
150  ManifoldPressureLag = el->FindElementValueAsNumber("man-press-lag");
151  if (el->FindElement("displacement"))
152  Displacement = el->FindElementValueAsNumberConvertTo("displacement","IN3");
153  if (el->FindElement("maxhp"))
154  MaxHP = el->FindElementValueAsNumberConvertTo("maxhp","HP");
155  if (el->FindElement("static-friction"))
156  StaticFriction_HP = el->FindElementValueAsNumberConvertTo("static-friction","HP");
157  if (el->FindElement("sparkfaildrop"))
158  SparkFailDrop = Constrain(0, 1 - el->FindElementValueAsNumber("sparkfaildrop"), 1);
159  if (el->FindElement("cycles"))
160  Cycles = el->FindElementValueAsNumber("cycles");
161  if (el->FindElement("idlerpm"))
162  IdleRPM = el->FindElementValueAsNumber("idlerpm");
163  if (el->FindElement("maxrpm"))
164  MaxRPM = el->FindElementValueAsNumber("maxrpm");
165  if (el->FindElement("maxthrottle"))
166  MaxThrottle = el->FindElementValueAsNumber("maxthrottle");
167  if (el->FindElement("minthrottle"))
168  MinThrottle = el->FindElementValueAsNumber("minthrottle");
169  if (el->FindElement("bsfc"))
170  ISFC = el->FindElementValueAsNumberConvertTo("bsfc", "LBS/HP*HR");
171  if (el->FindElement("volumetric-efficiency"))
172  volumetric_efficiency = el->FindElementValueAsNumber("volumetric-efficiency");
173  if (el->FindElement("compression-ratio"))
174  CompressionRatio = el->FindElementValueAsNumber("compression-ratio");
175  if (el->FindElement("bore"))
176  Bore = el->FindElementValueAsNumberConvertTo("bore","IN");
177  if (el->FindElement("stroke"))
178  Stroke = el->FindElementValueAsNumberConvertTo("stroke","IN");
179  if (el->FindElement("cylinders"))
180  Cylinders = el->FindElementValueAsNumber("cylinders");
181  if (el->FindElement("cylinder-head-mass"))
182  CylinderHeadMass = el->FindElementValueAsNumberConvertTo("cylinder-head-mass","KG");
183  if (el->FindElement("air-intake-impedance-factor"))
184  Z_airbox = el->FindElementValueAsNumber("air-intake-impedance-factor");
185  if (el->FindElement("ram-air-factor"))
186  Ram_Air_Factor = el->FindElementValueAsNumber("ram-air-factor");
187  if (el->FindElement("cooling-factor"))
188  Cooling_Factor = el->FindElementValueAsNumber("cooling-factor");
189  if (el->FindElement("starter-rpm"))
190  StarterRPM = el->FindElementValueAsNumber("starter-rpm");
191  if (el->FindElement("starter-torque"))
192  StarterTorque = el->FindElementValueAsNumber("starter-torque");
193  if (el->FindElement("dynamic-fmep"))
194  FMEPDynamic= el->FindElementValueAsNumberConvertTo("dynamic-fmep","PA");
195  if (el->FindElement("static-fmep"))
196  FMEPStatic = el->FindElementValueAsNumberConvertTo("static-fmep","PA");
197  if (el->FindElement("peak-piston-speed"))
198  PeakMeanPistonSpeed_fps = el->FindElementValueAsNumber("peak-piston-speed");
199  if (el->FindElement("numboostspeeds")) { // Turbo- and super-charging parameters
200  BoostSpeeds = (int)el->FindElementValueAsNumber("numboostspeeds");
201  if (el->FindElement("boostoverride"))
202  BoostOverride = (int)el->FindElementValueAsNumber("boostoverride");
203  if (el->FindElement("boostmanual"))
204  BoostManual = (int)el->FindElementValueAsNumber("boostmanual");
205  if (el->FindElement("takeoffboost"))
206  TakeoffBoost = el->FindElementValueAsNumberConvertTo("takeoffboost", "PSI");
207  if (el->FindElement("boost-loss-factor"))
208  BoostLossFactor = el->FindElementValueAsNumber("boost-loss-factor");
209  if (el->FindElement("ratedboost1"))
210  RatedBoost[0] = el->FindElementValueAsNumberConvertTo("ratedboost1", "PSI");
211  if (el->FindElement("ratedboost2"))
212  RatedBoost[1] = el->FindElementValueAsNumberConvertTo("ratedboost2", "PSI");
213  if (el->FindElement("ratedboost3"))
214  RatedBoost[2] = el->FindElementValueAsNumberConvertTo("ratedboost3", "PSI");
215  if (el->FindElement("ratedpower1"))
216  RatedPower[0] = el->FindElementValueAsNumberConvertTo("ratedpower1", "HP");
217  if (el->FindElement("ratedpower2"))
218  RatedPower[1] = el->FindElementValueAsNumberConvertTo("ratedpower2", "HP");
219  if (el->FindElement("ratedpower3"))
220  RatedPower[2] = el->FindElementValueAsNumberConvertTo("ratedpower3", "HP");
221  if (el->FindElement("ratedrpm1"))
222  RatedRPM[0] = el->FindElementValueAsNumber("ratedrpm1");
223  if (el->FindElement("ratedrpm2"))
224  RatedRPM[1] = el->FindElementValueAsNumber("ratedrpm2");
225  if (el->FindElement("ratedrpm3"))
226  RatedRPM[2] = el->FindElementValueAsNumber("ratedrpm3");
227  if (el->FindElement("ratedaltitude1"))
228  RatedAltitude[0] = el->FindElementValueAsNumberConvertTo("ratedaltitude1", "FT");
229  if (el->FindElement("ratedaltitude2"))
230  RatedAltitude[1] = el->FindElementValueAsNumberConvertTo("ratedaltitude2", "FT");
231  if (el->FindElement("ratedaltitude3"))
232  RatedAltitude[2] = el->FindElementValueAsNumberConvertTo("ratedaltitude3", "FT");
233  }
234 
235  Design_Oil_Temp = 358; // degK;
236  Oil_Viscosity_Index = 0.25;
237  Oil_Press_Relief_Valve = 60; // psi
238  Oil_Press_RPM_Max = MaxRPM*0.75;
239  if (el->FindElement("oil-pressure-relief-valve-psi"))
240  Oil_Press_Relief_Valve = el->FindElementValueAsNumberConvertTo("oil-pressure-relief-valve-psi", "PSI");
241  if (el->FindElement("design-oil-temp-degK"))
242  Design_Oil_Temp = el->FindElementValueAsNumberConvertTo("design-oil-temp-degK", "DEGK");
243  if (el->FindElement("oil-pressure-rpm-max"))
244  Oil_Press_RPM_Max = el->FindElementValueAsNumber("oil-pressure-rpm-max");
245  if (el->FindElement("oil-viscosity-index"))
246  Oil_Viscosity_Index = el->FindElementValueAsNumber("oil-viscosity-index");
247 
248  while((table_element = el->FindNextElement("table")) != 0) {
249  string name = table_element->GetAttributeValue("name");
250  try {
251  if (name == "COMBUSTION") {
252  Lookup_Combustion_Efficiency = new FGTable(PropertyManager, table_element);
253  } else if (name == "MIXTURE") {
254  Mixture_Efficiency_Correlation = new FGTable(PropertyManager, table_element);
255  } else {
256  cerr << "Unknown table type: " << name << " in piston engine definition." << endl;
257  }
258  } catch (std::string& str) {
259  // Make sure allocated resources are freed before rethrowing.
260  // (C++ standard guarantees that a null pointer deletion is no-op).
261  delete Lookup_Combustion_Efficiency;
262  delete Mixture_Efficiency_Correlation;
263  throw("Error loading piston engine table:" + name + ". " + str);
264  }
265  }
266 
267 
268  volumetric_efficiency_reduced = volumetric_efficiency;
269 
270  if(StarterRPM < 0.) StarterRPM = 2*IdleRPM;
271  if(StarterTorque < 0)
272  StarterTorque = (MaxHP)*0.4; //just a wag.
273 
274  displacement_SI = Displacement * in3tom3;
275  RatedMeanPistonSpeed_fps = ( MaxRPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS
276 
277  // Create IFSC to match the engine if not provided
278  if (ISFC < 0) {
279  double pmep = 29.92 - MaxManifoldPressure_inHg;
280  pmep *= inhgtopa * volumetric_efficiency;
281  double fmep = (FMEPDynamic * RatedMeanPistonSpeed_fps * fttom + FMEPStatic);
282  double hp_loss = ((pmep + fmep) * displacement_SI * MaxRPM)/(Cycles*22371);
283  ISFC = ( 1.1*Displacement * MaxRPM * volumetric_efficiency *(MaxManifoldPressure_inHg / 29.92) ) / (9411 * (MaxHP+hp_loss-StaticFriction_HP));
284 // cout <<"FMEP: "<< fmep <<" PMEP: "<< pmep << " hp_loss: " <<hp_loss <<endl;
285  }
286  if ( MaxManifoldPressure_inHg > 29.9 ) { // Don't allow boosting with a bogus number
287  MaxManifoldPressure_inHg = 29.9;
288  }
289  minMAP = MinManifoldPressure_inHg * inhgtopa; // inHg to Pa
290  maxMAP = MaxManifoldPressure_inHg * inhgtopa;
291 
292 // For throttle
293 /*
294  * Pm = ( Ze / ( Ze + Zi + Zt ) ) * Pa
295  * Where:
296  * Pm = Manifold Pressure
297  * Pa = Ambient Pressre
298  * Ze = engine impedance, Ze is effectively 1 / Mean Piston Speed
299  * Zi = airbox impedance
300  * Zt = throttle impedance
301  *
302  * For the calculation below throttle is fully open or Zt = 0
303  *
304  *
305  *
306  */
307  if(Z_airbox < 0.0){
308  double Ze=PeakMeanPistonSpeed_fps/RatedMeanPistonSpeed_fps; // engine impedence
309  Z_airbox = (standard_pressure *Ze / maxMAP) - Ze; // impedence of airbox
310  }
311  // Constant for Throttle impedence
312  Z_throttle=(PeakMeanPistonSpeed_fps/((IdleRPM * Stroke) / 360))*(standard_pressure/minMAP - 1) - Z_airbox;
313  // Z_throttle=(MaxRPM/IdleRPM )*(standard_pressure/minMAP+2); // Constant for Throttle impedence
314 
315 // Default tables if not provided in the configuration file
316  if(Lookup_Combustion_Efficiency == 0) {
317  // First column is thi, second is neta (combustion efficiency)
318  Lookup_Combustion_Efficiency = new FGTable(12);
319  *Lookup_Combustion_Efficiency << 0.00 << 0.980;
320  *Lookup_Combustion_Efficiency << 0.90 << 0.980;
321  *Lookup_Combustion_Efficiency << 1.00 << 0.970;
322  *Lookup_Combustion_Efficiency << 1.05 << 0.950;
323  *Lookup_Combustion_Efficiency << 1.10 << 0.900;
324  *Lookup_Combustion_Efficiency << 1.15 << 0.850;
325  *Lookup_Combustion_Efficiency << 1.20 << 0.790;
326  *Lookup_Combustion_Efficiency << 1.30 << 0.700;
327  *Lookup_Combustion_Efficiency << 1.40 << 0.630;
328  *Lookup_Combustion_Efficiency << 1.50 << 0.570;
329  *Lookup_Combustion_Efficiency << 1.60 << 0.525;
330  *Lookup_Combustion_Efficiency << 2.00 << 0.345;
331  }
332 
333  // First column is Fuel/Air Ratio, second is neta (mixture efficiency)
334  if( Mixture_Efficiency_Correlation == 0) {
335  Mixture_Efficiency_Correlation = new FGTable(15);
336  *Mixture_Efficiency_Correlation << 0.05000 << 0.00000;
337  *Mixture_Efficiency_Correlation << 0.05137 << 0.00862;
338  *Mixture_Efficiency_Correlation << 0.05179 << 0.21552;
339  *Mixture_Efficiency_Correlation << 0.05430 << 0.48276;
340  *Mixture_Efficiency_Correlation << 0.05842 << 0.70690;
341  *Mixture_Efficiency_Correlation << 0.06312 << 0.83621;
342  *Mixture_Efficiency_Correlation << 0.06942 << 0.93103;
343  *Mixture_Efficiency_Correlation << 0.07786 << 1.00000;
344  *Mixture_Efficiency_Correlation << 0.08845 << 1.00000;
345  *Mixture_Efficiency_Correlation << 0.09270 << 0.98276;
346  *Mixture_Efficiency_Correlation << 0.10120 << 0.93103;
347  *Mixture_Efficiency_Correlation << 0.11455 << 0.72414;
348  *Mixture_Efficiency_Correlation << 0.12158 << 0.45690;
349  *Mixture_Efficiency_Correlation << 0.12435 << 0.23276;
350  *Mixture_Efficiency_Correlation << 0.12500 << 0.00000;
351  }
352 
353  string property_name, base_property_name;
354  base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber);
355  property_name = base_property_name + "/power-hp";
356  PropertyManager->Tie(property_name, &HP);
357  property_name = base_property_name + "/friction-hp";
358  PropertyManager->Tie(property_name, &StaticFriction_HP);
359  property_name = base_property_name + "/bsfc-lbs_hphr";
360  PropertyManager->Tie(property_name, &ISFC);
361  property_name = base_property_name + "/starter-norm";
362  PropertyManager->Tie(property_name, &StarterGain);
363  property_name = base_property_name + "/volumetric-efficiency";
364  PropertyManager->Tie(property_name, &volumetric_efficiency);
365  property_name = base_property_name + "/map-pa";
366  PropertyManager->Tie(property_name, &MAP);
367  property_name = base_property_name + "/map-inhg";
368  PropertyManager->Tie(property_name, &ManifoldPressure_inHg);
369  property_name = base_property_name + "/air-intake-impedance-factor";
370  PropertyManager->Tie(property_name, &Z_airbox);
371  property_name = base_property_name + "/ram-air-factor";
372  PropertyManager->Tie(property_name, &Ram_Air_Factor);
373  property_name = base_property_name + "/cooling-factor";
374  PropertyManager->Tie(property_name, &Cooling_Factor);
375  property_name = base_property_name + "/boost-speed";
376  PropertyManager->Tie(property_name, &BoostSpeed);
377  property_name = base_property_name + "/cht-degF";
378  PropertyManager->Tie(property_name, this, &FGPiston::getCylinderHeadTemp_degF);
379  property_name = base_property_name + "/oil-temperature-degF";
380  PropertyManager->Tie(property_name, this, &FGPiston::getOilTemp_degF);
381  property_name = base_property_name + "/oil-pressure-psi";
382  PropertyManager->Tie(property_name, this, &FGPiston::getOilPressure_psi);
383  property_name = base_property_name + "/egt-degF";
384  PropertyManager->Tie(property_name, this, &FGPiston::getExhaustGasTemp_degF);
385  if(BoostLossFactor > 0.0) {
386  property_name = base_property_name + "/boostloss-factor";
387  PropertyManager->Tie(property_name, &BoostLossFactor);
388  property_name = base_property_name + "/boostloss-hp";
389  PropertyManager->Tie(property_name, &BoostLossHP);
390  }
391  property_name = base_property_name + "/AFR";
392  PropertyManager->Tie(property_name, this, &FGPiston::getAFR);
393 
394  // Set up and sanity-check the turbo/supercharging configuration based on the input values.
395  if (TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true;
396  for (i=0; i<BoostSpeeds; ++i) {
397  bool bad = false;
398  if (RatedBoost[i] <= 0.0) bad = true;
399  if (RatedPower[i] <= 0.0) bad = true;
400  if (RatedAltitude[i] < 0.0) bad = true; // 0.0 is deliberately allowed - this corresponds to unregulated supercharging.
401  if (i > 0 && RatedAltitude[i] < RatedAltitude[i - 1]) bad = true;
402  if (bad) {
403  // We can't recover from the above - don't use this supercharger speed.
404  BoostSpeeds--;
405  // TODO - put out a massive error message!
406  break;
407  }
408  // Now sanity-check stuff that is recoverable.
409  if (i < BoostSpeeds - 1) {
410  if (BoostSwitchAltitude[i] < RatedAltitude[i]) {
411  // TODO - put out an error message
412  // But we can also make a reasonable estimate, as below.
413  BoostSwitchAltitude[i] = RatedAltitude[i] + 1000;
414  }
415  BoostSwitchPressure[i] = GetStdPressure100K(BoostSwitchAltitude[i]) * psftopa;
416  //cout << "BoostSwitchAlt = " << BoostSwitchAltitude[i] << ", pressure = " << BoostSwitchPressure[i] << '\n';
417  // Assume there is some hysteresis on the supercharger gear switch, and guess the value for now
418  BoostSwitchHysteresis = 1000;
419  }
420  // Now work out the supercharger pressure multiplier of this speed from the rated boost and altitude.
421  RatedMAP[i] = standard_pressure + RatedBoost[i] * 6895; // psi*6895 = Pa.
422  // Sometimes a separate BCV setting for takeoff or extra power is fitted.
423  if (TakeoffBoost > RatedBoost[0]) {
424  // Assume that the effect on the BCV is the same whichever speed is in use.
425  TakeoffMAP[i] = RatedMAP[i] + ((TakeoffBoost - RatedBoost[0]) * 6895);
426  bTakeoffBoost = true;
427  } else {
428  TakeoffMAP[i] = RatedMAP[i];
429  bTakeoffBoost = false;
430  }
431  BoostMul[i] = RatedMAP[i] / (GetStdPressure100K(RatedAltitude[i]) * psftopa);
432 
433  }
434 
435  if (BoostSpeeds > 0) {
436  Boosted = true;
437  BoostSpeed = 0;
438  }
439  bBoostOverride = (BoostOverride == 1 ? true : false);
440  bBoostManual = (BoostManual == 1 ? true : false);
441  Debug(0); // Call Debug() routine from constructor if needed
442 }
443 
444 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
445 
447 {
448  delete Lookup_Combustion_Efficiency;
449  delete Mixture_Efficiency_Correlation;
450  Debug(1); // Call Debug() routine from constructor if needed
451 }
452 
453 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
454 
456 {
458 
459  ManifoldPressure_inHg = in.Pressure * psftoinhg; // psf to in Hg
460  MAP = in.Pressure * psftopa;
461  TMAP = MAP;
462  double airTemperature_degK = RankineToKelvin(in.Temperature);
463  OilTemp_degK = airTemperature_degK;
464  CylinderHeadTemp_degK = airTemperature_degK;
465  ExhaustGasTemp_degK = airTemperature_degK;
466  EGT_degC = ExhaustGasTemp_degK - 273;
467  Thruster->SetRPM(0.0);
468  RPM = 0.0;
469  OilPressure_psi = 0.0;
470  BoostLossHP = 0.;
471 }
472 
473 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
474 
476 {
477  // Input values.
478 
479  p_amb = in.Pressure * psftopa;
480  double p = in.TotalPressure * psftopa;
481  p_ram = (p - p_amb) * Ram_Air_Factor + p_amb;
482  T_amb = RankineToKelvin(in.Temperature);
483 
484  RunPreFunctions();
485 
486 /* The thruster controls the engine RPM because it encapsulates the gear ratio and other transmission variables */
487  RPM = Thruster->GetEngineRPM();
488 
489  MeanPistonSpeed_fps = ( RPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS
490 
491  IAS = in.Vc;
492 
493  doEngineStartup();
494  if (Boosted) doBoostControl();
495  doMAP();
496  doAirFlow();
497  doFuelFlow();
498 
499  //Now that the fuel flow is done check if the mixture is too lean to run the engine
500  //Assume lean limit at 22 AFR for now - thats a thi of 0.668
501  //This might be a bit generous, but since there's currently no audiable warning of impending
502  //cutout in the form of misfiring and/or rough running its probably reasonable for now.
503 
504  // if (equivalence_ratio < 0.668)
505  // Running = false;
506 
507  doEnginePower();
508  if (IndicatedHorsePower < 0.1250) Running = false;
509 
510  doEGT();
511  doCHT();
512  doOilTemperature();
513  doOilPressure();
514 
515  if (Thruster->GetType() == FGThruster::ttPropeller) {
516  ((FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
517  ((FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
518  }
519 
520  LoadThrusterInputs();
521  // Filters out negative powers when the propeller is not rotating.
522  double power = HP * hptoftlbssec;
523  if (RPM <= 0.1) power = max(power, 0.0);
524  Thruster->Calculate(power);
525 
526  RunPostFunctions();
527 }
528 
529 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
530 
532 {
533  FuelExpended = FuelFlowRate * in.TotalDeltaT;
534  if (!Starved) FuelUsedLbs += FuelExpended;
535  return FuelExpended;
536 }
537 
538 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
539 
540 int FGPiston::InitRunning(void)
541 {
542  Magnetos=3;
543  in.MixtureCmd[EngineNumber] = in.PressureRatio*1.3;
544  in.MixturePos[EngineNumber] = in.PressureRatio*1.3;
545  Thruster->SetRPM( 2.0*IdleRPM/Thruster->GetGearRatio() );
546  Running = true;
547  return 1;
548 }
549 
550 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
555 void FGPiston::doEngineStartup(void)
556 {
557  // Check parameters that may alter the operating state of the engine.
558  // (spark, fuel, starter motor etc)
559  bool spark;
560  bool fuel;
561  // Check for spark
562  Magneto_Left = false;
563  Magneto_Right = false;
564  // Magneto positions:
565  // 0 -> off
566  // 1 -> left only
567  // 2 -> right only
568  // 3 -> both
569  if (Magnetos != 0) {
570  spark = true;
571  } else {
572  spark = false;
573  } // neglects battery voltage, master on switch, etc for now.
574 
575  if ((Magnetos == 1) || (Magnetos > 2)) Magneto_Left = true;
576  if (Magnetos > 1) Magneto_Right = true;
577 
578 // We will 'run' with any fuel flow. If there is not enough fuel to make power it will show in doEnginePower
579  fuel = FuelFlowRate > 0.0 ? 1 : 0;
580 
581  // Check if we are turning the starter motor
582  if (Cranking != Starter) {
583  // This check saves .../cranking from getting updated every loop - they
584  // only update when changed.
585  Cranking = Starter;
586  }
587 
588 
589  // Cut the engine *power* - Note: the engine will continue to
590  // spin depending on prop Ixx and freestream velocity
591 
592  if ( Running ) {
593  if (!spark || !fuel) Running = false;
594  if (RPM < IdleRPM*0.8 ) Running = false;
595  } else { // !Running
596  if ( spark && fuel) { // start the engine if revs high enough
597  if (RPM > IdleRPM*0.8) // This allows us to in-air start
598  Running = true; // when windmilling
599  }
600  }
601 
602 }
603 
604 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
605 
617 void FGPiston::doBoostControl(void)
618 {
619  if(bBoostManual) {
620  if(BoostSpeed > BoostSpeeds-1) BoostSpeed = BoostSpeeds-1;
621  if(BoostSpeed < 0) BoostSpeed = 0;
622  } else {
623  if(BoostSpeed < BoostSpeeds - 1) {
624  // Check if we need to change to a higher boost speed
625  if(p_amb < BoostSwitchPressure[BoostSpeed] - BoostSwitchHysteresis) {
626  BoostSpeed++;
627  }
628  } if(BoostSpeed > 0) {
629  // Check if we need to change to a lower boost speed
630  if(p_amb > BoostSwitchPressure[BoostSpeed - 1] + BoostSwitchHysteresis) {
631  BoostSpeed--;
632  }
633  }
634  }
635 }
636 
637 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
638 
652 void FGPiston::doMAP(void)
653 {
654  double Zt = (1 - in.ThrottlePos[EngineNumber])*(1 - in.ThrottlePos[EngineNumber])*Z_throttle; // throttle impedence
655  double Ze= MeanPistonSpeed_fps > 0 ? PeakMeanPistonSpeed_fps/MeanPistonSpeed_fps : 999999; // engine impedence
656 
657  double map_coefficient = Ze/(Ze+Z_airbox+Zt);
658 
659  // Add a variable lag to manifold pressure changes
660  double dMAP=(TMAP - p_ram * map_coefficient);
661  if (ManifoldPressureLag > in.TotalDeltaT) dMAP *= in.TotalDeltaT/ManifoldPressureLag;
662 
663  TMAP -=dMAP;
664 
665  // Find the mean effective pressure required to achieve this manifold pressure
666  // Fixme: determine the HP consumed by the supercharger
667 
668  PMEP = (TMAP - p_amb) * volumetric_efficiency; // Fixme: p_amb should be exhaust manifold pressure
669 
670  if (Boosted) {
671  // If takeoff boost is fitted, we currently assume the following throttle map:
672  // (In throttle % - actual input is 0 -> 1)
673  // 99 / 100 - Takeoff boost
674  // In real life, most planes would be fitted with a mechanical 'gate' between
675  // the rated boost and takeoff boost positions.
676 
677  bool bTakeoffPos = false;
678  if (bTakeoffBoost) {
679  if (in.ThrottlePos[EngineNumber] > 0.98) {
680  bTakeoffPos = true;
681  }
682  }
683  // Boost the manifold pressure.
684  double boost_factor = (( BoostMul[BoostSpeed] - 1 ) / RatedRPM[BoostSpeed] ) * RPM + 1;
685  MAP = TMAP * boost_factor;
686  // Now clip the manifold pressure to BCV or Wastegate setting.
687  if(!bBoostOverride) {
688  if (bTakeoffPos) {
689  if (MAP > TakeoffMAP[BoostSpeed]) MAP = TakeoffMAP[BoostSpeed];
690  } else {
691  if (MAP > RatedMAP[BoostSpeed]) MAP = RatedMAP[BoostSpeed];
692  }
693  }
694  } else {
695  MAP = TMAP;
696  }
697 
698  if( BoostLossFactor > 0.0 )
699  {
700  double gamma = 1.414; // specific heat constants
701  double Nstage = 1; // Nstage is the number of boost stages.
702  BoostLossHP = ((Nstage * TMAP * v_dot_air * gamma) / (gamma - 1)) * (pow((MAP/TMAP),((gamma-1)/(Nstage * gamma))) - 1) * BoostLossFactor / 745.7 ; // 745.7 convert watt to hp
703  } else {
704  BoostLossHP = 0;
705  }
706 
707  // And set the value in American units as well
708  ManifoldPressure_inHg = MAP / inhgtopa;
709 }
710 
711 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 void FGPiston::doAirFlow(void)
726 {
727  double gamma = 1.3; // specific heat constants
728 // loss of volumentric efficiency due to difference between MAP and exhaust pressure
729 // Eq 6-10 from The Internal Combustion Engine - Charles Taylor Vol 1
730  double mratio = MAP < 1. ? CompressionRatio : p_amb/MAP;
731  if (mratio > CompressionRatio) mratio = CompressionRatio;
732  double ve =((gamma-1)/gamma) +( CompressionRatio -(mratio))/(gamma*( CompressionRatio - 1));
733 
734  rho_air = p_amb / (R_air * T_amb);
735  double swept_volume = (displacement_SI * (RPM/60)) / 2;
736  volumetric_efficiency_reduced = volumetric_efficiency *ve;
737  v_dot_air = swept_volume * volumetric_efficiency_reduced;
738 
739  double rho_air_manifold = MAP / (R_air * T_amb);
740  m_dot_air = v_dot_air * rho_air_manifold;
741 
742 }
743 
744 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753 void FGPiston::doFuelFlow(void)
754 {
755  double thi_sea_level = 1.3 * in.MixturePos[EngineNumber]; // Allows an AFR of infinity:1 to 11.3075:1
756  equivalence_ratio = thi_sea_level * 101325.0 / p_amb;
757  m_dot_fuel = (m_dot_air * equivalence_ratio) / 14.7;
758  FuelFlowRate = m_dot_fuel * 2.2046; // kg to lb
759  if(Starved) // There is no fuel, so zero out the flows we've calculated so far
760  {
761  equivalence_ratio = 0.0;
762  FuelFlowRate = 0.0;
763  m_dot_fuel = 0.0;
764  }
765  FuelFlow_pph = FuelFlowRate * 3600;
766  FuelFlow_gph = FuelFlow_pph / FuelDensity;
767 }
768 
769 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780 void FGPiston::doEnginePower(void)
781 {
782  IndicatedHorsePower = -StaticFriction_HP;
783  FMEP = 0;
784  if (Running) {
785  double ME, power; // Convienience term for use in the calculations
786  ME = Mixture_Efficiency_Correlation->GetValue(m_dot_fuel/m_dot_air);
787 
788 // Guestimate engine friction losses from Figure 4.4 of "Engines: An Introduction", John Lumley
789  FMEP = (-FMEPDynamic * MeanPistonSpeed_fps * fttom - FMEPStatic);
790 
791  power = 1;
792 
793  if ( Magnetos != 3 ) power *= SparkFailDrop;
794 
795 
796  IndicatedHorsePower = (FuelFlow_pph / ISFC )* ME * power - StaticFriction_HP; //FIXME static friction should depend on oil temp and configuration;
797 
798  } else {
799  // Power output when the engine is not running
800  double torque, k_torque, rpm; // Convienience term for use in the calculations
801 
802  rpm = RPM < 1.0 ? 1.0 : RPM;
803  if (Cranking) {
804  if(RPM<StarterRPM) k_torque = 1.0-RPM/(StarterRPM);
805  else k_torque = 0;
806  torque = StarterTorque*k_torque*StarterGain;
807  IndicatedHorsePower = torque * rpm / 5252;
808  }
809  }
810 
811  // Constant is (1/2) * 60 * 745.7
812  // (1/2) convert cycles, 60 minutes to seconds, 745.7 watts to hp.
813  double pumping_hp = ((PMEP + FMEP) * displacement_SI * RPM)/(Cycles*22371);
814 
815 HP = IndicatedHorsePower + pumping_hp - BoostLossHP;
816 // cout << "pumping_hp " <<pumping_hp << FMEP << PMEP <<endl;
817  PctPower = HP / MaxHP ;
818 // cout << "Power = " << HP << " RPM = " << RPM << " Running = " << Running << " Cranking = " << Cranking << endl;
819 }
820 
821 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831 void FGPiston::doEGT(void)
832 {
833  double delta_T_exhaust;
834  double enthalpy_exhaust;
835  double heat_capacity_exhaust;
836  double dEGTdt;
837 
838  if ((Running) && (m_dot_air > 0.0)) { // do the energy balance
839  combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio);
840  enthalpy_exhaust = m_dot_fuel * calorific_value_fuel *
841  combustion_efficiency * 0.30;
842  heat_capacity_exhaust = (Cp_air * m_dot_air) + (Cp_fuel * m_dot_fuel);
843  delta_T_exhaust = enthalpy_exhaust / heat_capacity_exhaust;
844  ExhaustGasTemp_degK = T_amb + delta_T_exhaust;
845  } else { // Drop towards ambient - guess an appropriate time constant for now
846  combustion_efficiency = 0;
847  dEGTdt = (RankineToKelvin(in.Temperature) - ExhaustGasTemp_degK) / 100.0;
848  delta_T_exhaust = dEGTdt * in.TotalDeltaT;
849 
850  ExhaustGasTemp_degK += delta_T_exhaust;
851  }
852 }
853 
854 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
864 void FGPiston::doCHT(void)
865 {
866  double h1 = -95.0;
867  double h2 = -3.95;
868  double h3 = -140.0; // -0.05 * 2800 (default maxrpm)
869 
870  double arbitary_area = Displacement/360.0;
871  double CpCylinderHead = 800.0;
872  double MassCylinderHead = CylinderHeadMass * Cylinders;
873 
874  double temperature_difference = CylinderHeadTemp_degK - T_amb;
875  double v_apparent = IAS * Cooling_Factor;
876  double v_dot_cooling_air = arbitary_area * v_apparent;
877  double m_dot_cooling_air = v_dot_cooling_air * rho_air;
878  double dqdt_from_combustion =
879  m_dot_fuel * calorific_value_fuel * combustion_efficiency * 0.33;
880  double dqdt_forced = (h2 * m_dot_cooling_air * temperature_difference) +
881  (h3 * RPM * temperature_difference / MaxRPM);
882  double dqdt_free = h1 * temperature_difference * arbitary_area;
883  double dqdt_cylinder_head = dqdt_from_combustion + dqdt_forced + dqdt_free;
884 
885  double HeatCapacityCylinderHead = CpCylinderHead * MassCylinderHead;
886 
887  CylinderHeadTemp_degK +=
888  (dqdt_cylinder_head / HeatCapacityCylinderHead) * in.TotalDeltaT;
889 
890 }
891 
892 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
901 void FGPiston::doOilTemperature(void)
902 {
903  double target_oil_temp; // Steady state oil temp at the current engine conditions
904  double time_constant; // The time constant for the differential equation
905  double efficiency = 0.667; // The aproximate oil cooling system efficiency // FIXME: may vary by engine
906 
907 // Target oil temp is interpolated between ambient temperature and Cylinder Head Tempurature
908 // target_oil_temp = ( T_amb * efficiency ) + (CylinderHeadTemp_degK *(1-efficiency)) ;
909  target_oil_temp = CylinderHeadTemp_degK + efficiency * (T_amb - CylinderHeadTemp_degK) ;
910 
911  if (OilPressure_psi > 5.0 ) {
912  time_constant = 5000 / OilPressure_psi; // Guess at a time constant for circulated oil.
913  // The higher the pressure the faster it reaches
914  // target temperature. Oil pressure should be about
915  // 60 PSI yielding a TC of about 80.
916  } else {
917  time_constant = 1000; // Time constant for engine-off; reflects the fact
918  // that oil is no longer getting circulated
919  }
920 
921  double dOilTempdt = (target_oil_temp - OilTemp_degK) / time_constant;
922 
923  OilTemp_degK += (dOilTempdt * in.TotalDeltaT);
924 }
925 
926 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
935 void FGPiston::doOilPressure(void)
936 {
937  OilPressure_psi = (Oil_Press_Relief_Valve / Oil_Press_RPM_Max) * RPM;
938 
939  if (OilPressure_psi >= Oil_Press_Relief_Valve) {
940  OilPressure_psi = Oil_Press_Relief_Valve;
941  }
942 
943  OilPressure_psi += (Design_Oil_Temp - OilTemp_degK) * Oil_Viscosity_Index * OilPressure_psi / Oil_Press_Relief_Valve;
944 }
945 
946 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
947 //
948 // This is a local copy of the same function in FGStandardAtmosphere.
949 
950 double FGPiston::GetStdPressure100K(double altitude) const
951 {
952  // Limit this equation to input altitudes of 100000 ft.
953  if (altitude > 100000.0) altitude = 100000.0;
954 
955  double alt[5];
956  const double coef[5] = { 2116.217,
957  -7.648932746E-2,
958  1.0925498604E-6,
959  -7.1135726027E-12,
960  1.7470331356E-17 };
961 
962  alt[0] = 1;
963  for (int pwr=1; pwr<=4; pwr++) alt[pwr] = alt[pwr-1]*altitude;
964 
965  double press = 0.0;
966  for (int ctr=0; ctr<=4; ctr++) press += coef[ctr]*alt[ctr];
967  return press;
968 }
969 
970 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
971 
972 string FGPiston::GetEngineLabels(const string& delimiter)
973 {
974  std::ostringstream buf;
975 
976  buf << Name << " Power Available (engine " << EngineNumber << " in ft-lbs/sec)" << delimiter
977  << Name << " HP (engine " << EngineNumber << ")" << delimiter
978  << Name << " equivalent ratio (engine " << EngineNumber << ")" << delimiter
979  << Name << " MAP (engine " << EngineNumber << " in inHg)" << delimiter
980  << Thruster->GetThrusterLabels(EngineNumber, delimiter);
981 
982  return buf.str();
983 }
984 
985 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
986 
987 string FGPiston::GetEngineValues(const string& delimiter)
988 {
989  std::ostringstream buf;
990 
991  buf << (HP * hptoftlbssec) << delimiter << HP << delimiter
992  << equivalence_ratio << delimiter << ManifoldPressure_inHg << delimiter
993  << Thruster->GetThrusterValues(EngineNumber, delimiter);
994 
995  return buf.str();
996 }
997 
998 //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
999 //
1000 // The bitmasked value choices are as follows:
1001 // unset: In this case (the default) JSBSim would only print
1002 // out the normally expected messages, essentially echoing
1003 // the config files as they are read. If the environment
1004 // variable is not set, debug_lvl is set to 1 internally
1005 // 0: This requests JSBSim not to output any messages
1006 // whatsoever.
1007 // 1: This value explicity requests the normal JSBSim
1008 // startup messages
1009 // 2: This value asks for a message to be printed out when
1010 // a class is instantiated
1011 // 4: When this value is set, a message is displayed when a
1012 // FGModel object executes its Run() method
1013 // 8: When this value is set, various runtime state variables
1014 // are printed out periodically
1015 // 16: When set various parameters are sanity checked and
1016 // a message is printed out when they go out of bounds
1017 
1018 void FGPiston::Debug(int from)
1019 {
1020  if (debug_lvl <= 0) return;
1021 
1022  if (debug_lvl & 1) { // Standard console startup message output
1023  if (from == 0) { // Constructor
1024 
1025  cout << "\n Engine Name: " << Name << endl;
1026  cout << " MinManifoldPressure: " << MinManifoldPressure_inHg << endl;
1027  cout << " MaxManifoldPressure: " << MaxManifoldPressure_inHg << endl;
1028  cout << " MinMaP (Pa): " << minMAP << endl;
1029  cout << " MaxMaP (Pa): " << maxMAP << endl;
1030  cout << " Displacement: " << Displacement << endl;
1031  cout << " Bore: " << Bore << endl;
1032  cout << " Stroke: " << Stroke << endl;
1033  cout << " Cylinders: " << Cylinders << endl;
1034  cout << " Cylinders Head Mass: " << CylinderHeadMass << endl;
1035  cout << " Compression Ratio: " << CompressionRatio << endl;
1036  cout << " MaxHP: " << MaxHP << endl;
1037  cout << " Cycles: " << Cycles << endl;
1038  cout << " IdleRPM: " << IdleRPM << endl;
1039  cout << " MaxRPM: " << MaxRPM << endl;
1040  cout << " Throttle Constant: " << Z_throttle << endl;
1041  cout << " ISFC: " << ISFC << endl;
1042  cout << " Volumetric Efficiency: " << volumetric_efficiency << endl;
1043  cout << " PeakMeanPistonSpeed_fps: " << PeakMeanPistonSpeed_fps << endl;
1044  cout << " Intake Impedance Factor: " << Z_airbox << endl;
1045  cout << " Dynamic FMEP Factor: " << FMEPDynamic << endl;
1046  cout << " Static FMEP Factor: " << FMEPStatic << endl;
1047 
1048  cout << " Starter Motor Torque: " << StarterTorque << endl;
1049  cout << " Starter Motor RPM: " << StarterRPM << endl;
1050 
1051  cout << endl;
1052  cout << " Combustion Efficiency table:" << endl;
1053  Lookup_Combustion_Efficiency->Print();
1054  cout << endl;
1055 
1056  cout << endl;
1057  cout << " Mixture Efficiency Correlation table:" << endl;
1058  Mixture_Efficiency_Correlation->Print();
1059  cout << endl;
1060 
1061  }
1062  }
1063  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
1064  if (from == 0) cout << "Instantiated: FGPiston" << endl;
1065  if (from == 1) cout << "Destroyed: FGPiston" << endl;
1066  }
1067  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
1068  }
1069  if (debug_lvl & 8 ) { // Runtime state variables
1070  }
1071  if (debug_lvl & 16) { // Sanity checking
1072  }
1073  if (debug_lvl & 64) {
1074  if (from == 0) { // Constructor
1075  }
1076  }
1077 }
1078 } // namespace JSBSim
Element * FindElement(const std::string &el="")
Searches for a specified element.
std::string GetAttributeValue(const std::string &key)
Retrieves an attribute.
double FindElementValueAsNumberConvertTo(const std::string &el, const std::string &target_units)
Searches for the named element and converts and returns the data belonging to it.
Element * FindNextElement(const std::string &el="")
Searches for the next element as specified.
double FindElementValueAsNumber(const std::string &el="")
Searches for the named element and returns the data belonging to it as a number.
Base class for all engines.
Definition: FGEngine.h:104
virtual void ResetToIC(void)
Resets the Engine parameters to the initial conditions.
Definition: FGEngine.cpp:77
Encapsulates the JSBSim simulation executive.
Definition: FGFDMExec.h:184
std::shared_ptr< FGPropertyManager > GetPropertyManager(void) const
Returns a pointer to the property manager object.
Definition: FGFDMExec.h:421
static constexpr double Constrain(double min, double value, double max)
Constrain a value between a minimum and a maximum value.
Definition: FGJSBBase.h:289
static constexpr double RankineToKelvin(double rankine)
Converts from degrees Rankine to degrees Kelvin.
Definition: FGJSBBase.h:214
void Calculate(void)
Calculates the thrust of the engine, and other engine functions.
Definition: FGPiston.cpp:475
~FGPiston()
Destructor.
Definition: FGPiston.cpp:446
void ResetToIC(void)
Resets the Engine parameters to the initial conditions.
Definition: FGPiston.cpp:455
double CalcFuelNeed(void)
The fuel need is calculated based on power levels and flow rate for that power level.
Definition: FGPiston.cpp:531
FGPropeller models a propeller given the tabular data for Ct (thrust) and Cp (power),...
Definition: FGPropeller.h:169
Lookup table class.
Definition: FGTable.h:234
double GetValue(void) const
Get the current table value.
Definition: FGTable.cpp:464