From 720b6fa3f66eff7981e97be0507bf93cee6b9834 Mon Sep 17 00:00:00 2001 From: ehofman Date: Mon, 13 Apr 2009 11:47:57 +0000 Subject: [PATCH 1/1] Sync. w. JSBSim CVS --- src/FDM/JSBSim/FGFDMExec.cpp | 198 ++++++++++++---- src/FDM/JSBSim/JSBSim.cxx | 17 +- .../initialization/FGInitialCondition.cpp | 4 + .../initialization/FGInitialCondition.h | 1 + src/FDM/JSBSim/models/FGLGear.cpp | 3 + src/FDM/JSBSim/models/propulsion/FGPiston.cpp | 216 ++++++++++-------- src/FDM/JSBSim/models/propulsion/FGPiston.h | 25 +- src/FDM/JSBSim/models/propulsion/FGTank.cpp | 8 +- src/FDM/JSBSim/models/propulsion/FGTank.h | 4 +- 9 files changed, 322 insertions(+), 154 deletions(-) diff --git a/src/FDM/JSBSim/FGFDMExec.cpp b/src/FDM/JSBSim/FGFDMExec.cpp index 159ce58a0..72f37d4e5 100644 --- a/src/FDM/JSBSim/FGFDMExec.cpp +++ b/src/FDM/JSBSim/FGFDMExec.cpp @@ -514,56 +514,170 @@ bool FGFDMExec::LoadModel(string model, bool addModelToPath) document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member if (document) { ReadPrologue(document); - element = document->GetElement(); - - result = true; - while (element && result) { - string element_name = element->GetName(); - if (element_name == "fileheader" ) result = ReadFileHeader(element); - else if (element_name == "slave") result = ReadSlave(element); - else if (element_name == "metrics") result = Aircraft->Load(element); - else if (element_name == "mass_balance") result = MassBalance->Load(element); - else if (element_name == "ground_reactions") result = GroundReactions->Load(element); - else if (element_name == "external_reactions") result = ExternalReactions->Load(element); - else if (element_name == "buoyant_forces") result = BuoyantForces->Load(element); - else if (element_name == "propulsion") result = Propulsion->Load(element); - else if (element_name == "system") result = FCS->Load(element, - FGFCS::stSystem); - else if (element_name == "autopilot") result = FCS->Load(element, - FGFCS::stAutoPilot); - else if (element_name == "flight_control") result = FCS->Load(element, - FGFCS::stFCS); - else if (element_name == "aerodynamics") result = Aerodynamics->Load(element); - else if (element_name == "input") result = Input->Load(element); - else if (element_name == "output") { - FGOutput* Output = new FGOutput(this); - Output->InitModel(); - Schedule(Output, 1); - result = Output->Load(element); - Outputs.push_back(Output); + + // Process the fileheader element in the aircraft config file. This element is OPTIONAL. + element = document->FindElement("fileheader"); + if (element) { + result = ReadFileHeader(element); + if (!result) { + cerr << endl << "Aircraft fileheader element has problems in file " << aircraftCfgFileName << endl; + return result; } - else { - cerr << "Found unexpected subsystem: " << element_name << ", exiting." << endl; - result = false; - break; + } + + // Process the metrics element. This element is REQUIRED. + element = document->FindElement("metrics"); + if (element) { + result = Aircraft->Load(element); + if (!result) { + cerr << endl << "Aircraft metrics element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No metrics element was found in the aircraft config file." << endl; + return false; + } + + // Process the mass_balance element. This element is REQUIRED. + element = document->FindElement("mass_balance"); + if (element) { + result = MassBalance->Load(element); + if (!result) { + cerr << endl << "Aircraft mass_balance element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No mass_balance element was found in the aircraft config file." << endl; + return false; + } + + // Process the ground_reactions element. This element is REQUIRED. + element = document->FindElement("ground_reactions"); + if (element) { + result = GroundReactions->Load(element); + if (!result) { + cerr << endl << "Aircraft ground_reactions element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No ground_reactions element was found in the aircraft config file." << endl; + return false; + } + + // Process the external_reactions element. This element is OPTIONAL. + element = document->FindElement("external_reactions"); + if (element) { + result = ExternalReactions->Load(element); + if (!result) { + cerr << endl << "Aircraft external_reactions element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the buoyant_forces element. This element is OPTIONAL. + element = document->FindElement("buoyant_forces"); + if (element) { + result = BuoyantForces->Load(element); + if (!result) { + cerr << endl << "Aircraft buoyant_forces element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the propulsion element. This element is OPTIONAL. + element = document->FindElement("propulsion"); + if (element) { + result = Propulsion->Load(element); + if (!result) { + cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the system element[s]. This element is OPTIONAL, and there may be more than one. + element = document->FindElement("system"); + while (element) { + result = FCS->Load(element, FGFCS::stSystem); + if (!result) { + cerr << endl << "Aircraft system element has problems in file " << aircraftCfgFileName << endl; + return result; + } + element = document->FindNextElement("system"); + } + + // Process the autopilot element. This element is OPTIONAL. + element = document->FindElement("autopilot"); + if (element) { + result = FCS->Load(element, FGFCS::stAutoPilot); + if (!result) { + cerr << endl << "Aircraft autopilot element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the flight_control element. This element is OPTIONAL. + element = document->FindElement("flight_control"); + if (element) { + result = FCS->Load(element, FGFCS::stFCS); + if (!result) { + cerr << endl << "Aircraft flight_control element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the aerodynamics element. This element is OPTIONAL, but almost always expected. + element = document->FindElement("aerodynamics"); + if (element) { + result = Aerodynamics->Load(element); + if (!result) { + cerr << endl << "Aircraft aerodynamics element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } else { + cerr << endl << "No expected aerodynamics element was found in the aircraft config file." << endl; + } + + // Process the input element. This element is OPTIONAL. + element = document->FindElement("input"); + if (element) { + result = Input->Load(element); + if (!result) { + cerr << endl << "Aircraft input element has problems in file " << aircraftCfgFileName << endl; + return result; + } + } + + // Process the output element[s]. This element is OPTIONAL, and there may be more than one. + element = document->FindElement("output"); + while (element) { + FGOutput* Output = new FGOutput(this); + Output->InitModel(); + Schedule(Output, 1); + result = Output->Load(element); + Outputs.push_back(Output); + if (!result) { + cerr << endl << "Aircraft output element has problems in file " << aircraftCfgFileName << endl; + return result; + } + element = document->FindNextElement("output"); + } + + // Lastly, process the slave element. This element is OPTIONAL - and NOT YET SUPPORTED. + element = document->FindElement("slave"); + if (element) { + result = ReadSlave(element); + if (!result) { + cerr << endl << "Aircraft slave element has problems in file " << aircraftCfgFileName << endl; + return result; } - element = document->GetNextElement(); } - } else { - cerr << fgred - << " JSBSim failed to load aircraft model." - << fgdef << endl; - return false; - } - if (result) { modelLoaded = true; - Debug(3); + } else { cerr << fgred - << " JSBSim failed to load properly." + << " JSBSim failed to open the configuration file: " << aircraftCfgFileName << fgdef << endl; - return false; } struct PropertyCatalogStructure masterPCS; diff --git a/src/FDM/JSBSim/JSBSim.cxx b/src/FDM/JSBSim/JSBSim.cxx index dc4ab1f65..5a1be0a29 100644 --- a/src/FDM/JSBSim/JSBSim.cxx +++ b/src/FDM/JSBSim/JSBSim.cxx @@ -150,9 +150,6 @@ FGJSBsim::FGJSBsim( double dt ) // file on each FlightGear reset. fgGetNode("/fdm/jsbsim/simulation/write-state-file")->untie(); fgGetNode("/fdm/jsbsim/simulation")->removeChild("write-state-file", false); - // Prevent nuking of the state on JSBSim recreation after FlightGear reset. - fgGetNode("/fdm/jsbsim/simulation/reset")->untie(); - fgGetNode("/fdm/jsbsim/simulation")->removeChild("reset", false); // end ugly hack // Register ground callback. @@ -328,9 +325,9 @@ void FGJSBsim::init() Atmosphere->UseInternal(); } - fgic->SetVNorthFpsIC( wind_from_north->getDoubleValue() ); - fgic->SetVEastFpsIC( wind_from_east->getDoubleValue() ); - fgic->SetVDownFpsIC( wind_from_down->getDoubleValue() ); + fgic->SetVNorthFpsIC( -wind_from_north->getDoubleValue() ); + fgic->SetVEastFpsIC( -wind_from_east->getDoubleValue() ); + fgic->SetVDownFpsIC( -wind_from_down->getDoubleValue() ); //Atmosphere->SetExTemperature(get_Static_temperature()); //Atmosphere->SetExPressure(get_Static_pressure()); @@ -625,9 +622,9 @@ bool FGJSBsim::copy_to_JSBsim() tmp = turbulence_rate->getDoubleValue(); //Atmosphere->SetTurbRate(tmp); - Atmosphere->SetWindNED( wind_from_north->getDoubleValue(), - wind_from_east->getDoubleValue(), - wind_from_down->getDoubleValue() ); + Atmosphere->SetWindNED( -wind_from_north->getDoubleValue(), + -wind_from_east->getDoubleValue(), + -wind_from_down->getDoubleValue() ); // SG_LOG(SG_FLIGHT,SG_INFO, "Wind NED: " // << get_V_north_airmass() << ", " // << get_V_east_airmass() << ", " @@ -1100,6 +1097,7 @@ void FGJSBsim::init_gear(void ) node->setDoubleValue("yoffset-in", gear->GetBodyLocation()(2)); node->setDoubleValue("zoffset-in", gear->GetBodyLocation()(3)); node->setBoolValue("wow", gear->GetWOW()); + node->setDoubleValue("rollspeed-ms", gear->GetWheelRollVel()*0.3043); node->setBoolValue("has-brake", gear->GetBrakeGroup() > 0); node->setDoubleValue("position-norm", gear->GetGearUnitPos()); node->setDoubleValue("tire-pressure-norm", gear->GetTirePressure()); @@ -1118,6 +1116,7 @@ void FGJSBsim::update_gear(void) FGLGear *gear = gr->GetGearUnit(i); SGPropertyNode * node = fgGetNode("gear/gear", i, true); node->getChild("wow", 0, true)->setBoolValue( gear->GetWOW()); + node->getChild("rollspeed-ms", 0, true)->setDoubleValue(gear->GetWheelRollVel()*0.3043); node->getChild("position-norm", 0, true)->setDoubleValue(gear->GetGearUnitPos()); gear->SetTirePressure(node->getDoubleValue("tire-pressure-norm")); node->setDoubleValue("compression-norm", gear->GetCompLen()); diff --git a/src/FDM/JSBSim/initialization/FGInitialCondition.cpp b/src/FDM/JSBSim/initialization/FGInitialCondition.cpp index 2fbbae224..4df38860a 100644 --- a/src/FDM/JSBSim/initialization/FGInitialCondition.cpp +++ b/src/FDM/JSBSim/initialization/FGInitialCondition.cpp @@ -69,7 +69,9 @@ FGInitialCondition::FGInitialCondition(FGFDMExec *FDMExec) : fdmex(FDMExec) fdmex->GetPropagate()->Seth(altitude); fdmex->GetAtmosphere()->Run(); PropertyManager=fdmex->GetPropertyManager(); + Constructing = true; bind(); + Constructing = false; } else { cout << "FGInitialCondition: This class requires a pointer to a valid FGFDMExec object" << endl; } @@ -156,6 +158,8 @@ void FGInitialCondition::InitializeIC(void) void FGInitialCondition::WriteStateFile(int num) { + if (Constructing) return; + string filename = fdmex->GetFullAircraftPath(); if (filename.empty()) diff --git a/src/FDM/JSBSim/initialization/FGInitialCondition.h b/src/FDM/JSBSim/initialization/FGInitialCondition.h index 3723508bd..c70cfc9ab 100644 --- a/src/FDM/JSBSim/initialization/FGInitialCondition.h +++ b/src/FDM/JSBSim/initialization/FGInitialCondition.h @@ -644,6 +644,7 @@ private: FGFDMExec *fdmex; FGPropertyManager *PropertyManager; + bool Constructing; bool getAlpha(void); bool getTheta(void); bool getMachFromVcas(double *Mach,double vcas); diff --git a/src/FDM/JSBSim/models/FGLGear.cpp b/src/FDM/JSBSim/models/FGLGear.cpp index 9dd5be8a4..8ea49d207 100644 --- a/src/FDM/JSBSim/models/FGLGear.cpp +++ b/src/FDM/JSBSim/models/FGLGear.cpp @@ -340,6 +340,9 @@ FGColumnVector3& FGLGear::Force(void) WOW = false; compressLength = 0.0; + // No wheel conditons + RollingWhlVel = SideWhlVel = WheelSlip = 0.0; + // Return to neutral position between 1.0 and 0.8 gear pos. SteerAngle *= max(GetGearUnitPos()-0.8, 0.0)/0.2; diff --git a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp index 56941f77b..80ac4dc8b 100644 --- a/src/FDM/JSBSim/models/propulsion/FGPiston.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGPiston.cpp @@ -71,8 +71,9 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number) dt = State->Getdt(); // These items are read from the configuration file + // Defaults are from a Lycoming O-360, more or less - Cycles = 2; + Cycles = 4; IdleRPM = 600; MaxRPM = 2800; Displacement = 360; @@ -80,10 +81,12 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number) MaxHP = 200; MinManifoldPressure_inHg = 6.5; MaxManifoldPressure_inHg = 28.5; - BSFC = -1; - - // Initialisation - volumetric_efficiency = 0.8; // Actually f(speed, load) but this will get us running + ISFC = -1; + volumetric_efficiency = -0.1; + Bore = 5.125; + Stroke = 4.375; + Cylinders = 4; + CompressionRatio = 8.5; // These are internal program variables @@ -173,9 +176,17 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number) if (el->FindElement("minthrottle")) MinThrottle = el->FindElementValueAsNumber("minthrottle"); if (el->FindElement("bsfc")) - BSFC = el->FindElementValueAsNumberConvertTo("bsfc", "LBS/HP*HR"); + ISFC = el->FindElementValueAsNumberConvertTo("bsfc", "LBS/HP*HR"); if (el->FindElement("volumetric-efficiency")) volumetric_efficiency = el->FindElementValueAsNumber("volumetric-efficiency"); + if (el->FindElement("compression-ratio")) + CompressionRatio = el->FindElementValueAsNumber("compression-ratio"); + if (el->FindElement("bore")) + Bore = el->FindElementValueAsNumberConvertTo("bore","IN"); + if (el->FindElement("stroke")) + Stroke = el->FindElementValueAsNumberConvertTo("stroke","IN"); + if (el->FindElement("stroke")) + Cylinders = el->FindElementValueAsNumber("cylinders"); if (el->FindElement("numboostspeeds")) { // Turbo- and super-charging parameters BoostSpeeds = (int)el->FindElementValueAsNumber("numboostspeeds"); if (el->FindElement("boostoverride")) @@ -208,30 +219,42 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number) RatedAltitude[2] = el->FindElementValueAsNumberConvertTo("ratedaltitude3", "FT"); } - MaxManifoldPressure_Percent = MaxManifoldPressure_inHg / 29.92; - // Create a BSFC to match the engine if not provided - if (BSFC < 0) { - BSFC = ( Displacement * MaxRPM * volumetric_efficiency ) / (9411 * MaxHP); - BSFC *= (MaxManifoldPressure_Percent * MaxManifoldPressure_Percent * MaxManifoldPressure_Percent); + StarterHP = sqrt(MaxHP) * 0.4; + displacement_SI = Displacement * in3tom3; + + // Create IFSC and VE to match the engine if not provided + int calculated_ve=0; + if (volumetric_efficiency < 0) { + volumetric_efficiency = MaxManifoldPressure_inHg / 29.92; + calculated_ve=1; + } + if (ISFC < 0) { + double pmep = MaxManifoldPressure_inHg > 29.92 ? 0 : 29.92 - MaxManifoldPressure_inHg; + pmep *= inhgtopa; + double fmep = (18400 * (2*(Stroke/12)*(MaxRPM/60)) * fttom + 46500)/2; + double hp_loss = ((pmep + fmep) * displacement_SI * MaxRPM)/(Cycles*22371); + ISFC = ( Displacement * MaxRPM * volumetric_efficiency ) / (9411 * (MaxHP+hp_loss)); +// cout <<"FMEP: "<< fmep <<" PMEP: "<< pmep << " hp_loss: " < 29.9 ) { // Don't allow boosting with a bogus number MaxManifoldPressure_inHg = 29.9; - MaxManifoldPressure_Percent = MaxManifoldPressure_inHg / 29.92; + if (calculated_ve) volumetric_efficiency = 1.0; } + minMAP = MinManifoldPressure_inHg * inhgtopa; // inHg to Pa + maxMAP = MaxManifoldPressure_inHg * inhgtopa; string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/power-hp"; PropertyManager->Tie(property_name, &HP); property_name = base_property_name + "/bsfc-lbs_hphr"; - PropertyManager->Tie(property_name, &BSFC); + PropertyManager->Tie(property_name, &ISFC); property_name = base_property_name + "/volumetric-efficiency"; PropertyManager->Tie(property_name, &volumetric_efficiency); + property_name = base_property_name + "/map-pa"; + PropertyManager->Tie(property_name, &MAP); property_name = base_property_name + "/map-inhg"; PropertyManager->Tie(property_name, &ManifoldPressure_inHg); - minMAP = MinManifoldPressure_inHg * inhgtopa; // inHg to Pa - maxMAP = MaxManifoldPressure_inHg * inhgtopa; - StarterHP = sqrt(MaxHP) * 0.4; // Set up and sanity-check the turbo/supercharging configuration based on the input values. if (TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true; @@ -279,9 +302,6 @@ FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number) BoostSpeed = 0; } bBoostOverride = (BoostOverride == 1 ? true : false); - if (MinThrottle < 0.12) MinThrottle = 0.12; //MinThrottle is limited to 0.12 to prevent the - // throttle area equation from going negative - // 0.12 is 1% of maximum area Debug(0); // Call Debug() routine from constructor if needed } @@ -319,7 +339,7 @@ double FGPiston::Calculate(void) if (FuelFlow_gph > 0.0) ConsumeFuel(); Throttle = FCS->GetThrottlePos(EngineNumber); - // calculate the throttle plate angle. 1 unit is pi/2 radians. + // calculate the throttle plate angle. 1 unit is approx pi/2 radians. ThrottleAngle = MinThrottle+((MaxThrottle-MinThrottle)*Throttle ); Mixture = FCS->GetMixturePos(EngineNumber); @@ -328,10 +348,10 @@ double FGPiston::Calculate(void) // p_amb = Atmosphere->GetPressure() * psftopa; - p_amb_sea_level = Atmosphere->GetPressureSL() * psftopa; T_amb = RankineToKelvin(Atmosphere->GetTemperature()); RPM = Thruster->GetRPM() * Thruster->GetGearRatio(); + MeanPistonSpeed_fps = ( RPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS IAS = Auxiliary->GetVcalibratedKTS(); @@ -349,7 +369,7 @@ double FGPiston::Calculate(void) // Running = false; doEnginePower(); - if (HP < 0.1250) Running = false; + if (IndicatedHorsePower < 0.1250) Running = false; doEGT(); doCHT(); @@ -489,57 +509,62 @@ void FGPiston::doBoostControl(void) * from the throttle position, turbo/supercharger boost control * system, engine speed and local ambient air density. * - * Inputs: p_amb, Throttle, MaxManifoldPressure_Percent, ThrottleAngle - * RPM, MaxRPM + * Inputs: p_amb, Throttle, ThrottleAngle, + * MeanPistonSpeed_fps, dt * * Outputs: MAP, ManifoldPressure_inHg */ void FGPiston::doMAP(void) { - // estimate throttle plate area. This maps 0.2 -> 0.1 for historical performance reasons - double throttle_area = ThrottleAngle * 1.125 - 0.125; - map_coefficient = pow ((throttle_area * MaxManifoldPressure_Percent),RPM/MaxRPM); - MAP = p_amb * map_coefficient; - - 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. - double boost_factor = BoostMul[BoostSpeed] * map_coefficient * RPM/RatedRPM[BoostSpeed]; - if (boost_factor < 1.0) boost_factor = 1.0; // boost will never reduce the MAP - MAP *= boost_factor; - // Now clip the manifold pressure to BCV or Wastegate setting. - if(bTakeoffPos) { - if(MAP > TakeoffMAP[BoostSpeed]) { - MAP = TakeoffMAP[BoostSpeed]; - } + // estimate throttle plate area. + double throttle_area = ThrottleAngle*ThrottleAngle; + // Internal Combustion Engine in Theory and Practice, Volume 2. Charles Fayette Taylor. Revised Edition, 1985 fig 6-13 + double map_coefficient = 1-((MeanPistonSpeed_fps*MeanPistonSpeed_fps)/(24978*throttle_area)); + + if ( map_coefficient < 0.1 ) map_coefficient = 0.1; + + // map_coefficient = pow ((throttle_area * MaxManifoldPressure_Percent),RPM/MaxRPM); + // Add a one second lag to manifold pressure changes + double dMAP = (MAP - p_amb * map_coefficient) * dt; + MAP -=dMAP; + + // Find the mean effective pressure required to achieve this manifold pressure + // Doing this before boost so boost doesn't add horsepower to the engine. + // A better method would be deterimining the HP consumed by the supercharger + + PMEP = MAP - p_amb; // Fixme: p_amb should be exhaust manifold pressure + + 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. + + bool bTakeoffPos = false; + if (bTakeoffBoost) { + if (Throttle > 0.98) { + bTakeoffPos = true; + } else if(Throttle <= 0.95) { + bTakeoffPos = false; } else { - if(MAP > RatedMAP[BoostSpeed]) { - MAP = RatedMAP[BoostSpeed]; - } + bTakeoffPos = false; } } + // Boost the manifold pressure. + double boost_factor = BoostMul[BoostSpeed] * map_coefficient * RPM/RatedRPM[BoostSpeed]; + if (boost_factor < 1.0) boost_factor = 1.0; // boost will never reduce the MAP + MAP *= boost_factor; + // 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]; + } + } // And set the value in American units as well ManifoldPressure_inHg = MAP / inhgtopa; @@ -561,20 +586,24 @@ void FGPiston::doMAP(void) void FGPiston::doAirFlow(void) { + double gamma = 1.4; // specific heat constants +// loss of volumentric efficiency due to difference between MAP and exhaust pressure + double ve =((gamma-1)/gamma)+( CompressionRatio -(p_amb/MAP))/(gamma*( CompressionRatio - 1)); + rho_air = p_amb / (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 * map_coefficient; + double v_dot_air = swept_volume * volumetric_efficiency *ve; double rho_air_manifold = MAP / (R_air * T_amb); 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 + * Inputs: Mixture, thi_sea_level, p_amb, m_dot_air * * Outputs: equivalence_ratio, m_dot_fuel */ @@ -601,49 +630,53 @@ void FGPiston::doFuelFlow(void) * 200HP. * * Inputs: ManifoldPressure_inHg, p_amb, RPM, T_amb, - * Mixture_Efficiency_Correlation, Cycles, MaxHP + * Mixture_Efficiency_Correlation, Cycles, MaxHP, PMEP, * - * Outputs: Percentage_Power, HP + * Outputs: PctPower, HP */ void FGPiston::doEnginePower(void) { + IndicatedHorsePower = 0; + FMEP = 0; if (Running) { // FIXME: this needs to be generalized - double ME, friction, percent_RPM, power; // Convienience term for use in the calculations + double ME, percent_RPM, power; // Convienience term for use in the calculations ME = Mixture_Efficiency_Correlation->GetValue(m_dot_fuel/m_dot_air); percent_RPM = RPM/MaxRPM; - friction = 1 - (percent_RPM * percent_RPM * percent_RPM * percent_RPM/10); - if (friction < 0 ) friction = 0; - power = friction; +// Guestimate engine friction as a percentage of rated HP + a percentage of rpm + a percentage of Indicted HP +// friction = 1 - (percent_RPM * percent_RPM * percent_RPM/10); + FMEP = (-18400 * MeanPistonSpeed_fps * fttom - 46500); + + power = 1; if ( Magnetos != 3 ) power *= SparkFailDrop; - HP = (FuelFlow_gph * 6.0 / BSFC )* ME * map_coefficient * power; + IndicatedHorsePower = (FuelFlow_pph / ISFC )* ME * power; } else { - // Power output when the engine is not running if (Cranking) { if (RPM < 10) { - HP = StarterHP; + IndicatedHorsePower = StarterHP; } else if (RPM < IdleRPM*0.8) { - HP = StarterHP + ((IdleRPM*0.8 - RPM) / 8.0); + IndicatedHorsePower = StarterHP + ((IdleRPM*0.8 - RPM) / 8.0); // This is a guess - would be nice to find a proper starter moter torque curve } else { - HP = StarterHP; + IndicatedHorsePower = StarterHP; } - } else { - // Quick hack until we port the FMEP stuff - if (RPM > 0.0) - HP = -1.5; - else - HP = 0.0; } } - Percentage_Power = HP / MaxHP ; + + // Constant is (1/2) * 60 * 745.7 + // (1/2) convert cycles, 60 minutes to seconds, 745.7 watts to hp. + double pumping_hp = ((PMEP + FMEP) * displacement_SI * RPM)/(Cycles*22371); + + HP = IndicatedHorsePower + pumping_hp - 1.5; //FIXME 1.5 static friction should depend on oil temp and configuration +// cout << "pumping_hp " < {number} - {number} + {number} {number} + {number} + {number} + {number} + {number} {number} {number} {number} @@ -76,9 +80,9 @@ CLASS DOCUMENTATION {number} {number} {number} - {number} {number} {number} + {number} {0 | 1} {number} {number} @@ -210,7 +214,9 @@ protected: private: int crank_counter; - double BrakeHorsePower; + double IndicatedHorsePower; + double PMEP; + double FMEP; double SpeedSlope; double SpeedIntercept; double AltitudeSlope; @@ -243,7 +249,6 @@ private: const double Cp_fuel; // J/KgK FGTable *Lookup_Combustion_Efficiency; - FGTable *Power_Mixture_Correlation; FGTable *Mixture_Efficiency_Correlation; // @@ -253,11 +258,17 @@ private: double MaxManifoldPressure_inHg; // Inches Hg double MaxManifoldPressure_Percent; // MaxManifoldPressure / 29.92 double Displacement; // cubic inches + double displacement_SI; // cubic meters double MaxHP; // horsepower double SparkFailDrop; // drop of power due to spark failure double Cycles; // cycles/power stroke double IdleRPM; // revolutions per minute double MaxRPM; // revolutions per minute + double Bore; // inches + double Stroke; // inches + double Cylinders; // number + double CompressionRatio; // number + double StarterHP; // initial horsepower of starter motor int BoostSpeeds; // Number of super/turbocharger boost speeds - zero implies no turbo/supercharging. int BoostSpeed; // The current boost-speed (zero-based). @@ -284,13 +295,12 @@ private: double minMAP; // Pa double maxMAP; // Pa double MAP; // Pa - double BSFC; // brake specific fuel consumption [lbs/horsepower*hour + double ISFC; // Indicated specific fuel consumption [lbs/horsepower*hour // // Inputs (in addition to those in FGEngine). // double p_amb; // Pascals - double p_amb_sea_level; // Pascals double T_amb; // degrees Kelvin double RPM; // revolutions per minute double IAS; // knots @@ -298,7 +308,6 @@ private: bool Magneto_Right; int Magnetos; - // // Outputs (in addition to those in FGEngine). // @@ -308,7 +317,6 @@ private: double m_dot_air; double equivalence_ratio; double m_dot_fuel; - double Percentage_Power; double HP; double combustion_efficiency; double ExhaustGasTemp_degK; @@ -317,6 +325,7 @@ private: double CylinderHeadTemp_degK; double OilPressure_psi; double OilTemp_degK; + double MeanPistonSpeed_fps; void Debug(int from); }; diff --git a/src/FDM/JSBSim/models/propulsion/FGTank.cpp b/src/FDM/JSBSim/models/propulsion/FGTank.cpp index 7b5e6729d..cbf81329f 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTank.cpp +++ b/src/FDM/JSBSim/models/propulsion/FGTank.cpp @@ -37,6 +37,7 @@ INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ #include "FGTank.h" +#include using std::cerr; using std::endl; @@ -52,7 +53,7 @@ CLASS IMPLEMENTATION %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number) - : TankNumber(tank_number) + : TankNumber(tank_number), Exec(exec) { string token; Element* element; @@ -60,9 +61,8 @@ FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number) Area = 1.0; Temperature = -9999.0; Ixx = Iyy = Izz = 0.0; - Auxiliary = exec->GetAuxiliary(); Radius = Capacity = Contents = Standpipe = Length = InnerRadius = 0.0; - PropertyManager = exec->GetPropertyManager(); + PropertyManager = Exec->GetPropertyManager(); vXYZ.InitMatrix(); vXYZ_drain.InitMatrix(); @@ -245,7 +245,7 @@ double FGTank::Calculate(double dt) if (Temperature == -9999.0) return 0.0; double HeatCapacity = 900.0; // Joules/lbm/C double TempFlowFactor = 1.115; // Watts/sqft/C - double TAT = Auxiliary->GetTAT_C(); + double TAT = Exec->GetAuxiliary()->GetTAT_C(); double Tdiff = TAT - Temperature; double dTemp = 0.0; // Temp change due to one surface if (fabs(Tdiff) > 0.1) { diff --git a/src/FDM/JSBSim/models/propulsion/FGTank.h b/src/FDM/JSBSim/models/propulsion/FGTank.h index 950c0c12a..a6b574a06 100644 --- a/src/FDM/JSBSim/models/propulsion/FGTank.h +++ b/src/FDM/JSBSim/models/propulsion/FGTank.h @@ -44,10 +44,10 @@ SENTRY INCLUDES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/ +#include "FGFDMExec.h" #include #include #include -#include #include using std::string; @@ -281,7 +281,7 @@ private: double Temperature, InitialTemperature; double Standpipe, InitialStandpipe; bool Selected; - FGAuxiliary* Auxiliary; + FGFDMExec* Exec; FGPropertyManager* PropertyManager; void CalculateInertias(void); void Debug(int from); -- 2.39.2