void FGFCS::bindThrottle(unsigned int num) { string tmp; tmp = CreateIndexedPropertyName("fcs/throttle-cmd-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottleCmd, &FGFCS::SetThrottleCmd); tmp = CreateIndexedPropertyName("fcs/throttle-pos-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetThrottlePos, &FGFCS::SetThrottlePos); tmp = CreateIndexedPropertyName("fcs/mixture-cmd-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixtureCmd, &FGFCS::SetMixtureCmd); tmp = CreateIndexedPropertyName("fcs/mixture-pos-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetMixturePos, &FGFCS::SetMixturePos); tmp = CreateIndexedPropertyName("fcs/advance-cmd-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvanceCmd, &FGFCS::SetPropAdvanceCmd); tmp = CreateIndexedPropertyName("fcs/advance-pos-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropAdvance, &FGFCS::SetPropAdvance); tmp = CreateIndexedPropertyName("fcs/feather-cmd-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetFeatherCmd, &FGFCS::SetFeatherCmd); tmp = CreateIndexedPropertyName("fcs/feather-pos-norm", num); PropertyManager->Tie( tmp.c_str(), this, num, &FGFCS::GetPropFeather, &FGFCS::SetPropFeather); }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // // This function binds properties for each pointmass object created. // void FGMassBalance::PointMass::bind(FGPropertyManager* PropertyManager, int num) { string tmp = CreateIndexedPropertyName("inertia/pointmass-weight-lbs", num); PropertyManager->Tie( tmp.c_str(), this, &PointMass::GetPointMassWeight, &PointMass::SetPointMassWeight); tmp = CreateIndexedPropertyName("inertia/pointmass-location-X-inches", num); PropertyManager->Tie( tmp.c_str(), this, eX, &PointMass::GetPointMassLocation, &PointMass::SetPointMassLocation); tmp = CreateIndexedPropertyName("inertia/pointmass-location-Y-inches", num); PropertyManager->Tie( tmp.c_str(), this, eY, &PointMass::GetPointMassLocation, &PointMass::SetPointMassLocation); tmp = CreateIndexedPropertyName("inertia/pointmass-location-Z-inches", num); PropertyManager->Tie( tmp.c_str(), this, eZ, &PointMass::GetPointMassLocation, &PointMass::SetPointMassLocation); }
void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs) { struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure; int node_idx = 0; for (int i=0; i<pcs->node->nChildren(); i++) { string access=""; pcsNew->base_string = pcs->base_string + "/" + pcs->node->getChild(i)->getName(); node_idx = pcs->node->getChild(i)->getIndex(); if (node_idx != 0) { pcsNew->base_string = CreateIndexedPropertyName(pcsNew->base_string, node_idx); } if (pcs->node->getChild(i)->nChildren() == 0) { if (pcsNew->base_string.substr(0,12) == string("/fdm/jsbsim/")) { pcsNew->base_string = pcsNew->base_string.erase(0,12); } if (pcs->node->getChild(i)->getAttribute(SGPropertyNode::READ)) access="R"; if (pcs->node->getChild(i)->getAttribute(SGPropertyNode::WRITE)) access+="W"; PropertyCatalog.push_back(pcsNew->base_string+" ("+access+")"); } else { pcsNew->node = (FGPropertyNode*)pcs->node->getChild(i); BuildPropertyCatalog(pcsNew); } } delete pcsNew; }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // This function should tie properties to rocket engine specific properties // that are not bound in the base class (FGEngine) code. // void FGRocket::bindmodel() { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/total-impulse"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalImpulse); property_name = base_property_name + "/vacuum-thrust_lbs"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetVacThrust); if (ThrustTable) { // Solid rocket motor property_name = base_property_name + "/thrust-variation_pct"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetThrustVariation, &FGRocket::SetThrustVariation); property_name = base_property_name + "/total-isp-variation_pct"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalIspVariation, &FGRocket::SetTotalIspVariation); } else { // Liquid rocket motor property_name = base_property_name + "/oxi-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetOxiFlowRate); property_name = base_property_name + "/mixture-ratio"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetMixtureRatio, &FGRocket::SetMixtureRatio); property_name = base_property_name + "/isp"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetIsp, &FGRocket::SetIsp); } }
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% // This funciton should tie properties to rocket engine specific properties // that are not bound in the base class (FGEngine) code. // void FGRocket::bindmodel() { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/total-impulse"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetTotalImpulse); property_name = base_property_name + "/oxi-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetOxiFlowRate); property_name = base_property_name + "/vacuum-thrust_lbs"; PropertyManager->Tie( property_name.c_str(), this, &FGRocket::GetVacThrust); }
FGElectric::FGElectric(FGFDMExec* exec, Element *el, int engine_number, struct FGEngine::Inputs& input) : FGEngine(engine_number, input) { Load(exec,el); Type = etElectric; PowerWatts = 745.7; hptowatts = 745.7; if (el->FindElement("power")) PowerWatts = el->FindElementValueAsNumberConvertTo("power","WATTS"); string base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); exec->GetPropertyManager()->Tie(base_property_name + "/power-hp", &HP); Debug(0); // Call Debug() routine from constructor if needed }
bool FGRotor::bind(void) { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNum); PropertyManager->Tie( base_property_name + "/rotor-rpm", this, &FGRotor::GetRPM ); PropertyManager->Tie( base_property_name + "/thrust-mr-lbs", &mr.Thrust ); PropertyManager->Tie( base_property_name + "/vi-mr-fps", &mr.v_induced ); PropertyManager->Tie( base_property_name + "/a0-mr-rad", &mr.a0 ); PropertyManager->Tie( base_property_name + "/a1-mr-rad", &mr.a1s ); // s means shaft axes PropertyManager->Tie( base_property_name + "/b1-mr-rad", &mr.b1s ); PropertyManager->Tie( base_property_name + "/thrust-tr-lbs", &tr.Thrust ); PropertyManager->Tie( base_property_name + "/vi-tr-fps", &tr.v_induced ); // lambda PropertyManager->Tie( base_property_name + "/inflow-ratio", &prop_inflow_ratio_lambda ); // mu PropertyManager->Tie( base_property_name + "/advance-ratio", &prop_advance_ratio_mu ); // nu PropertyManager->Tie( base_property_name + "/induced-inflow-ratio", &prop_inflow_ratio_induced_nu ); PropertyManager->Tie( base_property_name + "/torque-mr-lbsft", &prop_mr_torque ); PropertyManager->Tie( base_property_name + "/thrust-coefficient", &prop_thrust_coefficient ); PropertyManager->Tie( base_property_name + "/main-rotor-rpm", &mr.ActualRPM ); PropertyManager->Tie( base_property_name + "/tail-rotor-rpm", &tr.ActualRPM ); // position of the downwash PropertyManager->Tie( base_property_name + "/theta-downwash-rad", &prop_theta_downwash ); PropertyManager->Tie( base_property_name + "/phi-downwash-rad", &prop_phi_downwash ); // nodes to use via get<xyz>Value prop_collective_ctrl = PropertyManager->GetNode(base_property_name + "/collective-ctrl-rad",true); prop_lateral_ctrl = PropertyManager->GetNode(base_property_name + "/lateral-ctrl-rad",true); prop_longitudinal_ctrl = PropertyManager->GetNode(base_property_name + "/longitudinal-ctrl-rad",true); prop_antitorque_ctrl = PropertyManager->GetNode(base_property_name + "/antitorque-ctrl-rad",true); prop_rotorbrake = PropertyManager->GetNode(base_property_name + "/rotorbrake-hp", true); prop_freewheel_factor = PropertyManager->GetNode(base_property_name + "/freewheel-factor", true); PropertyManager->Tie( base_property_name + "/dump-flag", &prop_DumpFlag ); return true; }
bool FGTransmission::BindModel(int num) { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", num); property_name = base_property_name + "/brake-ctrl-norm"; PropertyManager->Tie( property_name.c_str(), this, &FGTransmission::GetBrakeCtrlNorm, &FGTransmission::SetBrakeCtrlNorm); property_name = base_property_name + "/clutch-ctrl-norm"; PropertyManager->Tie( property_name.c_str(), this, &FGTransmission::GetClutchCtrlNorm, &FGTransmission::SetClutchCtrlNorm); property_name = base_property_name + "/free-wheel-transmission"; PropertyManager->Tie( property_name.c_str(), this, &FGTransmission::GetFreeWheelTransmission); return true; }
void FGTurboProp::bindmodel() { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/n1"; PropertyManager->Tie( property_name.c_str(), &N1); // property_name = base_property_name + "/n2"; // PropertyManager->Tie( property_name.c_str(), &N2); property_name = base_property_name + "/reverser"; PropertyManager->Tie( property_name.c_str(), &Reversed); property_name = base_property_name + "/power-hp"; PropertyManager->Tie( property_name.c_str(), &HP); property_name = base_property_name + "/itt-c"; PropertyManager->Tie( property_name.c_str(), &Eng_ITT_degC); property_name = base_property_name + "/engtemp-c"; PropertyManager->Tie( property_name.c_str(), &Eng_Temperature); property_name = base_property_name + "/ielu_intervent"; PropertyManager->Tie( property_name.c_str(), &Ielu_intervent); property_name = base_property_name + "/combustion_efficiency"; PropertyManager->Tie( property_name.c_str(), &CombustionEfficiency); }
bool FGFDMExec::SetOutputDirectives(const string& fname) { bool result; FGOutput* Output = new FGOutput(this); Output->SetDirectivesFile(RootDir + fname); Output->InitModel(); Schedule(Output); result = Output->Load(0); if (result) { Output->Run(holding); Outputs.push_back(Output); typedef double (FGOutput::*iOPMF)(void) const; string outputProp = CreateIndexedPropertyName("simulation/output",Outputs.size()-1); instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); } else delete Output; return result; }
void FGFDMExec::BuildPropertyCatalog(struct PropertyCatalogStructure* pcs) { struct PropertyCatalogStructure* pcsNew = new struct PropertyCatalogStructure; int node_idx = 0; for (int i=0; i<pcs->node->nChildren(); i++) { pcsNew->base_string = pcs->base_string + "/" + pcs->node->getChild(i)->getName(); node_idx = pcs->node->getChild(i)->getIndex(); if (node_idx != 0) { pcsNew->base_string = CreateIndexedPropertyName(pcsNew->base_string, node_idx); } if (pcs->node->getChild(i)->nChildren() == 0) { if (pcsNew->base_string.substr(0,11) == string("/fdm/jsbsim")) { pcsNew->base_string = pcsNew->base_string.erase(0,12); } PropertyCatalog.push_back(pcsNew->base_string); } else { pcsNew->node = (FGPropertyManager*)pcs->node->getChild(i); BuildPropertyCatalog(pcsNew); } } delete pcsNew; }
void FGTank::bind(FGPropertyManager* PropertyManager) { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/tank", TankNumber); property_name = base_property_name + "/contents-lbs"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetContents, &FGTank::SetContents ); property_name = base_property_name + "/pct-full"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetPctFull); property_name = base_property_name + "/priority"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetPriority, &FGTank::SetPriority ); property_name = base_property_name + "/external-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetExternalFlow, &FGTank::SetExternalFlow ); property_name = base_property_name + "/local-ixx-slug_ft2"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetIxx); property_name = base_property_name + "/local-iyy-slug_ft2"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetIyy); property_name = base_property_name + "/local-izz-slug_ft2"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetIzz); }
void FGTurbine::bindmodel(FGPropertyManager* PropertyManager) { string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/n1"; PropertyManager->Tie( property_name.c_str(), &N1); property_name = base_property_name + "/n2"; PropertyManager->Tie( property_name.c_str(), &N2); property_name = base_property_name + "/injection_cmd"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetInjection, &FGTurbine::SetInjection); property_name = base_property_name + "/seized"; PropertyManager->Tie( property_name.c_str(), &Seized); property_name = base_property_name + "/stalled"; PropertyManager->Tie( property_name.c_str(), &Stalled); property_name = base_property_name + "/bleed-factor"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetBleedDemand, &FGTurbine::SetBleedDemand); property_name = base_property_name + "/MaxN1"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetMaxN1, &FGTurbine::SetMaxN1); property_name = base_property_name + "/MaxN2"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetMaxN2, &FGTurbine::SetMaxN2); property_name = base_property_name + "/InjectionTimer"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetInjectionTimer, &FGTurbine::SetInjectionTimer); property_name = base_property_name + "/InjWaterNorm"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetInjWaterNorm, &FGTurbine::SetInjWaterNorm); property_name = base_property_name + "/InjN1increment"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetInjN1increment, &FGTurbine::SetInjN1increment); property_name = base_property_name + "/InjN2increment"; PropertyManager->Tie( property_name.c_str(), (FGTurbine*)this, &FGTurbine::GetInjN2increment, &FGTurbine::SetInjN2increment); }
FGBallonet::FGBallonet(FGFDMExec* exec, Element* el, unsigned int num, FGGasCell* parent, const struct FGGasCell::Inputs& input) : in(input) { string token; Element* element; FGPropertyManager* PropertyManager = exec->GetPropertyManager(); MassBalance = exec->GetMassBalance(); ballonetJ = FGMatrix33(); MaxVolume = MaxOverpressure = Temperature = Pressure = Contents = Volume = dVolumeIdeal = dU = 0.0; Xradius = Yradius = Zradius = Xwidth = Ywidth = Zwidth = 0.0; ValveCoefficient = ValveOpen = 0.0; BlowerInput = NULL; CellNum = num; Parent = parent; // NOTE: In the local system X points north, Y points east and Z points down. element = el->FindElement("location"); if (element) { vXYZ = element->FindElementTripletConvertTo("IN"); } else { std::stringstream error; error << "Fatal Error: No location found for this ballonet." << endl; throw std::runtime_error(error.str()); } if ((el->FindElement("x_radius") || el->FindElement("x_width")) && (el->FindElement("y_radius") || el->FindElement("y_width")) && (el->FindElement("z_radius") || el->FindElement("z_width"))) { if (el->FindElement("x_radius")) { Xradius = el->FindElementValueAsNumberConvertTo("x_radius", "FT"); } if (el->FindElement("y_radius")) { Yradius = el->FindElementValueAsNumberConvertTo("y_radius", "FT"); } if (el->FindElement("z_radius")) { Zradius = el->FindElementValueAsNumberConvertTo("z_radius", "FT"); } if (el->FindElement("x_width")) { Xwidth = el->FindElementValueAsNumberConvertTo("x_width", "FT"); } if (el->FindElement("y_width")) { Ywidth = el->FindElementValueAsNumberConvertTo("y_width", "FT"); } if (el->FindElement("z_width")) { Zwidth = el->FindElementValueAsNumberConvertTo("z_width", "FT"); } // The volume is a (potentially) extruded ellipsoid. // FIXME: However, currently only a few combinations of radius and // width are fully supported. if ((Xradius != 0.0) && (Yradius != 0.0) && (Zradius != 0.0) && (Xwidth == 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) { // Ellipsoid volume. MaxVolume = 4.0 * M_PI * Xradius * Yradius * Zradius / 3.0; } else if ((Xradius == 0.0) && (Yradius != 0.0) && (Zradius != 0.0) && (Xwidth != 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) { // Cylindrical volume. MaxVolume = M_PI * Yradius * Zradius * Xwidth; } else { cerr << "Warning: Unsupported ballonet shape." << endl; MaxVolume = (4.0 * M_PI * Xradius * Yradius * Zradius / 3.0 + M_PI * Yradius * Zradius * Xwidth + M_PI * Xradius * Zradius * Ywidth + M_PI * Xradius * Yradius * Zwidth + 2.0 * Xradius * Ywidth * Zwidth + 2.0 * Yradius * Xwidth * Zwidth + 2.0 * Zradius * Xwidth * Ywidth + Xwidth * Ywidth * Zwidth); } } else { std::stringstream error; error << "Fatal Error: Ballonet shape must be given." << endl; throw std::runtime_error(error.str()); } if (el->FindElement("max_overpressure")) { MaxOverpressure = el->FindElementValueAsNumberConvertTo("max_overpressure", "LBS/FT2"); } if (el->FindElement("fullness")) { const double Fullness = el->FindElementValueAsNumber("fullness"); if (0 <= Fullness) { Volume = Fullness * MaxVolume; } else { cerr << "Warning: Invalid initial ballonet fullness value." << endl; } } if (el->FindElement("valve_coefficient")) { ValveCoefficient = el->FindElementValueAsNumberConvertTo("valve_coefficient", "FT4*SEC/SLUG"); ValveCoefficient = max(ValveCoefficient, 0.0); } // Initialize state if (Temperature == 0.0) { Temperature = Parent->GetTemperature(); } if (Pressure == 0.0) { Pressure = Parent->GetPressure(); } if (Volume != 0.0) { // Calculate initial air content. Contents = Pressure * Volume / (R * Temperature); // Clip to max allowed value. const double IdealPressure = Contents * R * Temperature / MaxVolume; if (IdealPressure > Pressure + MaxOverpressure) { Contents = (Pressure + MaxOverpressure) * MaxVolume / (R * Temperature); Pressure = Pressure + MaxOverpressure; } else { Pressure = max(IdealPressure, Pressure); } } else { // Calculate initial air content. Contents = Pressure * MaxVolume / (R * Temperature); } Volume = Contents * R * Temperature / Pressure; // Bind relevant properties string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("buoyant_forces/gas-cell", Parent->GetIndex()); base_property_name = CreateIndexedPropertyName(base_property_name + "/ballonet", CellNum); property_name = base_property_name + "/max_volume-ft3"; PropertyManager->Tie( property_name, &MaxVolume, false ); PropertyManager->GetNode()->SetWritable( property_name, false ); property_name = base_property_name + "/temp-R"; PropertyManager->Tie( property_name, &Temperature, false ); property_name = base_property_name + "/pressure-psf"; PropertyManager->Tie( property_name, &Pressure, false ); property_name = base_property_name + "/volume-ft3"; PropertyManager->Tie( property_name, &Volume, false ); property_name = base_property_name + "/contents-mol"; PropertyManager->Tie( property_name, &Contents, false ); property_name = base_property_name + "/valve_open"; PropertyManager->Tie( property_name, &ValveOpen, false ); Debug(0); // Read heat transfer coefficients if (Element* heat = el->FindElement("heat")) { Element* function_element = heat->FindElement("function"); while (function_element) { HeatTransferCoeff.push_back(new FGFunction(PropertyManager, function_element)); function_element = heat->FindNextElement("function"); } } // Read blower input function if (Element* blower = el->FindElement("blower_input")) { Element* function_element = blower->FindElement("function"); BlowerInput = new FGFunction(PropertyManager, function_element); } }
bool FGFDMExec::LoadModel(const string& model, bool addModelToPath) { string token; string aircraftCfgFileName; Element* element = 0L; bool result = false; // initialize result to false, indicating input file not yet read modelName = model; // Set the class modelName attribute if( AircraftPath.empty() || EnginePath.empty() || SystemsPath.empty()) { cerr << "Error: attempted to load aircraft with undefined "; cerr << "aircraft, engine, and system paths" << endl; return false; } FullAircraftPath = AircraftPath; if (addModelToPath) FullAircraftPath += "/" + model; aircraftCfgFileName = FullAircraftPath + "/" + model + ".xml"; if (modelLoaded) { DeAllocate(); Allocate(); } int saved_debug_lvl = debug_lvl; document = LoadXMLDocument(aircraftCfgFileName); // "document" is a class member if (document) { if (IsChild) debug_lvl = 0; ReadPrologue(document); if (IsChild) debug_lvl = saved_debug_lvl; // 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; } } if (IsChild) debug_lvl = 0; // Process the metrics element. This element is REQUIRED. element = document->FindElement("metrics"); if (element) { result = ((FGAircraft*)Models[eAircraft])->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 = ((FGMassBalance*)Models[eMassBalance])->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 = ((FGGroundReactions*)Models[eGroundReactions])->Load(element); if (!result) { cerr << endl << "Aircraft ground_reactions element has problems in file " << aircraftCfgFileName << endl; return result; } ((FGFCS*)Models[eSystems])->AddGear(((FGGroundReactions*)Models[eGroundReactions])->GetNumGearUnits()); } 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 = ((FGExternalReactions*)Models[eExternalReactions])->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 = ((FGBuoyantForces*)Models[eBuoyantForces])->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 = ((FGPropulsion*)Models[ePropulsion])->Load(element); if (!result) { cerr << endl << "Aircraft propulsion element has problems in file " << aircraftCfgFileName << endl; return result; } for (unsigned int i=0; i<((FGPropulsion*)Models[ePropulsion])->GetNumEngines(); i++) ((FGFCS*)Models[eSystems])->AddThrottle(); } // Process the system element[s]. This element is OPTIONAL, and there may be more than one. element = document->FindElement("system"); while (element) { result = ((FGFCS*)Models[eSystems])->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 = ((FGFCS*)Models[eSystems])->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 = ((FGFCS*)Models[eSystems])->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 = ((FGAerodynamics*)Models[eAerodynamics])->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 = ((FGInput*)Models[eInput])->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. unsigned int idx=0; typedef double (FGOutput::*iOPMF)(void) const; typedef int (FGFDMExec::*iOPV)(void) const; element = document->FindElement("output"); while (element) { if (debug_lvl > 0) cout << endl << " Output data set: " << idx << " "; FGOutput* Output = new FGOutput(this); Output->InitModel(); Schedule(Output); result = Output->Load(element); if (!result) { cerr << endl << "Aircraft output element has problems in file " << aircraftCfgFileName << endl; return result; } else { Outputs.push_back(Output); string outputProp = CreateIndexedPropertyName("simulation/output",idx); instance->Tie(outputProp+"/log_rate_hz", Output, (iOPMF)0, &FGOutput::SetRate, false); instance->Tie("simulation/force-output", this, (iOPV)0, &FGFDMExec::ForceOutput, false); idx++; } element = document->FindNextElement("output"); } // Lastly, process the child element. This element is OPTIONAL - and NOT YET SUPPORTED. element = document->FindElement("child"); if (element) { result = ReadChild(element); if (!result) { cerr << endl << "Aircraft child element has problems in file " << aircraftCfgFileName << endl; return result; } } // Since all vehicle characteristics have been loaded, place the values in the Inputs // structure for the FGModel-derived classes. LoadModelConstants(); modelLoaded = true; if (debug_lvl > 0) { LoadInputs(eMassBalance); // Update all input mass properties for the report. Models[eMassBalance]->Run(false); // Update all mass properties for the report. ((FGMassBalance*)Models[eMassBalance])->GetMassPropertiesReport(); cout << endl << fgblue << highint << "End of vehicle configuration loading." << endl << "-------------------------------------------------------------------------------" << reset << endl; } if (IsChild) debug_lvl = saved_debug_lvl; } else { cerr << fgred << " JSBSim failed to open the configuration file: " << aircraftCfgFileName << fgdef << endl; } for (unsigned int i=0; i< Models.size(); i++) LoadInputs(i); if (result) { struct PropertyCatalogStructure masterPCS; masterPCS.base_string = ""; masterPCS.node = (FGPropertyManager*)Root; BuildPropertyCatalog(&masterPCS); } return result; }
bool FGTurbine::Load(FGFDMExec* exec, Element *el) { Element* function_element = el->FindElement("function"); while(function_element) { string name = function_element->GetAttributeValue("name"); if (name == "IdleThrust" || name == "MilThrust" || name == "AugThrust" || name == "Injection") function_element->SetAttributeValue("name", string("propulsion/engine[#]/") + name); function_element = el->FindNextElement("function"); } FGEngine::Load(exec, el); ResetToIC(); if (el->FindElement("milthrust")) MilThrust = el->FindElementValueAsNumberConvertTo("milthrust","LBS"); if (el->FindElement("maxthrust")) MaxThrust = el->FindElementValueAsNumberConvertTo("maxthrust","LBS"); if (el->FindElement("bypassratio")) BypassRatio = el->FindElementValueAsNumber("bypassratio"); if (el->FindElement("bleed")) BleedDemand = el->FindElementValueAsNumber("bleed"); if (el->FindElement("tsfc")) TSFC = el->FindElementValueAsNumber("tsfc"); if (el->FindElement("atsfc")) ATSFC = el->FindElementValueAsNumber("atsfc"); if (el->FindElement("idlen1")) IdleN1 = el->FindElementValueAsNumber("idlen1"); if (el->FindElement("idlen2")) IdleN2 = el->FindElementValueAsNumber("idlen2"); if (el->FindElement("maxn1")) MaxN1 = el->FindElementValueAsNumber("maxn1"); if (el->FindElement("maxn2")) MaxN2 = el->FindElementValueAsNumber("maxn2"); if (el->FindElement("n1spinup")) N1_spinup = el->FindElementValueAsNumber("n1spinup"); if (el->FindElement("n2spinup")) N2_spinup = el->FindElementValueAsNumber("n2spinup"); if (el->FindElement("augmented")) Augmented = (int)el->FindElementValueAsNumber("augmented"); if (el->FindElement("augmethod")) AugMethod = (int)el->FindElementValueAsNumber("augmethod"); if (el->FindElement("injected")) Injected = (int)el->FindElementValueAsNumber("injected"); if (el->FindElement("injection-time")){ InjectionTime = el->FindElementValueAsNumber("injection-time"); InjWaterNorm =1.0; } if (el->FindElement("injection-N1-inc")) InjN1increment = el->FindElementValueAsNumber("injection-N1-inc"); if (el->FindElement("injection-N2-inc")) InjN2increment = el->FindElementValueAsNumber("injection-N2-inc"); string property_prefix = CreateIndexedPropertyName("propulsion/engine", EngineNumber); IdleThrustLookup = GetPreFunction(property_prefix+"/IdleThrust"); MilThrustLookup = GetPreFunction(property_prefix+"/MilThrust"); MaxThrustLookup = GetPreFunction(property_prefix+"/AugThrust"); InjectionLookup = GetPreFunction(property_prefix+"/Injection"); // Pre-calculations and initializations delay = 90.0 / (BypassRatio + 3.0); N1_factor = MaxN1 - IdleN1; N2_factor = MaxN2 - IdleN2; OilTemp_degK = in.TAT_c + 273.0; IdleFF = pow(MilThrust, 0.2) * 107.0; // just an estimate bindmodel(exec->GetPropertyManager()); return true; }
FGPropeller::FGPropeller(FGFDMExec* exec, Element* prop_element, int num) : FGThruster(exec, prop_element, num) { string token; Element *table_element, *local_element; string name=""; FGPropertyManager* PropertyManager = exec->GetPropertyManager(); MaxPitch = MinPitch = P_Factor = Pitch = Advance = MinRPM = MaxRPM = 0.0; Sense = 1; // default clockwise rotation ReversePitch = 0.0; Reversed = false; Feathered = false; Reverse_coef = 0.0; GearRatio = 1.0; CtFactor = CpFactor = 1.0; if (prop_element->FindElement("ixx")) Ixx = prop_element->FindElementValueAsNumberConvertTo("ixx", "SLUG*FT2"); if (prop_element->FindElement("diameter")) Diameter = prop_element->FindElementValueAsNumberConvertTo("diameter", "FT"); if (prop_element->FindElement("numblades")) numBlades = (int)prop_element->FindElementValueAsNumber("numblades"); if (prop_element->FindElement("gearratio")) GearRatio = prop_element->FindElementValueAsNumber("gearratio"); if (prop_element->FindElement("minpitch")) MinPitch = prop_element->FindElementValueAsNumber("minpitch"); if (prop_element->FindElement("maxpitch")) MaxPitch = prop_element->FindElementValueAsNumber("maxpitch"); if (prop_element->FindElement("minrpm")) MinRPM = prop_element->FindElementValueAsNumber("minrpm"); if (prop_element->FindElement("maxrpm")) MaxRPM = prop_element->FindElementValueAsNumber("maxrpm"); if (prop_element->FindElement("reversepitch")) ReversePitch = prop_element->FindElementValueAsNumber("reversepitch"); for (int i=0; i<2; i++) { table_element = prop_element->FindNextElement("table"); name = table_element->GetAttributeValue("name"); if (name == "C_THRUST") { cThrust = new FGTable(PropertyManager, table_element); } else if (name == "C_POWER") { cPower = new FGTable(PropertyManager, table_element); } else { cerr << "Unknown table type: " << name << " in propeller definition." << endl; } } local_element = prop_element->GetParent()->FindElement("sense"); if (local_element) { double Sense = local_element->GetDataAsNumber(); SetSense(fabs(Sense)/Sense); } local_element = prop_element->GetParent()->FindElement("p_factor"); if (local_element) { P_Factor = local_element->GetDataAsNumber(); } if (P_Factor < 0) { cerr << "P-Factor value in config file must be greater than zero" << endl; } if (prop_element->FindElement("ct_factor")) SetCtFactor( prop_element->FindElementValueAsNumber("ct_factor") ); if (prop_element->FindElement("cp_factor")) SetCpFactor( prop_element->FindElementValueAsNumber("cp_factor") ); Type = ttPropeller; RPM = 0; vTorque.InitMatrix(); D4 = Diameter*Diameter*Diameter*Diameter; D5 = D4*Diameter; string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNum); property_name = base_property_name + "/advance-ratio"; PropertyManager->Tie( property_name.c_str(), &J ); property_name = base_property_name + "/blade-angle"; PropertyManager->Tie( property_name.c_str(), &Pitch ); property_name = base_property_name + "/thrust-coefficient"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetThrustCoefficient ); property_name = base_property_name + "/propeller-rpm"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetRPM ); Debug(0); }
FGPiston::FGPiston(FGFDMExec* exec, Element* el, int engine_number, struct Inputs& input) : FGEngine(exec, el, engine_number, input), R_air(287.3), // Gas constant for air J/Kg/K rho_fuel(800), // estimate calorific_value_fuel(47.3e6), // J/Kg Cp_air(1005), // Specific heat (constant pressure) J/Kg/K Cp_fuel(1700), standard_pressure(101320.73) { Element *table_element; string token; string name=""; // Defaults and initializations Type = etPiston; // These items are read from the configuration file // Defaults are from a Lycoming O-360, more or less Cycles = 4; IdleRPM = 600; MaxRPM = 2800; Displacement = 360; SparkFailDrop = 1.0; MaxHP = 200; MinManifoldPressure_inHg = 6.5; MaxManifoldPressure_inHg = 28.5; ManifoldPressureLag=1.0; ISFC = -1; volumetric_efficiency = 0.85; Bore = 5.125; Stroke = 4.375; Cylinders = 4; CylinderHeadMass = 2; //kg CompressionRatio = 8.5; Z_airbox = -999; Ram_Air_Factor = 1; PeakMeanPistonSpeed_fps = 100; FMEPDynamic= 18400; FMEPStatic = 46500; Cooling_Factor = 0.5144444; StaticFriction_HP = 1.5; StarterGain = 1.; StarterTorque = -1.; StarterRPM = -1.; // These are internal program variables Lookup_Combustion_Efficiency = 0; Mixture_Efficiency_Correlation = 0; crank_counter = 0; Magnetos = 0; minMAP = 21950; maxMAP = 96250; ResetToIC(); // Supercharging BoostSpeeds = 0; // Default to no supercharging BoostSpeed = 0; Boosted = false; BoostOverride = 0; BoostManual = 0; bBoostOverride = false; bTakeoffBoost = false; TakeoffBoost = 0.0; // Default to no extra takeoff-boost int i; for (i=0; i<FG_MAX_BOOST_SPEEDS; i++) { RatedBoost[i] = 0.0; RatedPower[i] = 0.0; RatedAltitude[i] = 0.0; BoostMul[i] = 1.0; RatedMAP[i] = 100000; RatedRPM[i] = 2500; TakeoffMAP[i] = 100000; } for (i=0; i<FG_MAX_BOOST_SPEEDS-1; i++) { BoostSwitchAltitude[i] = 0.0; BoostSwitchPressure[i] = 0.0; } // Read inputs from engine data file where present. if (el->FindElement("minmp")) MinManifoldPressure_inHg = el->FindElementValueAsNumberConvertTo("minmp","INHG"); if (el->FindElement("maxmp")) MaxManifoldPressure_inHg = el->FindElementValueAsNumberConvertTo("maxmp","INHG"); if (el->FindElement("man-press-lag")) ManifoldPressureLag = el->FindElementValueAsNumber("man-press-lag"); if (el->FindElement("displacement")) Displacement = el->FindElementValueAsNumberConvertTo("displacement","IN3"); if (el->FindElement("maxhp")) MaxHP = el->FindElementValueAsNumberConvertTo("maxhp","HP"); if (el->FindElement("static-friction")) StaticFriction_HP = el->FindElementValueAsNumberConvertTo("static-friction","HP"); if (el->FindElement("sparkfaildrop")) SparkFailDrop = Constrain(0, 1 - el->FindElementValueAsNumber("sparkfaildrop"), 1); if (el->FindElement("cycles")) Cycles = el->FindElementValueAsNumber("cycles"); if (el->FindElement("idlerpm")) IdleRPM = el->FindElementValueAsNumber("idlerpm"); if (el->FindElement("maxrpm")) MaxRPM = el->FindElementValueAsNumber("maxrpm"); if (el->FindElement("maxthrottle")) MaxThrottle = el->FindElementValueAsNumber("maxthrottle"); if (el->FindElement("minthrottle")) MinThrottle = el->FindElementValueAsNumber("minthrottle"); if (el->FindElement("bsfc")) 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("cylinders")) Cylinders = el->FindElementValueAsNumber("cylinders"); if (el->FindElement("cylinder-head-mass")) CylinderHeadMass = el->FindElementValueAsNumberConvertTo("cylinder-head-mass","KG"); if (el->FindElement("air-intake-impedance-factor")) Z_airbox = el->FindElementValueAsNumber("air-intake-impedance-factor"); if (el->FindElement("ram-air-factor")) Ram_Air_Factor = el->FindElementValueAsNumber("ram-air-factor"); if (el->FindElement("cooling-factor")) Cooling_Factor = el->FindElementValueAsNumber("cooling-factor"); if (el->FindElement("starter-rpm")) StarterRPM = el->FindElementValueAsNumber("starter-rpm"); if (el->FindElement("starter-torque")) StarterTorque = el->FindElementValueAsNumber("starter-torque"); if (el->FindElement("dynamic-fmep")) FMEPDynamic= el->FindElementValueAsNumberConvertTo("dynamic-fmep","PA"); if (el->FindElement("static-fmep")) FMEPStatic = el->FindElementValueAsNumberConvertTo("static-fmep","PA"); if (el->FindElement("peak-piston-speed")) PeakMeanPistonSpeed_fps = el->FindElementValueAsNumber("peak-piston-speed"); if (el->FindElement("numboostspeeds")) { // Turbo- and super-charging parameters BoostSpeeds = (int)el->FindElementValueAsNumber("numboostspeeds"); if (el->FindElement("boostoverride")) BoostOverride = (int)el->FindElementValueAsNumber("boostoverride"); if (el->FindElement("boostmanual")) BoostManual = (int)el->FindElementValueAsNumber("boostmanual"); if (el->FindElement("takeoffboost")) TakeoffBoost = el->FindElementValueAsNumberConvertTo("takeoffboost", "PSI"); if (el->FindElement("ratedboost1")) RatedBoost[0] = el->FindElementValueAsNumberConvertTo("ratedboost1", "PSI"); if (el->FindElement("ratedboost2")) RatedBoost[1] = el->FindElementValueAsNumberConvertTo("ratedboost2", "PSI"); if (el->FindElement("ratedboost3")) RatedBoost[2] = el->FindElementValueAsNumberConvertTo("ratedboost3", "PSI"); if (el->FindElement("ratedpower1")) RatedPower[0] = el->FindElementValueAsNumberConvertTo("ratedpower1", "HP"); if (el->FindElement("ratedpower2")) RatedPower[1] = el->FindElementValueAsNumberConvertTo("ratedpower2", "HP"); if (el->FindElement("ratedpower3")) RatedPower[2] = el->FindElementValueAsNumberConvertTo("ratedpower3", "HP"); if (el->FindElement("ratedrpm1")) RatedRPM[0] = el->FindElementValueAsNumber("ratedrpm1"); if (el->FindElement("ratedrpm2")) RatedRPM[1] = el->FindElementValueAsNumber("ratedrpm2"); if (el->FindElement("ratedrpm3")) RatedRPM[2] = el->FindElementValueAsNumber("ratedrpm3"); if (el->FindElement("ratedaltitude1")) RatedAltitude[0] = el->FindElementValueAsNumberConvertTo("ratedaltitude1", "FT"); if (el->FindElement("ratedaltitude2")) RatedAltitude[1] = el->FindElementValueAsNumberConvertTo("ratedaltitude2", "FT"); if (el->FindElement("ratedaltitude3")) RatedAltitude[2] = el->FindElementValueAsNumberConvertTo("ratedaltitude3", "FT"); } while((table_element = el->FindNextElement("table")) != 0) { name = table_element->GetAttributeValue("name"); try { if (name == "COMBUSTION") { Lookup_Combustion_Efficiency = new FGTable(PropertyManager, table_element); } else if (name == "MIXTURE") { Mixture_Efficiency_Correlation = new FGTable(PropertyManager, table_element); } else { cerr << "Unknown table type: " << name << " in piston engine definition." << endl; } } catch (std::string str) { throw("Error loading piston engine table:" + name + ". " + str); } } volumetric_efficiency_reduced = volumetric_efficiency; if(StarterRPM < 0.) StarterRPM = 2*IdleRPM; if(StarterTorque < 0) StarterTorque = (MaxHP)*0.4; //just a wag. displacement_SI = Displacement * in3tom3; RatedMeanPistonSpeed_fps = ( MaxRPM * Stroke) / (360); // AKA 2 * (RPM/60) * ( Stroke / 12) or 2NS // Create IFSC to match the engine if not provided if (ISFC < 0) { double pmep = 29.92 - MaxManifoldPressure_inHg; pmep *= inhgtopa * volumetric_efficiency; double fmep = (FMEPDynamic * RatedMeanPistonSpeed_fps * fttom + FMEPStatic); double hp_loss = ((pmep + fmep) * displacement_SI * MaxRPM)/(Cycles*22371); ISFC = ( 1.1*Displacement * MaxRPM * volumetric_efficiency *(MaxManifoldPressure_inHg / 29.92) ) / (9411 * (MaxHP+hp_loss-StaticFriction_HP)); // cout <<"FMEP: "<< fmep <<" PMEP: "<< pmep << " hp_loss: " <<hp_loss <<endl; } if ( MaxManifoldPressure_inHg > 29.9 ) { // Don't allow boosting with a bogus number MaxManifoldPressure_inHg = 29.9; } minMAP = MinManifoldPressure_inHg * inhgtopa; // inHg to Pa maxMAP = MaxManifoldPressure_inHg * inhgtopa; // For throttle /* * Pm = ( Ze / ( Ze + Zi + Zt ) ) * Pa * Where: * Pm = Manifold Pressure * Pa = Ambient Pressre * Ze = engine impedance, Ze is effectively 1 / Mean Piston Speed * Zi = airbox impedance * Zt = throttle impedance * * For the calculation below throttle is fully open or Zt = 0 * * * */ if(Z_airbox < 0.0){ double Ze=PeakMeanPistonSpeed_fps/RatedMeanPistonSpeed_fps; // engine impedence Z_airbox = (standard_pressure *Ze / maxMAP) - Ze; // impedence of airbox } // Constant for Throttle impedence Z_throttle=(PeakMeanPistonSpeed_fps/((IdleRPM * Stroke) / 360))*(standard_pressure/minMAP - 1) - Z_airbox; // Z_throttle=(MaxRPM/IdleRPM )*(standard_pressure/minMAP+2); // Constant for Throttle impedence // Default tables if not provided in the configuration file if(Lookup_Combustion_Efficiency == 0) { // First column is thi, second is neta (combustion efficiency) Lookup_Combustion_Efficiency = new FGTable(12); *Lookup_Combustion_Efficiency << 0.00 << 0.980; *Lookup_Combustion_Efficiency << 0.90 << 0.980; *Lookup_Combustion_Efficiency << 1.00 << 0.970; *Lookup_Combustion_Efficiency << 1.05 << 0.950; *Lookup_Combustion_Efficiency << 1.10 << 0.900; *Lookup_Combustion_Efficiency << 1.15 << 0.850; *Lookup_Combustion_Efficiency << 1.20 << 0.790; *Lookup_Combustion_Efficiency << 1.30 << 0.700; *Lookup_Combustion_Efficiency << 1.40 << 0.630; *Lookup_Combustion_Efficiency << 1.50 << 0.570; *Lookup_Combustion_Efficiency << 1.60 << 0.525; *Lookup_Combustion_Efficiency << 2.00 << 0.345; } // First column is Fuel/Air Ratio, second is neta (mixture efficiency) if( Mixture_Efficiency_Correlation == 0) { Mixture_Efficiency_Correlation = new FGTable(15); *Mixture_Efficiency_Correlation << 0.05000 << 0.00000; *Mixture_Efficiency_Correlation << 0.05137 << 0.00862; *Mixture_Efficiency_Correlation << 0.05179 << 0.21552; *Mixture_Efficiency_Correlation << 0.05430 << 0.48276; *Mixture_Efficiency_Correlation << 0.05842 << 0.70690; *Mixture_Efficiency_Correlation << 0.06312 << 0.83621; *Mixture_Efficiency_Correlation << 0.06942 << 0.93103; *Mixture_Efficiency_Correlation << 0.07786 << 1.00000; *Mixture_Efficiency_Correlation << 0.08845 << 1.00000; *Mixture_Efficiency_Correlation << 0.09270 << 0.98276; *Mixture_Efficiency_Correlation << 0.10120 << 0.93103; *Mixture_Efficiency_Correlation << 0.11455 << 0.72414; *Mixture_Efficiency_Correlation << 0.12158 << 0.45690; *Mixture_Efficiency_Correlation << 0.12435 << 0.23276; *Mixture_Efficiency_Correlation << 0.12500 << 0.00000; } 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, &ISFC); property_name = base_property_name + "/starter-norm"; PropertyManager->Tie(property_name, &StarterGain); 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); property_name = base_property_name + "/air-intake-impedance-factor"; PropertyManager->Tie(property_name, &Z_airbox); property_name = base_property_name + "/ram-air-factor"; PropertyManager->Tie(property_name, &Ram_Air_Factor); property_name = base_property_name + "/cooling-factor"; PropertyManager->Tie(property_name, &Cooling_Factor); property_name = base_property_name + "/boost-speed"; PropertyManager->Tie(property_name, &BoostSpeed); property_name = base_property_name + "/cht-degF"; PropertyManager->Tie(property_name, this, &FGPiston::getCylinderHeadTemp_degF); property_name = base_property_name + "/oil-temperature-degF"; PropertyManager->Tie(property_name, this, &FGPiston::getOilTemp_degF); property_name = base_property_name + "/oil-pressure-psi"; PropertyManager->Tie(property_name, this, &FGPiston::getOilPressure_psi); property_name = base_property_name + "/egt-degF"; PropertyManager->Tie(property_name, this, &FGPiston::getExhaustGasTemp_degF); // Set up and sanity-check the turbo/supercharging configuration based on the input values. if (TakeoffBoost > RatedBoost[0]) bTakeoffBoost = true; for (i=0; i<BoostSpeeds; ++i) { bool bad = false; if (RatedBoost[i] <= 0.0) bad = true; if (RatedPower[i] <= 0.0) bad = true; if (RatedAltitude[i] < 0.0) bad = true; // 0.0 is deliberately allowed - this corresponds to unregulated supercharging. if (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] = GetStdPressure100K(BoostSwitchAltitude[i]) * psftopa; //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] = standard_pressure + RatedBoost[i] * 6895; // 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] / (GetStdPressure100K(RatedAltitude[i]) * psftopa); } if (BoostSpeeds > 0) { Boosted = true; BoostSpeed = 0; } bBoostOverride = (BoostOverride == 1 ? true : false); bBoostManual = (BoostManual == 1 ? true : false); Debug(0); // Call Debug() routine from constructor if needed }
FGPropeller::FGPropeller(FGFDMExec* exec, Element* prop_element, int num) : FGThruster(exec, prop_element, num) { string token; Element *table_element, *local_element; string name=""; FGPropertyManager* PropertyManager = exec->GetPropertyManager(); MaxPitch = MinPitch = P_Factor = Pitch = Advance = MinRPM = MaxRPM = 0.0; Sense = 1; // default clockwise rotation ReversePitch = 0.0; Reversed = false; Feathered = false; Reverse_coef = 0.0; GearRatio = 1.0; CtFactor = CpFactor = 1.0; ConstantSpeed = 0; cThrust = cPower = CtMach = CpMach = 0; Vinduced = 0.0; if (prop_element->FindElement("ixx")) Ixx = prop_element->FindElementValueAsNumberConvertTo("ixx", "SLUG*FT2"); if (prop_element->FindElement("diameter")) Diameter = prop_element->FindElementValueAsNumberConvertTo("diameter", "FT"); if (prop_element->FindElement("numblades")) numBlades = (int)prop_element->FindElementValueAsNumber("numblades"); if (prop_element->FindElement("gearratio")) GearRatio = prop_element->FindElementValueAsNumber("gearratio"); if (prop_element->FindElement("minpitch")) MinPitch = prop_element->FindElementValueAsNumber("minpitch"); if (prop_element->FindElement("maxpitch")) MaxPitch = prop_element->FindElementValueAsNumber("maxpitch"); if (prop_element->FindElement("minrpm")) MinRPM = prop_element->FindElementValueAsNumber("minrpm"); if (prop_element->FindElement("maxrpm")) { MaxRPM = prop_element->FindElementValueAsNumber("maxrpm"); ConstantSpeed = 1; } if (prop_element->FindElement("constspeed")) ConstantSpeed = (int)prop_element->FindElementValueAsNumber("constspeed"); if (prop_element->FindElement("reversepitch")) ReversePitch = prop_element->FindElementValueAsNumber("reversepitch"); while((table_element = prop_element->FindNextElement("table")) != 0) { name = table_element->GetAttributeValue("name"); try { if (name == "C_THRUST") { cThrust = new FGTable(PropertyManager, table_element); } else if (name == "C_POWER") { cPower = new FGTable(PropertyManager, table_element); } else if (name == "CT_MACH") { CtMach = new FGTable(PropertyManager, table_element); } else if (name == "CP_MACH") { CpMach = new FGTable(PropertyManager, table_element); } else { cerr << "Unknown table type: " << name << " in propeller definition." << endl; } } catch (std::string str) { throw("Error loading propeller table:" + name + ". " + str); } } if( (cPower == 0) || (cThrust == 0)){ cerr << "Propeller configuration must contain C_THRUST and C_POWER tables!" << endl; } local_element = prop_element->GetParent()->FindElement("sense"); if (local_element) { double Sense = local_element->GetDataAsNumber(); SetSense(Sense >= 0.0 ? 1.0 : -1.0); } local_element = prop_element->GetParent()->FindElement("p_factor"); if (local_element) { P_Factor = local_element->GetDataAsNumber(); } if (P_Factor < 0) { cerr << "P-Factor value in propeller configuration file must be greater than zero" << endl; } if (prop_element->FindElement("ct_factor")) SetCtFactor( prop_element->FindElementValueAsNumber("ct_factor") ); if (prop_element->FindElement("cp_factor")) SetCpFactor( prop_element->FindElementValueAsNumber("cp_factor") ); Type = ttPropeller; RPM = 0; vTorque.InitMatrix(); D4 = Diameter*Diameter*Diameter*Diameter; D5 = D4*Diameter; Pitch = MinPitch; string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNum); property_name = base_property_name + "/engine-rpm"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetEngineRPM ); property_name = base_property_name + "/advance-ratio"; PropertyManager->Tie( property_name.c_str(), &J ); property_name = base_property_name + "/blade-angle"; PropertyManager->Tie( property_name.c_str(), &Pitch ); property_name = base_property_name + "/thrust-coefficient"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetThrustCoefficient ); property_name = base_property_name + "/propeller-rpm"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetRPM ); property_name = base_property_name + "/helical-tip-Mach"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetHelicalTipMach ); property_name = base_property_name + "/constant-speed-mode"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetConstantSpeed, &FGPropeller::SetConstantSpeed ); property_name = base_property_name + "/prop-induced-velocity_fps"; PropertyManager->Tie( property_name.c_str(), this, &FGPropeller::GetInducedVelocity, &FGPropeller::SetInducedVelocity ); Debug(0); }
FGThruster::FGThruster(FGFDMExec *FDMExec, Element *el, int num ): FGForce(FDMExec) { Element* thruster_element = el->GetParent(); Element* element; FGColumnVector3 location, orientation, pointing; Type = ttDirect; SetTransformType(FGForce::tCustom); Name = el->GetAttributeValue("name"); GearRatio = 1.0; ReverserAngle = 0.0; Thrust = 0.0; EngineNum = num; PropertyManager = FDMExec->GetPropertyManager(); // Determine the initial location and orientation of this thruster and load the // thruster with this information. element = thruster_element->FindElement("location"); if (element) location = element->FindElementTripletConvertTo("IN"); else cerr << fgred << " No thruster location found." << reset << endl; SetLocation(location); string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNum); element = thruster_element->FindElement("pointing"); if (element) { // This defines a fixed nozzle that has no public interface property to gimbal or reverse it. pointing = element->FindElementTripletConvertTo("RAD"); // The specification of RAD here is superfluous, // and simply precludes a conversion. mT.InitMatrix(); mT(1,1) = pointing(1); mT(2,1) = pointing(2); mT(3,1) = pointing(3); } else { element = thruster_element->FindElement("orient"); if (element) orientation = element->FindElementTripletConvertTo("RAD"); SetAnglesToBody(orientation); property_name = base_property_name + "/pitch-angle-rad"; PropertyManager->Tie( property_name.c_str(), (FGForce *)this, &FGForce::GetPitch, &FGForce::SetPitch); property_name = base_property_name + "/yaw-angle-rad"; PropertyManager->Tie( property_name.c_str(), (FGForce *)this, &FGForce::GetYaw, &FGForce::SetYaw); if (el->GetName() == "direct") // this is a direct thruster. At this time // only a direct thruster can be reversed. { property_name = base_property_name + "/reverser-angle-rad"; PropertyManager->Tie( property_name.c_str(), (FGThruster *)this, &FGThruster::GetReverserAngle, &FGThruster::SetReverserAngle); } } Debug(0); }
FGTank::FGTank(FGFDMExec* exec, Element* el, int tank_number) : TankNumber(tank_number), Exec(exec) { string token, strFuelName; Element* element; Element* element_Grain; Area = 1.0; Density = 6.6; InitialTemperature = Temperature = -9999.0; Ixx = Iyy = Izz = 0.0; InertiaFactor = 1.0; Radius = Contents = Standpipe = Length = InnerRadius = 0.0; PreviousUsed = 0.0; ExternalFlow = 0.0; InitialStandpipe = 0.0; Capacity = 0.00001; Priority = InitialPriority = 1; PropertyManager = Exec->GetPropertyManager(); vXYZ.InitMatrix(); vXYZ_drain.InitMatrix(); type = el->GetAttributeValue("type"); if (type == "FUEL") Type = ttFUEL; else if (type == "OXIDIZER") Type = ttOXIDIZER; else Type = ttUNKNOWN; element = el->FindElement("location"); if (element) vXYZ = element->FindElementTripletConvertTo("IN"); else cerr << "No location found for this tank." << endl; vXYZ_drain = vXYZ; // Set initial drain location to initial tank CG element = el->FindElement("drain_location"); if (element) { vXYZ_drain = element->FindElementTripletConvertTo("IN"); } if (el->FindElement("radius")) Radius = el->FindElementValueAsNumberConvertTo("radius", "IN"); if (el->FindElement("inertia_factor")) InertiaFactor = el->FindElementValueAsNumber("inertia_factor"); if (el->FindElement("capacity")) Capacity = el->FindElementValueAsNumberConvertTo("capacity", "LBS"); if (el->FindElement("contents")) InitialContents = Contents = el->FindElementValueAsNumberConvertTo("contents", "LBS"); if (el->FindElement("temperature")) InitialTemperature = Temperature = el->FindElementValueAsNumber("temperature"); if (el->FindElement("standpipe")) InitialStandpipe = Standpipe = el->FindElementValueAsNumberConvertTo("standpipe", "LBS"); if (el->FindElement("priority")) InitialPriority = Priority = (int)el->FindElementValueAsNumber("priority"); if (el->FindElement("density")) Density = el->FindElementValueAsNumberConvertTo("density", "LBS/GAL"); if (el->FindElement("type")) strFuelName = el->FindElementValue("type"); SetPriority( InitialPriority ); // this will also set the Selected flag if (Capacity == 0) { cerr << "Tank capacity must not be zero. Reset to 0.00001 lbs!" << endl; Capacity = 0.00001; Contents = 0.0; } if (Contents > Capacity) { cerr << "Tank content (" << Contents << " lbs) is greater than tank capacity (" << Capacity << " lbs) for tank " << tank_number << "! Did you accidentally swap contents and capacity?" << endl; throw("tank definition error"); } PctFull = 100.0*Contents/Capacity; // percent full; 0 to 100.0 // Check whether this is a solid propellant "tank". Initialize it if true. grainType = gtUNKNOWN; // This is the default element_Grain = el->FindElement("grain_config"); if (element_Grain) { strGType = element_Grain->GetAttributeValue("type"); if (strGType == "CYLINDRICAL") grainType = gtCYLINDRICAL; else if (strGType == "ENDBURNING") grainType = gtENDBURNING; else cerr << "Unknown propellant grain type specified" << endl; if (element_Grain->FindElement("length")) Length = element_Grain->FindElementValueAsNumberConvertTo("length", "IN"); if (element_Grain->FindElement("bore_diameter")) InnerRadius = element_Grain->FindElementValueAsNumberConvertTo("bore_diameter", "IN")/2.0; // Initialize solid propellant values for debug and runtime use. switch (grainType) { case gtCYLINDRICAL: if (Radius <= InnerRadius) { cerr << "The bore diameter should be smaller than the total grain diameter!" << endl; exit(-1); } Volume = M_PI * Length * (Radius*Radius - InnerRadius*InnerRadius); // cubic inches break; case gtENDBURNING: Volume = M_PI * Length * Radius * Radius; // cubic inches break; case gtUNKNOWN: cerr << "Unknown grain type found in this rocket engine definition." << endl; exit(-1); } Density = (Contents*lbtoslug)/Volume; // slugs/in^3 } CalculateInertias(); string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/tank", TankNumber); property_name = base_property_name + "/contents-lbs"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetContents, &FGTank::SetContents ); property_name = base_property_name + "/pct-full"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetPctFull); property_name = base_property_name + "/priority"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetPriority, &FGTank::SetPriority ); property_name = base_property_name + "/external-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), (FGTank*)this, &FGTank::GetExternalFlow, &FGTank::SetExternalFlow ); if (Temperature != -9999.0) InitialTemperature = Temperature = FahrenheitToCelsius(Temperature); Area = 40.0 * pow(Capacity/1975, 0.666666667); // A named fuel type will override a previous density value if (!strFuelName.empty()) Density = ProcessFuelName(strFuelName); Debug(0); }
FGRocket::FGRocket(FGFDMExec* exec, Element *el, int engine_number, struct Inputs& input) : FGEngine(exec, el, engine_number, input), isp_function(0L) { Type = etRocket; Element* thrust_table_element = 0; ThrustTable = 0L; BurnTime = 0.0; previousFuelNeedPerTank = 0.0; previousOxiNeedPerTank = 0.0; PropellantFlowRate = 0.0; TotalPropellantExpended = 0.0; FuelFlowRate = FuelExpended = 0.0; OxidizerFlowRate = OxidizerExpended = 0.0; SLOxiFlowMax = SLFuelFlowMax = PropFlowMax = 0.0; MxR = 0.0; BuildupTime = 0.0; It = ItVac = 0.0; ThrustVariation = 0.0; TotalIspVariation = 0.0; VacThrust = 0.0; Flameout = false; // Defaults MinThrottle = 0.0; MaxThrottle = 1.0; string base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); std::stringstream strEngineNumber; strEngineNumber << EngineNumber; Element* isp_el = el->FindElement("isp"); Element* isp_func_el=0; bindmodel(); // Bind model properties first, since they might be needed in functions. // Specific impulse may be specified as a constant value or as a function - perhaps as a function of mixture ratio. if (isp_el) { isp_func_el = isp_el->FindElement("function"); if (isp_func_el) { isp_function = new FGFunction(exec->GetPropertyManager(),isp_func_el, strEngineNumber.str()); } else { Isp = el->FindElementValueAsNumber("isp"); } } else { throw("Specific Impulse <isp> must be specified for a rocket engine"); } if (el->FindElement("builduptime")) BuildupTime = el->FindElementValueAsNumber("builduptime"); if (el->FindElement("maxthrottle")) MaxThrottle = el->FindElementValueAsNumber("maxthrottle"); if (el->FindElement("minthrottle")) MinThrottle = el->FindElementValueAsNumber("minthrottle"); if (el->FindElement("slfuelflowmax")) { SLFuelFlowMax = el->FindElementValueAsNumberConvertTo("slfuelflowmax", "LBS/SEC"); if (el->FindElement("sloxiflowmax")) { SLOxiFlowMax = el->FindElementValueAsNumberConvertTo("sloxiflowmax", "LBS/SEC"); } PropFlowMax = SLOxiFlowMax + SLFuelFlowMax; MxR = SLOxiFlowMax/SLFuelFlowMax; } else if (el->FindElement("propflowmax")) { PropFlowMax = el->FindElementValueAsNumberConvertTo("propflowmax", "LBS/SEC"); // Mixture ratio may be specified here, but it can also be specified as a function or via property if (el->FindElement("mixtureratio")) { MxR = el->FindElementValueAsNumber("mixtureratio"); } } if (isp_function) Isp = isp_function->GetValue(); // cause Isp function to be executed if present. // If there is a thrust table element, this is a solid propellant engine. thrust_table_element = el->FindElement("thrust_table"); if (thrust_table_element) { ThrustTable = new FGTable(PropertyManager, thrust_table_element); Element* variation_element = el->FindElement("variation"); if (variation_element) { if (variation_element->FindElement("thrust")) { ThrustVariation = variation_element->FindElementValueAsNumber("thrust"); } if (variation_element->FindElement("total_isp")) { TotalIspVariation = variation_element->FindElementValueAsNumber("total_isp"); } } } Debug(0); }
FGGasCell::FGGasCell(FGFDMExec* exec, Element* el, unsigned int num, const struct Inputs& input) : FGForce(exec), in(input) { string token; Element* element; FGPropertyManager* PropertyManager = exec->GetPropertyManager(); MassBalance = exec->GetMassBalance(); gasCellJ = FGMatrix33(); gasCellM = FGColumnVector3(); Buoyancy = MaxVolume = MaxOverpressure = Temperature = Pressure = Contents = Volume = dVolumeIdeal = 0.0; Xradius = Yradius = Zradius = Xwidth = Ywidth = Zwidth = 0.0; ValveCoefficient = ValveOpen = 0.0; CellNum = num; // NOTE: In the local system X points north, Y points east and Z points down. SetTransformType(FGForce::tLocalBody); type = el->GetAttributeValue("type"); if (type == "HYDROGEN") Type = ttHYDROGEN; else if (type == "HELIUM") Type = ttHELIUM; else if (type == "AIR") Type = ttAIR; else Type = ttUNKNOWN; element = el->FindElement("location"); if (element) { vXYZ = element->FindElementTripletConvertTo("IN"); } else { std::stringstream error; error << "Fatal Error: No location found for this gas cell." << endl; throw std::runtime_error(error.str()); } if ((el->FindElement("x_radius") || el->FindElement("x_width")) && (el->FindElement("y_radius") || el->FindElement("y_width")) && (el->FindElement("z_radius") || el->FindElement("z_width"))) { if (el->FindElement("x_radius")) { Xradius = el->FindElementValueAsNumberConvertTo("x_radius", "FT"); } if (el->FindElement("y_radius")) { Yradius = el->FindElementValueAsNumberConvertTo("y_radius", "FT"); } if (el->FindElement("z_radius")) { Zradius = el->FindElementValueAsNumberConvertTo("z_radius", "FT"); } if (el->FindElement("x_width")) { Xwidth = el->FindElementValueAsNumberConvertTo("x_width", "FT"); } if (el->FindElement("y_width")) { Ywidth = el->FindElementValueAsNumberConvertTo("y_width", "FT"); } if (el->FindElement("z_width")) { Zwidth = el->FindElementValueAsNumberConvertTo("z_width", "FT"); } // The volume is a (potentially) extruded ellipsoid. // However, currently only a few combinations of radius and width are // fully supported. if ((Xradius != 0.0) && (Yradius != 0.0) && (Zradius != 0.0) && (Xwidth == 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) { // Ellipsoid volume. MaxVolume = 4.0 * M_PI * Xradius * Yradius * Zradius / 3.0; } else if ((Xradius == 0.0) && (Yradius != 0.0) && (Zradius != 0.0) && (Xwidth != 0.0) && (Ywidth == 0.0) && (Zwidth == 0.0)) { // Cylindrical volume. MaxVolume = M_PI * Yradius * Zradius * Xwidth; } else { cerr << "Warning: Unsupported gas cell shape." << endl; MaxVolume = (4.0 * M_PI * Xradius * Yradius * Zradius / 3.0 + M_PI * Yradius * Zradius * Xwidth + M_PI * Xradius * Zradius * Ywidth + M_PI * Xradius * Yradius * Zwidth + 2.0 * Xradius * Ywidth * Zwidth + 2.0 * Yradius * Xwidth * Zwidth + 2.0 * Zradius * Xwidth * Ywidth + Xwidth * Ywidth * Zwidth); } } else { std::stringstream error; error << "Fatal Error: Gas cell shape must be given." << endl; throw std::runtime_error(error.str()); } if (el->FindElement("max_overpressure")) { MaxOverpressure = el->FindElementValueAsNumberConvertTo("max_overpressure", "LBS/FT2"); } if (el->FindElement("fullness")) { const double Fullness = el->FindElementValueAsNumber("fullness"); if (0 <= Fullness) { Volume = Fullness * MaxVolume; } else { cerr << "Warning: Invalid initial gas cell fullness value." << endl; } } if (el->FindElement("valve_coefficient")) { ValveCoefficient = el->FindElementValueAsNumberConvertTo("valve_coefficient", "FT4*SEC/SLUG"); ValveCoefficient = max(ValveCoefficient, 0.0); } // Initialize state SetLocation(vXYZ); if (Temperature == 0.0) { Temperature = in.Temperature; } if (Pressure == 0.0) { Pressure = in.Pressure; } if (Volume != 0.0) { // Calculate initial gas content. Contents = Pressure * Volume / (R * Temperature); // Clip to max allowed value. const double IdealPressure = Contents * R * Temperature / MaxVolume; if (IdealPressure > Pressure + MaxOverpressure) { Contents = (Pressure + MaxOverpressure) * MaxVolume / (R * Temperature); Pressure = Pressure + MaxOverpressure; } else { Pressure = max(IdealPressure, Pressure); } } else { // Calculate initial gas content. Contents = Pressure * MaxVolume / (R * Temperature); } Volume = Contents * R * Temperature / Pressure; Mass = Contents * M_gas(); // Bind relevant properties string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("buoyant_forces/gas-cell", CellNum); property_name = base_property_name + "/max_volume-ft3"; PropertyManager->Tie( property_name.c_str(), &MaxVolume, false ); PropertyManager->GetNode()->SetWritable( property_name, false ); property_name = base_property_name + "/temp-R"; PropertyManager->Tie( property_name.c_str(), &Temperature, false ); property_name = base_property_name + "/pressure-psf"; PropertyManager->Tie( property_name.c_str(), &Pressure, false ); property_name = base_property_name + "/volume-ft3"; PropertyManager->Tie( property_name.c_str(), &Volume, false ); property_name = base_property_name + "/buoyancy-lbs"; PropertyManager->Tie( property_name.c_str(), &Buoyancy, false ); property_name = base_property_name + "/contents-mol"; PropertyManager->Tie( property_name.c_str(), &Contents, false ); property_name = base_property_name + "/valve_open"; PropertyManager->Tie( property_name.c_str(), &ValveOpen, false ); Debug(0); // Read heat transfer coefficients if (Element* heat = el->FindElement("heat")) { Element* function_element = heat->FindElement("function"); while (function_element) { HeatTransferCoeff.push_back(new FGFunction(PropertyManager, function_element)); function_element = heat->FindNextElement("function"); } } // Load ballonets if there are any if (Element* ballonet_element = el->FindElement("ballonet")) { while (ballonet_element) { Ballonet.push_back(new FGBallonet(exec, ballonet_element, Ballonet.size(), this, in)); ballonet_element = el->FindNextElement("ballonet"); } } }
FGEngine::FGEngine(FGFDMExec* exec, Element* engine_element, int engine_number, struct Inputs& input) : in(input), EngineNumber(engine_number) { Element* local_element; FGColumnVector3 location, orientation; Name = ""; Type = etUnknown; X = Y = Z = 0.0; EnginePitch = EngineYaw = 0.0; SLFuelFlowMax = 0.0; FuelExpended = 0.0; MaxThrottle = 1.0; MinThrottle = 0.0; ResetToIC(); // initialize dynamic terms FDMExec = exec; PropertyManager = FDMExec->GetPropertyManager(); Name = engine_element->GetAttributeValue("name"); Load(engine_element, PropertyManager, to_string(EngineNumber)); // Call ModelFunctions loader // Find and set engine location local_element = engine_element->GetParent()->FindElement("location"); if (local_element) location = local_element->FindElementTripletConvertTo("IN"); else cerr << "No engine location found for this engine." << endl; local_element = engine_element->GetParent()->FindElement("orient"); if (local_element) orientation = local_element->FindElementTripletConvertTo("RAD"); // else cerr << "No engine orientation found for this engine." << endl; // Jon: The engine orientation has a default and is not normally used. SetPlacement(location, orientation); // Load thruster local_element = engine_element->GetParent()->FindElement("thruster"); if (local_element) { try { if (!LoadThruster(local_element)) exit(-1); } catch (std::string str) { throw("Error loading engine " + Name + ". " + str); } } else { cerr << "No thruster definition supplied with engine definition." << endl; } // Load feed tank[s] references local_element = engine_element->GetParent()->FindElement("feed"); while (local_element) { int tankID = (int)local_element->GetDataAsNumber(); SourceTanks.push_back(tankID); local_element = engine_element->GetParent()->FindNextElement("feed"); } string property_name, base_property_name; base_property_name = CreateIndexedPropertyName("propulsion/engine", EngineNumber); property_name = base_property_name + "/set-running"; PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetRunning, &FGEngine::SetRunning ); property_name = base_property_name + "/thrust-lbs"; PropertyManager->Tie( property_name.c_str(), Thruster, &FGThruster::GetThrust); property_name = base_property_name + "/fuel-flow-rate-pps"; PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRate); property_name = base_property_name + "/fuel-flow-rate-gph"; PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelFlowRateGPH); property_name = base_property_name + "/fuel-used-lbs"; PropertyManager->Tie( property_name.c_str(), this, &FGEngine::GetFuelUsedLbs); PostLoad(engine_element, PropertyManager, to_string(EngineNumber)); Debug(0); }