46 #include "FGFDMExec.h"
48 #include "FGPropeller.h"
49 #include "input_output/FGXMLElement.h"
62 calorific_value_fuel(47.3e6),
65 standard_pressure(101320.73)
85 MinManifoldPressure_inHg = 6.5;
86 MaxManifoldPressure_inHg = 28.5;
87 ManifoldPressureLag=1.0;
89 volumetric_efficiency = 0.85;
94 CompressionRatio = 8.5;
97 PeakMeanPistonSpeed_fps = 100;
100 Cooling_Factor = 0.5144444;
101 StaticFriction_HP = 1.5;
108 Lookup_Combustion_Efficiency = 0;
109 Mixture_Efficiency_Correlation = 0;
123 bBoostOverride =
false;
124 bTakeoffBoost =
false;
126 BoostLossFactor = 0.0;
129 for (i=0; i<FG_MAX_BOOST_SPEEDS; i++) {
132 RatedAltitude[i] = 0.0;
134 RatedMAP[i] = 100000;
136 TakeoffMAP[i] = 100000;
138 for (i=0; i<FG_MAX_BOOST_SPEEDS-1; i++) {
139 BoostSwitchAltitude[i] = 0.0;
140 BoostSwitchPressure[i] = 0.0;
183 if (el->
FindElement(
"air-intake-impedance-factor"))
235 Design_Oil_Temp = 358;
236 Oil_Viscosity_Index = 0.25;
237 Oil_Press_Relief_Valve = 60;
238 Oil_Press_RPM_Max = MaxRPM*0.75;
239 if (el->
FindElement(
"oil-pressure-relief-valve-psi"))
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);
256 cerr <<
"Unknown table type: " << name <<
" in piston engine definition." << endl;
258 }
catch (std::string& str) {
261 delete Lookup_Combustion_Efficiency;
262 delete Mixture_Efficiency_Correlation;
263 throw(
"Error loading piston engine table:" + name +
". " + str);
268 volumetric_efficiency_reduced = volumetric_efficiency;
270 if(StarterRPM < 0.) StarterRPM = 2*IdleRPM;
271 if(StarterTorque < 0)
272 StarterTorque = (MaxHP)*0.4;
274 displacement_SI = Displacement * in3tom3;
275 RatedMeanPistonSpeed_fps = ( MaxRPM * Stroke) / (360);
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));
286 if ( MaxManifoldPressure_inHg > 29.9 ) {
287 MaxManifoldPressure_inHg = 29.9;
289 minMAP = MinManifoldPressure_inHg * inhgtopa;
290 maxMAP = MaxManifoldPressure_inHg * inhgtopa;
308 double Ze=PeakMeanPistonSpeed_fps/RatedMeanPistonSpeed_fps;
309 Z_airbox = (standard_pressure *Ze / maxMAP) - Ze;
312 Z_throttle=(PeakMeanPistonSpeed_fps/((IdleRPM * Stroke) / 360))*(standard_pressure/minMAP - 1) - Z_airbox;
316 if(Lookup_Combustion_Efficiency == 0) {
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;
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;
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);
391 property_name = base_property_name +
"/AFR";
392 PropertyManager->
Tie(property_name,
this, &FGPiston::getAFR);
395 if (TakeoffBoost > RatedBoost[0]) bTakeoffBoost =
true;
396 for (i=0; i<BoostSpeeds; ++i) {
398 if (RatedBoost[i] <= 0.0) bad =
true;
399 if (RatedPower[i] <= 0.0) bad =
true;
400 if (RatedAltitude[i] < 0.0) bad =
true;
401 if (i > 0 && RatedAltitude[i] < RatedAltitude[i - 1]) bad =
true;
409 if (i < BoostSpeeds - 1) {
410 if (BoostSwitchAltitude[i] < RatedAltitude[i]) {
413 BoostSwitchAltitude[i] = RatedAltitude[i] + 1000;
415 BoostSwitchPressure[i] = GetStdPressure100K(BoostSwitchAltitude[i]) * psftopa;
418 BoostSwitchHysteresis = 1000;
421 RatedMAP[i] = standard_pressure + RatedBoost[i] * 6895;
423 if (TakeoffBoost > RatedBoost[0]) {
425 TakeoffMAP[i] = RatedMAP[i] + ((TakeoffBoost - RatedBoost[0]) * 6895);
426 bTakeoffBoost =
true;
428 TakeoffMAP[i] = RatedMAP[i];
429 bTakeoffBoost =
false;
431 BoostMul[i] = RatedMAP[i] / (GetStdPressure100K(RatedAltitude[i]) * psftopa);
435 if (BoostSpeeds > 0) {
439 bBoostOverride = (BoostOverride == 1 ? true :
false);
440 bBoostManual = (BoostManual == 1 ? true :
false);
448 delete Lookup_Combustion_Efficiency;
449 delete Mixture_Efficiency_Correlation;
459 ManifoldPressure_inHg = in.Pressure * psftoinhg;
460 MAP = in.Pressure * psftopa;
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);
469 OilPressure_psi = 0.0;
479 p_amb = in.Pressure * psftopa;
480 double p = in.TotalPressure * psftopa;
481 p_ram = (p - p_amb) * Ram_Air_Factor + p_amb;
487 RPM = Thruster->GetEngineRPM();
489 MeanPistonSpeed_fps = ( RPM * Stroke) / (360);
494 if (Boosted) doBoostControl();
508 if (IndicatedHorsePower < 0.1250) Running =
false;
515 if (Thruster->GetType() == FGThruster::ttPropeller) {
516 ((
FGPropeller*)Thruster)->SetAdvance(in.PropAdvance[EngineNumber]);
517 ((
FGPropeller*)Thruster)->SetFeather(in.PropFeather[EngineNumber]);
520 LoadThrusterInputs();
522 double power = HP * hptoftlbssec;
523 if (RPM <= 0.1) power = max(power, 0.0);
524 Thruster->Calculate(power);
533 FuelExpended = FuelFlowRate * in.TotalDeltaT;
534 if (!Starved) FuelUsedLbs += FuelExpended;
540 int FGPiston::InitRunning(
void)
543 in.MixtureCmd[EngineNumber] = in.PressureRatio*1.3;
544 in.MixturePos[EngineNumber] = in.PressureRatio*1.3;
545 Thruster->SetRPM( 2.0*IdleRPM/Thruster->GetGearRatio() );
555 void FGPiston::doEngineStartup(
void)
562 Magneto_Left =
false;
563 Magneto_Right =
false;
575 if ((Magnetos == 1) || (Magnetos > 2)) Magneto_Left =
true;
576 if (Magnetos > 1) Magneto_Right =
true;
579 fuel = FuelFlowRate > 0.0 ? 1 : 0;
582 if (Cranking != Starter) {
593 if (!spark || !fuel) Running =
false;
594 if (RPM < IdleRPM*0.8 ) Running =
false;
596 if ( spark && fuel) {
597 if (RPM > IdleRPM*0.8)
617 void FGPiston::doBoostControl(
void)
620 if(BoostSpeed > BoostSpeeds-1) BoostSpeed = BoostSpeeds-1;
621 if(BoostSpeed < 0) BoostSpeed = 0;
623 if(BoostSpeed < BoostSpeeds - 1) {
625 if(p_amb < BoostSwitchPressure[BoostSpeed] - BoostSwitchHysteresis) {
628 }
if(BoostSpeed > 0) {
630 if(p_amb > BoostSwitchPressure[BoostSpeed - 1] + BoostSwitchHysteresis) {
652 void FGPiston::doMAP(
void)
654 double Zt = (1 - in.ThrottlePos[EngineNumber])*(1 - in.ThrottlePos[EngineNumber])*Z_throttle;
655 double Ze= MeanPistonSpeed_fps > 0 ? PeakMeanPistonSpeed_fps/MeanPistonSpeed_fps : 999999;
657 double map_coefficient = Ze/(Ze+Z_airbox+Zt);
660 double dMAP=(TMAP - p_ram * map_coefficient);
661 if (ManifoldPressureLag > in.TotalDeltaT) dMAP *= in.TotalDeltaT/ManifoldPressureLag;
668 PMEP = (TMAP - p_amb) * volumetric_efficiency;
677 bool bTakeoffPos =
false;
679 if (in.ThrottlePos[EngineNumber] > 0.98) {
684 double boost_factor = (( BoostMul[BoostSpeed] - 1 ) / RatedRPM[BoostSpeed] ) * RPM + 1;
685 MAP = TMAP * boost_factor;
687 if(!bBoostOverride) {
689 if (MAP > TakeoffMAP[BoostSpeed]) MAP = TakeoffMAP[BoostSpeed];
691 if (MAP > RatedMAP[BoostSpeed]) MAP = RatedMAP[BoostSpeed];
698 if( BoostLossFactor > 0.0 )
700 double gamma = 1.414;
702 BoostLossHP = ((Nstage * TMAP * v_dot_air * gamma) / (gamma - 1)) * (pow((MAP/TMAP),((gamma-1)/(Nstage * gamma))) - 1) * BoostLossFactor / 745.7 ;
708 ManifoldPressure_inHg = MAP / inhgtopa;
725 void FGPiston::doAirFlow(
void)
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));
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;
739 double rho_air_manifold = MAP / (R_air * T_amb);
740 m_dot_air = v_dot_air * rho_air_manifold;
753 void FGPiston::doFuelFlow(
void)
755 double thi_sea_level = 1.3 * in.MixturePos[EngineNumber];
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;
761 equivalence_ratio = 0.0;
765 FuelFlow_pph = FuelFlowRate * 3600;
766 FuelFlow_gph = FuelFlow_pph / FuelDensity;
780 void FGPiston::doEnginePower(
void)
782 IndicatedHorsePower = -StaticFriction_HP;
786 ME = Mixture_Efficiency_Correlation->GetValue(m_dot_fuel/m_dot_air);
789 FMEP = (-FMEPDynamic * MeanPistonSpeed_fps * fttom - FMEPStatic);
793 if ( Magnetos != 3 ) power *= SparkFailDrop;
796 IndicatedHorsePower = (FuelFlow_pph / ISFC )* ME * power - StaticFriction_HP;
800 double torque, k_torque, rpm;
802 rpm = RPM < 1.0 ? 1.0 : RPM;
804 if(RPM<StarterRPM) k_torque = 1.0-RPM/(StarterRPM);
806 torque = StarterTorque*k_torque*StarterGain;
807 IndicatedHorsePower = torque * rpm / 5252;
813 double pumping_hp = ((PMEP + FMEP) * displacement_SI * RPM)/(Cycles*22371);
815 HP = IndicatedHorsePower + pumping_hp - BoostLossHP;
817 PctPower = HP / MaxHP ;
831 void FGPiston::doEGT(
void)
833 double delta_T_exhaust;
834 double enthalpy_exhaust;
835 double heat_capacity_exhaust;
838 if ((Running) && (m_dot_air > 0.0)) {
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;
846 combustion_efficiency = 0;
847 dEGTdt = (
RankineToKelvin(in.Temperature) - ExhaustGasTemp_degK) / 100.0;
848 delta_T_exhaust = dEGTdt * in.TotalDeltaT;
850 ExhaustGasTemp_degK += delta_T_exhaust;
864 void FGPiston::doCHT(
void)
870 double arbitary_area = Displacement/360.0;
871 double CpCylinderHead = 800.0;
872 double MassCylinderHead = CylinderHeadMass * Cylinders;
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;
885 double HeatCapacityCylinderHead = CpCylinderHead * MassCylinderHead;
887 CylinderHeadTemp_degK +=
888 (dqdt_cylinder_head / HeatCapacityCylinderHead) * in.TotalDeltaT;
901 void FGPiston::doOilTemperature(
void)
903 double target_oil_temp;
904 double time_constant;
905 double efficiency = 0.667;
909 target_oil_temp = CylinderHeadTemp_degK + efficiency * (T_amb - CylinderHeadTemp_degK) ;
911 if (OilPressure_psi > 5.0 ) {
912 time_constant = 5000 / OilPressure_psi;
917 time_constant = 1000;
921 double dOilTempdt = (target_oil_temp - OilTemp_degK) / time_constant;
923 OilTemp_degK += (dOilTempdt * in.TotalDeltaT);
935 void FGPiston::doOilPressure(
void)
937 OilPressure_psi = (Oil_Press_Relief_Valve / Oil_Press_RPM_Max) * RPM;
939 if (OilPressure_psi >= Oil_Press_Relief_Valve) {
940 OilPressure_psi = Oil_Press_Relief_Valve;
943 OilPressure_psi += (Design_Oil_Temp - OilTemp_degK) * Oil_Viscosity_Index * OilPressure_psi / Oil_Press_Relief_Valve;
950 double FGPiston::GetStdPressure100K(
double altitude)
const
953 if (altitude > 100000.0) altitude = 100000.0;
956 const double coef[5] = { 2116.217,
963 for (
int pwr=1; pwr<=4; pwr++) alt[pwr] = alt[pwr-1]*altitude;
966 for (
int ctr=0; ctr<=4; ctr++) press += coef[ctr]*alt[ctr];
972 string FGPiston::GetEngineLabels(
const string& delimiter)
974 std::ostringstream buf;
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);
987 string FGPiston::GetEngineValues(
const string& delimiter)
989 std::ostringstream buf;
991 buf << (HP * hptoftlbssec) << delimiter << HP << delimiter
992 << equivalence_ratio << delimiter << ManifoldPressure_inHg << delimiter
993 << Thruster->GetThrusterValues(EngineNumber, delimiter);
1018 void FGPiston::Debug(
int from)
1020 if (debug_lvl <= 0)
return;
1022 if (debug_lvl & 1) {
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;
1048 cout <<
" Starter Motor Torque: " << StarterTorque << endl;
1049 cout <<
" Starter Motor RPM: " << StarterRPM << endl;
1052 cout <<
" Combustion Efficiency table:" << endl;
1053 Lookup_Combustion_Efficiency->Print();
1057 cout <<
" Mixture Efficiency Correlation table:" << endl;
1058 Mixture_Efficiency_Correlation->Print();
1063 if (debug_lvl & 2 ) {
1064 if (from == 0) cout <<
"Instantiated: FGPiston" << endl;
1065 if (from == 1) cout <<
"Destroyed: FGPiston" << endl;
1067 if (debug_lvl & 4 ) {
1069 if (debug_lvl & 8 ) {
1071 if (debug_lvl & 16) {
1073 if (debug_lvl & 64) {