/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Module: FGPiston.cpp Author: Jon S. Berndt, JSBSim framework Dave Luff, Piston engine model Date started: 09/12/2000 Purpose: This module models a Piston engine ------------- 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 -------------------------------------------------------------------------------- This class descends from the FGEngine class and models a Piston engine based on parameters given in the engine config file for this class HISTORY -------------------------------------------------------------------------------- 09/12/2000 JSB Created %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include #include "FGPiston.h" #include "FGPropulsion.h" #include "FGPropeller.h" namespace JSBSim { static const char *IdSrc = "$Id$"; static const char *IdHdr = ID_PISTON; /*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ FGPiston::FGPiston(FGFDMExec* exec, FGConfigFile* Eng_cfg, int engine_number) : FGEngine(exec, engine_number), R_air(287.3), rho_fuel(800), // estimate calorific_value_fuel(47.3e6), Cp_air(1005), Cp_fuel(1700) { string token; Type = etPiston; crank_counter = 0; OilTemp_degK = 298; MinManifoldPressure_inHg = 6.5; MaxManifoldPressure_inHg = 28.5; ManifoldPressure_inHg = Atmosphere->GetPressure() * psftoinhg; // psf to in Hg minMAP = 21950; maxMAP = 96250; MAP = Atmosphere->GetPressure() * 47.88; // psf to Pa CylinderHeadTemp_degK = 0.0; Displacement = 360; MaxHP = 200; Cycles = 2; IdleRPM = 600; Magnetos = 0; ExhaustGasTemp_degK = 0.0; EGT_degC = 0.0; dt = State->Getdt(); // Supercharging BoostSpeeds = 0; // Default to no supercharging BoostSpeed = 0; Boosted = false; BoostOverride = 0; bBoostOverride = false; bTakeoffBoost = false; TakeoffBoost = 0.0; // Default to no extra takeoff-boost int i; for (i=0; iGetValue("NAME"); Eng_cfg->GetNextConfigLine(); while (Eng_cfg->GetValue() != string("/FG_PISTON")) { *Eng_cfg >> token; if (token == "MINMP") *Eng_cfg >> MinManifoldPressure_inHg; else if (token == "MAXMP") *Eng_cfg >> MaxManifoldPressure_inHg; else if (token == "DISPLACEMENT") *Eng_cfg >> Displacement; else if (token == "MAXHP") *Eng_cfg >> MaxHP; else if (token == "CYCLES") *Eng_cfg >> Cycles; else if (token == "IDLERPM") *Eng_cfg >> IdleRPM; else if (token == "MAXTHROTTLE") *Eng_cfg >> MaxThrottle; else if (token == "MINTHROTTLE") *Eng_cfg >> MinThrottle; else if (token == "NUMBOOSTSPEEDS") *Eng_cfg >> BoostSpeeds; else if (token == "BOOSTOVERRIDE") *Eng_cfg >> BoostOverride; else if (token == "TAKEOFFBOOST") *Eng_cfg >> TakeoffBoost; else if (token == "RATEDBOOST1") *Eng_cfg >> RatedBoost[0]; else if (token == "RATEDBOOST2") *Eng_cfg >> RatedBoost[1]; else if (token == "RATEDBOOST3") *Eng_cfg >> RatedBoost[2]; else if (token == "RATEDPOWER1") *Eng_cfg >> RatedPower[0]; else if (token == "RATEDPOWER2") *Eng_cfg >> RatedPower[1]; else if (token == "RATEDPOWER3") *Eng_cfg >> RatedPower[2]; else if (token == "RATEDRPM1") *Eng_cfg >> RatedRPM[0]; else if (token == "RATEDRPM2") *Eng_cfg >> RatedRPM[1]; else if (token == "RATEDRPM3") *Eng_cfg >> RatedRPM[2]; else if (token == "RATEDALTITUDE1") *Eng_cfg >> RatedAltitude[0]; else if (token == "RATEDALTITUDE2") *Eng_cfg >> RatedAltitude[1]; else if (token == "RATEDALTITUDE3") *Eng_cfg >> RatedAltitude[2]; else cerr << "Unhandled token in Engine config file: " << token << endl; } minMAP = MinManifoldPressure_inHg * 3376.85; // inHg to Pa maxMAP = MaxManifoldPressure_inHg * 3376.85; // Set up and sanity-check the turbo/supercharging configuration based on the input values. if(TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true; for(i=0; i 0 && RatedAltitude[i] < RatedAltitude[i - 1]) bad = true; if(bad) { // We can't recover from the above - don't use this supercharger speed. BoostSpeeds--; // TODO - put out a massive error message! break; } // Now sanity-check stuff that is recoverable. if(i < BoostSpeeds - 1) { if(BoostSwitchAltitude[i] < RatedAltitude[i]) { // TODO - put out an error message // But we can also make a reasonable estimate, as below. BoostSwitchAltitude[i] = RatedAltitude[i] + 1000; } BoostSwitchPressure[i] = Atmosphere->GetPressure(BoostSwitchAltitude[i]) * 47.88; //cout << "BoostSwitchAlt = " << BoostSwitchAltitude[i] << ", pressure = " << BoostSwitchPressure[i] << '\n'; // Assume there is some hysteresis on the supercharger gear switch, and guess the value for now BoostSwitchHysteresis = 1000; } // Now work out the supercharger pressure multiplier of this speed from the rated boost and altitude. RatedMAP[i] = Atmosphere->GetPressureSL() * 47.88 + RatedBoost[i] * 6895; // psf*47.88 = Pa, psi*6895 = Pa. // Sometimes a separate BCV setting for takeoff or extra power is fitted. if(TakeoffBoost > RatedBoost[0]) { // Assume that the effect on the BCV is the same whichever speed is in use. TakeoffMAP[i] = RatedMAP[i] + ((TakeoffBoost - RatedBoost[0]) * 6895); bTakeoffBoost = true; } else { TakeoffMAP[i] = RatedMAP[i]; bTakeoffBoost = false; } BoostMul[i] = RatedMAP[i] / (Atmosphere->GetPressure(RatedAltitude[i]) * 47.88); // TODO - get rid of the debugging output before sending it to Jon //cout << "Speed " << i+1 << '\n'; //cout << "BoostMul = " << BoostMul[i] << ", RatedMAP = " << RatedMAP[i] << ", TakeoffMAP = " << TakeoffMAP[i] << '\n'; } if(BoostSpeeds > 0) { Boosted = true; BoostSpeed = 0; } bBoostOverride = (BoostOverride == 1 ? true : false); //cout << "Engine is " << (Boosted ? "supercharged" : "naturally aspirated") << '\n'; Debug(0); // Call Debug() routine from constructor if needed } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% FGPiston::~FGPiston() { Debug(1); // Call Debug() routine from constructor if needed } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% double FGPiston::Calculate(void) { if (FuelFlow_gph > 0.0) ConsumeFuel(); Throttle = FCS->GetThrottlePos(EngineNumber); Mixture = FCS->GetMixturePos(EngineNumber); // // Input values. // p_amb = Atmosphere->GetPressure() * 47.88; // convert from lbs/ft2 to Pa p_amb_sea_level = Atmosphere->GetPressureSL() * 47.88; T_amb = Atmosphere->GetTemperature() * (5.0 / 9.0); // convert from Rankine to Kelvin RPM = Thruster->GetRPM() * Thruster->GetGearRatio(); IAS = Auxiliary->GetVcalibratedKTS(); doEngineStartup(); if(Boosted) doBoostControl(); doMAP(); doAirFlow(); doFuelFlow(); //Now that the fuel flow is done check if the mixture is too lean to run the engine //Assume lean limit at 22 AFR for now - thats a thi of 0.668 //This might be a bit generous, but since there's currently no audiable warning of impending //cutout in the form of misfiring and/or rough running its probably reasonable for now. if (equivalence_ratio < 0.668) Running = false; doEnginePower(); doEGT(); doCHT(); doOilTemperature(); doOilPressure(); if (Thruster->GetType() == FGThruster::ttPropeller) { ((FGPropeller*)Thruster)->SetAdvance(FCS->GetPropAdvance(EngineNumber)); } PowerAvailable = (HP * hptoftlbssec) - Thruster->GetPowerRequired(); return Thrust = Thruster->Calculate(PowerAvailable); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Start or stop the engine. */ void FGPiston::doEngineStartup(void) { // Check parameters that may alter the operating state of the engine. // (spark, fuel, starter motor etc) bool spark; bool fuel; // Check for spark Magneto_Left = false; Magneto_Right = false; // Magneto positions: // 0 -> off // 1 -> left only // 2 -> right only // 3 -> both if (Magnetos != 0) { spark = true; } else { spark = false; } // neglects battery voltage, master on switch, etc for now. if ((Magnetos == 1) || (Magnetos > 2)) Magneto_Left = true; if (Magnetos > 1) Magneto_Right = true; // Assume we have fuel for now fuel = !Starved; // Check if we are turning the starter motor if (Cranking != Starter) { // This check saves .../cranking from getting updated every loop - they // only update when changed. Cranking = Starter; crank_counter = 0; } if (Cranking) crank_counter++; //Check mode of engine operation if (!Running && spark && fuel) { // start the engine if revs high enough if (Cranking) { if ((RPM > 450) && (crank_counter > 175)) // Add a little delay to startup Running = true; // on the starter } else { if (RPM > 450) // This allows us to in-air start Running = true; // when windmilling } } // Cut the engine *power* - Note: the engine may continue to // spin if the prop is in a moving airstream if ( Running && (!spark || !fuel) ) Running = false; // Check for stalling (RPM = 0). if (Running) { if (RPM == 0) { Running = false; } else if ((RPM <= 480) && (Cranking)) { Running = false; } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the Current Boost Speed * * This function calculates the current turbo/supercharger boost speed * based on altitude and the (automatic) boost-speed control valve configuration. * * Inputs: p_amb, BoostSwitchPressure, BoostSwitchHysteresis * * Outputs: BoostSpeed */ void FGPiston::doBoostControl(void) { if(BoostSpeed < BoostSpeeds - 1) { // Check if we need to change to a higher boost speed if(p_amb < BoostSwitchPressure[BoostSpeed] - BoostSwitchHysteresis) { BoostSpeed++; } } else if(BoostSpeed > 0) { // Check if we need to change to a lower boost speed if(p_amb > BoostSwitchPressure[BoostSpeed - 1] + BoostSwitchHysteresis) { BoostSpeed--; } } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the manifold absolute pressure (MAP) in inches hg * * This function calculates manifold absolute pressure (MAP) * from the throttle position, turbo/supercharger boost control * system, engine speed and local ambient air density. * * TODO: changes in MP should not be instantaneous -- introduce * a lag between throttle changes and MP changes, to allow pressure * to build up or disperse. * * Inputs: minMAP, maxMAP, p_amb, Throttle * * Outputs: MAP, ManifoldPressure_inHg */ void FGPiston::doMAP(void) { if(RPM > 10) { // Naturally aspirated MAP = minMAP + (Throttle * (maxMAP - minMAP)); MAP *= p_amb / p_amb_sea_level; if(Boosted) { // If takeoff boost is fitted, we currently assume the following throttle map: // (In throttle % - actual input is 0 -> 1) // 99 / 100 - Takeoff boost // 96 / 97 / 98 - Rated boost // 0 - 95 - Idle to Rated boost (MinManifoldPressure to MaxManifoldPressure) // In real life, most planes would be fitted with a mechanical 'gate' between // the rated boost and takeoff boost positions. double T = Throttle; // processed throttle value. bool bTakeoffPos = false; if(bTakeoffBoost) { if(Throttle > 0.98) { //cout << "Takeoff Boost!!!!\n"; bTakeoffPos = true; } else if(Throttle <= 0.95) { bTakeoffPos = false; T *= 1.0 / 0.95; } else { bTakeoffPos = false; //cout << "Rated Boost!!\n"; T = 1.0; } } // Boost the manifold pressure. MAP *= BoostMul[BoostSpeed]; // Now clip the manifold pressure to BCV or Wastegate setting. if(bTakeoffPos) { if(MAP > TakeoffMAP[BoostSpeed]) { MAP = TakeoffMAP[BoostSpeed]; } } else { if(MAP > RatedMAP[BoostSpeed]) { MAP = RatedMAP[BoostSpeed]; } } } } else { // rpm < 10 - effectively stopped. // TODO - add a better variation of MAP with engine speed MAP = Atmosphere->GetPressure() * 47.88; // psf to Pa } // And set the value in American units as well ManifoldPressure_inHg = MAP / 3376.85; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the air flow through the engine. * Also calculates ambient air density * (used in CHT calculation for air-cooled engines). * * Inputs: p_amb, R_air, T_amb, MAP, Displacement, * RPM, volumetric_efficiency * * TODO: Model inlet manifold air temperature. * * Outputs: rho_air, m_dot_air */ void FGPiston::doAirFlow(void) { rho_air = p_amb / (R_air * T_amb); double rho_air_manifold = MAP / (R_air * T_amb); double displacement_SI = Displacement * in3tom3; double swept_volume = (displacement_SI * (RPM/60)) / 2; double v_dot_air = swept_volume * volumetric_efficiency; m_dot_air = v_dot_air * rho_air_manifold; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the fuel flow into the engine. * * Inputs: Mixture, thi_sea_level, p_amb_sea_level, p_amb, m_dot_air * * Outputs: equivalence_ratio, m_dot_fuel */ void FGPiston::doFuelFlow(void) { double thi_sea_level = 1.3 * Mixture; equivalence_ratio = thi_sea_level * p_amb_sea_level / p_amb; m_dot_fuel = m_dot_air / 14.7 * equivalence_ratio; FuelFlow_gph = m_dot_fuel * 3600 // seconds to hours * 2.2046 // kg to lb / 6.6; // lb to gal_us of kerosene } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the power produced by the engine. * * Currently, the JSBSim propellor model does not allow the * engine to produce enough RPMs to get up to a high horsepower. * When tested with sufficient RPM, it has no trouble reaching * 200HP. * * Inputs: ManifoldPressure_inHg, p_amb, p_amb_sea_level, RPM, T_amb, * equivalence_ratio, Cycles, MaxHP * * Outputs: Percentage_Power, HP */ void FGPiston::doEnginePower(void) { if (Running) { double T_amb_degF = KelvinToFahrenheit(T_amb); double T_amb_sea_lev_degF = KelvinToFahrenheit(288); // FIXME: this needs to be generalized double ManXRPM; // Convienience term for use in the calculations if(Boosted) { // Currently a simple linear fit. // The zero crossing is moved up the speed-load range to reduce the idling power. // This will change! double zeroOffset = (minMAP / 2.0) * (IdleRPM / 2.0); ManXRPM = MAP * (RPM > RatedRPM[BoostSpeed] ? RatedRPM[BoostSpeed] : RPM); // The speed clip in the line above is deliberate. Percentage_Power = ((ManXRPM - zeroOffset) / ((RatedMAP[BoostSpeed] * RatedRPM[BoostSpeed]) - zeroOffset)) * 107.0; Percentage_Power -= 7.0; // Another idle power reduction offset - see line above with 107. if (Percentage_Power < 0.0) Percentage_Power = 0.0; // Note that %power is allowed to go over 100 for boosted powerplants // such as for the BCV-override or takeoff power settings. // TODO - currently no altitude effect (temperature & exhaust back-pressure) modelled // for boosted engines. } else { ManXRPM = ManifoldPressure_inHg * RPM; // Note that inHg must be used for the following correlation. Percentage_Power = (6e-9 * ManXRPM * ManXRPM) + (8e-4 * ManXRPM) - 1.0; Percentage_Power += ((T_amb_sea_lev_degF - T_amb_degF) * 7 /120); if (Percentage_Power < 0.0) Percentage_Power = 0.0; else if (Percentage_Power > 100.0) Percentage_Power = 100.0; } double Percentage_of_best_power_mixture_power = Power_Mixture_Correlation->GetValue(14.7 / equivalence_ratio); Percentage_Power *= Percentage_of_best_power_mixture_power / 100.0; if(Boosted) { HP = Percentage_Power * RatedPower[BoostSpeed] / 100.0; } else { HP = Percentage_Power * MaxHP / 100.0; } } else { // Power output when the engine is not running if (Cranking) { if (RPM < 10) { HP = 3.0; // This is a hack to prevent overshooting the idle rpm in // the first time step. It may possibly need to be changed // if the prop model is changed. } else if (RPM < 480) { HP = 3.0 + ((480 - RPM) / 10.0); // This is a guess - would be nice to find a proper starter moter torque curve } else { HP = 3.0; } } else { // Quick hack until we port the FMEP stuff if (RPM > 0.0) HP = -1.5; else HP = 0.0; } } //cout << "Power = " << HP << '\n'; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the exhaust gas temperature. * * Inputs: equivalence_ratio, m_dot_fuel, calorific_value_fuel, * Cp_air, m_dot_air, Cp_fuel, m_dot_fuel, T_amb, Percentage_Power * * Outputs: combustion_efficiency, ExhaustGasTemp_degK */ void FGPiston::doEGT(void) { double delta_T_exhaust; double enthalpy_exhaust; double heat_capacity_exhaust; double dEGTdt; if ((Running) && (m_dot_air > 0.0)) { // do the energy balance combustion_efficiency = Lookup_Combustion_Efficiency->GetValue(equivalence_ratio); enthalpy_exhaust = m_dot_fuel * calorific_value_fuel * combustion_efficiency * 0.33; heat_capacity_exhaust = (Cp_air * m_dot_air) + (Cp_fuel * m_dot_fuel); delta_T_exhaust = enthalpy_exhaust / heat_capacity_exhaust; ExhaustGasTemp_degK = T_amb + delta_T_exhaust; ExhaustGasTemp_degK *= 0.444 + ((0.544 - 0.444) * Percentage_Power / 100.0); } else { // Drop towards ambient - guess an appropriate time constant for now dEGTdt = (298.0 - ExhaustGasTemp_degK) / 100.0; delta_T_exhaust = dEGTdt * dt; ExhaustGasTemp_degK += delta_T_exhaust; } } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the cylinder head temperature. * * Inputs: T_amb, IAS, rho_air, m_dot_fuel, calorific_value_fuel, * combustion_efficiency, RPM * * Outputs: CylinderHeadTemp_degK */ void FGPiston::doCHT(void) { double h1 = -95.0; double h2 = -3.95; double h3 = -0.05; double arbitary_area = 1.0; double CpCylinderHead = 800.0; double MassCylinderHead = 8.0; double temperature_difference = CylinderHeadTemp_degK - T_amb; double v_apparent = IAS * 0.5144444; double v_dot_cooling_air = arbitary_area * v_apparent; double m_dot_cooling_air = v_dot_cooling_air * rho_air; double dqdt_from_combustion = m_dot_fuel * calorific_value_fuel * combustion_efficiency * 0.33; double dqdt_forced = (h2 * m_dot_cooling_air * temperature_difference) + (h3 * RPM * temperature_difference); double dqdt_free = h1 * temperature_difference; double dqdt_cylinder_head = dqdt_from_combustion + dqdt_forced + dqdt_free; double HeatCapacityCylinderHead = CpCylinderHead * MassCylinderHead; CylinderHeadTemp_degK += (dqdt_cylinder_head / HeatCapacityCylinderHead) * dt; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the oil temperature. * * Inputs: Percentage_Power, running flag. * * Outputs: OilTemp_degK */ void FGPiston::doOilTemperature(void) { double idle_percentage_power = 2.3; // approximately double target_oil_temp; // Steady state oil temp at the current engine conditions double time_constant; // The time constant for the differential equation if (Running) { target_oil_temp = 363; time_constant = 500; // Time constant for engine-on idling. if (Percentage_Power > idle_percentage_power) { time_constant /= ((Percentage_Power / idle_percentage_power) / 10.0); // adjust for power } } else { target_oil_temp = 298; time_constant = 1000; // Time constant for engine-off; reflects the fact // that oil is no longer getting circulated } double dOilTempdt = (target_oil_temp - OilTemp_degK) / time_constant; OilTemp_degK += (dOilTempdt * dt); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% /** * Calculate the oil pressure. * * Inputs: RPM * * Outputs: OilPressure_psi */ void FGPiston::doOilPressure(void) { double Oil_Press_Relief_Valve = 60; // FIXME: may vary by engine double Oil_Press_RPM_Max = 1800; // FIXME: may vary by engine double Design_Oil_Temp = 358; // degK; FIXME: may vary by engine double Oil_Viscosity_Index = 0.25; OilPressure_psi = (Oil_Press_Relief_Valve / Oil_Press_RPM_Max) * RPM; if (OilPressure_psi >= Oil_Press_Relief_Valve) { OilPressure_psi = Oil_Press_Relief_Valve; } OilPressure_psi += (Design_Oil_Temp - OilTemp_degK) * Oil_Viscosity_Index; } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% string FGPiston::GetEngineLabels(string delimeter) { std::ostringstream buf; buf << Name << "_PwrAvail[" << EngineNumber << "]" << delimeter << Name << "_HP[" << EngineNumber << "]" << delimeter << Thruster->GetThrusterLabels(EngineNumber, delimeter); return buf.str(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% string FGPiston::GetEngineValues(string delimeter) { std::ostringstream buf; buf << PowerAvailable << delimeter << HP << delimeter << Thruster->GetThrusterValues(EngineNumber, delimeter); return buf.str(); } //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // 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 FGPiston::Debug(int from) { if (debug_lvl <= 0) return; if (debug_lvl & 1) { // Standard console startup message output if (from == 0) { // Constructor cout << "\n Engine Name: " << Name << endl; cout << " MinManifoldPressure: " << MinManifoldPressure_inHg << endl; cout << " MaxManifoldPressure: " << MaxManifoldPressure_inHg << endl; cout << " Displacement: " << Displacement << endl; cout << " MaxHP: " << MaxHP << endl; cout << " Cycles: " << Cycles << endl; cout << " IdleRPM: " << IdleRPM << endl; cout << " MaxThrottle: " << MaxThrottle << endl; cout << " MinThrottle: " << MinThrottle << endl; cout << endl; cout << " Combustion Efficiency table:" << endl; Lookup_Combustion_Efficiency->Print(); cout << endl; cout << endl; cout << " Power Mixture Correlation table:" << endl; Power_Mixture_Correlation->Print(); cout << endl; } } if (debug_lvl & 2 ) { // Instantiation/Destruction notification if (from == 0) cout << "Instantiated: FGPiston" << endl; if (from == 1) cout << "Destroyed: FGPiston" << 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; } } } double FGPiston::CalcFuelNeed(void) { return FuelFlow_gph / 3600 * 6 * State->Getdt() * Propulsion->GetRate(); } } // namespace JSBSim