// basically P = Q*w and Q_Engine + (-Q_Rotor) = J * dw/dt, J = Moment // void FGTransmission::Calculate(double EnginePower, double ThrusterTorque, double dt) { double coupling = 1.0, coupling_sq = 1.0; double fw_mult = 1.0; double d_omega = 0.0, engine_d_omega = 0.0, thruster_d_omega = 0.0; // relative changes double engine_omega = rpm_to_omega(EngineRPM); double safe_engine_omega = engine_omega < 1e-1 ? 1e-1 : engine_omega; double engine_torque = EnginePower / safe_engine_omega; double thruster_omega = rpm_to_omega(ThrusterRPM); double safe_thruster_omega = thruster_omega < 1e-1 ? 1e-1 : thruster_omega; engine_torque -= EngineFriction / safe_engine_omega; ThrusterTorque += Constrain(0.0, BrakeCtrlNorm, 1.0) * MaxBrakePower / safe_thruster_omega; // would the FWU release ? engine_d_omega = engine_torque/EngineMoment * dt; thruster_d_omega = - ThrusterTorque/ThrusterMoment * dt; if ( thruster_omega+thruster_d_omega > engine_omega+engine_d_omega ) { // don't drive the engine FreeWheelTransmission = 0.0; } else { FreeWheelTransmission = 1.0; } fw_mult = FreeWheelLag.execute(FreeWheelTransmission); coupling = fw_mult * Constrain(0.0, ClutchCtrlNorm, 1.0); if (coupling < 0.999999) { // are the separate calculations needed ? // assume linear transfer engine_d_omega = (engine_torque - ThrusterTorque*coupling)/(ThrusterMoment*coupling + EngineMoment) * dt; thruster_d_omega = (engine_torque*coupling - ThrusterTorque)/(ThrusterMoment + EngineMoment*coupling) * dt; EngineRPM += omega_to_rpm(engine_d_omega); ThrusterRPM += omega_to_rpm(thruster_d_omega); // simulate transit to static friction coupling_sq = coupling*coupling; EngineRPM = (1.0-coupling_sq) * EngineRPM + coupling_sq * 0.02 * (49.0*EngineRPM + ThrusterRPM); ThrusterRPM = (1.0-coupling_sq) * ThrusterRPM + coupling_sq * 0.02 * (EngineRPM + 49.0*ThrusterRPM); // enforce equal rpm if ( fabs(EngineRPM-ThrusterRPM) < 1e-3 ) { EngineRPM = ThrusterRPM = 0.5 * (EngineRPM+ThrusterRPM); } } else { d_omega = (engine_torque - ThrusterTorque)/(ThrusterMoment + EngineMoment) * dt; EngineRPM = ThrusterRPM += omega_to_rpm(d_omega); } // nothing will turn backward if (EngineRPM < 0.0 ) EngineRPM = 0.0; if (ThrusterRPM < 0.0 ) ThrusterRPM = 0.0; }
boolean VertexManip::Manipulating (Event& e) { Rubberband* r = GetRubberband(); if (r == nil) { return false; } if (e.eventType == MotionEvent) { Constrain(e); r->Track(e.x, e.y); } else if (e.eventType == DownEvent) { Constrain(e); if (e.button == LEFTMOUSE) { GetGrowingVertices()->AddVertex(e.x, e.y); _origx = e.x; _origy = e.y; } else if (e.button == MIDDLEMOUSE) { GetGrowingVertices()->AddVertex(e.x, e.y); return false; } else if (e.button == RIGHTMOUSE) { GetGrowingVertices()->RemoveVertex(); if (GetGrowingVertices()->Count()==0) return false; } } return true; }
void pokef_in3(t_pokef *x, long n) { if (n) x->l_chan = Constrain(n,1,4) - 1; else x->l_chan = 0; }
void Slider::Slide (Event& e) { IntCoord newleft, newbot, dummy; boolean control = e.control; Listen(allEvents); SlidingRect r(output, canvas, left, bottom, right, top, e.x, e.y); CalcLimits(e); do { switch (e.eventType) { case MotionEvent: e.target->GetRelative(e.x, e.y, this); Constrain(e); r.Track(e.x, e.y); if ((syncScroll && !control) || (!syncScroll && control)) { r.Erase(); r.GetCurrent(newleft, newbot, dummy, dummy); Move(ViewX(newleft - left), ViewY(newbot - bottom)); interactor->Adjust(*shown); } break; default: break; } Read(e); } while (e.eventType != UpEvent); r.GetCurrent(newleft, newbot, dummy, dummy); Move(ViewX(newleft - left), ViewY(newbot - bottom)); Listen(input); }
void TNSpline::Smooth() { for (int i = 0; i < 3; ++i) { SNSpline::Smooth(); Constrain(); } }
////////////////// // User let go of mouse: leave size-drag mode // void CSizerBar::OnLButtonUp(UINT nFlags, CPoint pt) { if (m_bDragging) { pt = Constrain(pt); // don't go outside constraints pt = Rectify(pt); // clip x or y CPoint ptDelta = pt-m_ptOriginal; // distance moved CancelDrag(); // cancel drag mode NotifyMoved(ptDelta); // notify parent } }
void DragManip::Grasp (Event& e) { _grasp_e = e; if (!_origPreset) { _origx = e.x; _origy = e.y; } Constrain(e); if (_r != nil) _r->Track(e.x, e.y); }
////////////////// // User moved mouse: erase old bar and draw in new position. XOR makes this // easy. Keep track of previous point. // void CSizerBar::OnMouseMove(UINT nFlags, CPoint pt) { if (m_bDragging) { DrawBar(); // erase old bar pt = Constrain(pt); // don't go outside constrained rect! pt = Rectify(pt); // clip x or y depending if horizontal or vert CPoint ptDelta = pt-m_ptPrevious; m_rcBar += ptDelta; // move bar... DrawBar(); // and draw m_ptPrevious = pt; // remember for next mousemove } }
void DFS(int x, int y, int n, int m, int *step) { matrix[y][x] = '#'; int nx, ny; for (int i = 0; i < 4; ++i) { nx = x + move[i][0]; ny = y + move[i][1]; if (Constrain(nx, ny, n, m) && matrix[ny][nx] == '.') { matrix[ny][nx] = '#'; ++(*step); DFS(nx, ny, n, m, step); } } }
boolean DragManip::Manipulating (Event& e) { if (_r == nil) { return false; } if (e.eventType == MotionEvent) { Constrain(e); _r->Track(e.x, e.y); } else if (e.eventType == UpEvent) { return false; } return true; }
bool wxCompositeShape::Recompute() { int noIterations = 0; bool changed = TRUE; while (changed && (noIterations < 500)) { changed = Constrain(); noIterations ++; } /* #ifdef wx_x if (changed) cerr << "Warning: constraint algorithm failed after 500 iterations.\n"; #endif */ return (!changed); }
hConstraint Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { return Constrain(Type::POINTS_COINCIDENT, ptA, ptB, Entity::NO_ENTITY, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false); }
bool FGTurboProp::Load(FGFDMExec* exec, Element *el) { MaxStartingTime = 999999; //very big timeout -> infinite Ielu_max_torque=-1; // ToDo: Need to make sure units are properly accounted for below. if (el->FindElement("milthrust")) MilThrust = el->FindElementValueAsNumberConvertTo("milthrust","LBS"); 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("betarangeend")) BetaRangeThrottleEnd = el->FindElementValueAsNumber("betarangeend")/100.0; BetaRangeThrottleEnd = Constrain(0.0, BetaRangeThrottleEnd, 0.99999); if (el->FindElement("reversemaxpower")) ReverseMaxPower = el->FindElementValueAsNumber("reversemaxpower")/100.0; if (el->FindElement("maxpower")) MaxPower = el->FindElementValueAsNumber("maxpower"); if (el->FindElement("idlefuelflow")) { cerr << el->ReadFrom() << "Note: 'idlefuelflow' is obsolete, " << "use the 'CombustionEfficiency_N1' table instead." << endl; } if (el->FindElement("psfc")) PSFC = el->FindElementValueAsNumber("psfc"); if (el->FindElement("n1idle_max_delay")) Idle_Max_Delay = el->FindElementValueAsNumber("n1idle_max_delay"); if (el->FindElement("maxstartingtime")) MaxStartingTime = el->FindElementValueAsNumber("maxstartingtime"); if (el->FindElement("startern1")) StarterN1 = el->FindElementValueAsNumber("startern1"); if (el->FindElement("ielumaxtorque")) Ielu_max_torque = el->FindElementValueAsNumber("ielumaxtorque"); if (el->FindElement("itt_delay")) ITT_Delay = el->FindElementValueAsNumber("itt_delay"); Element *table_element; string name; FGPropertyManager* PropertyManager = exec->GetPropertyManager(); while (true) { table_element = el->FindNextElement("table"); if (!table_element) break; name = table_element->GetAttributeValue("name"); if (name == "EnginePowerVC") { EnginePowerVC = new FGTable(PropertyManager, table_element); } else if (name == "EnginePowerRPM_N1") { EnginePowerRPM_N1 = new FGTable(PropertyManager, table_element); } else if (name == "ITT_N1") { ITT_N1 = new FGTable(PropertyManager, table_element); } else if (name == "CombustionEfficiency_N1") { CombustionEfficiency_N1 = new FGTable(PropertyManager, table_element); } else { cerr << el->ReadFrom() << "Unknown table type: " << name << " in turboprop definition." << endl; } } // Pre-calculations and initializations delay=1; N1_factor = MaxN1 - IdleN1; N2_factor = MaxN2 - IdleN2; OilTemp_degK = in.TAT_c + 273.0; // default table based on '9.333 - (N1)/12.0' approximation // gives 430%Fuel at 60%N1 if (! CombustionEfficiency_N1) { CombustionEfficiency_N1 = new FGTable(6); *CombustionEfficiency_N1 << 60.0 << 12.0/52.0; *CombustionEfficiency_N1 << 82.0 << 12.0/30.0; *CombustionEfficiency_N1 << 96.0 << 12.0/16.0; *CombustionEfficiency_N1 << 100.0 << 1.0; *CombustionEfficiency_N1 << 104.0 << 1.5; *CombustionEfficiency_N1 << 110.0 << 6.0; } return true; }
void FGAccelerations::CalculateFrictionForces(double dt) { vector<LagrangeMultiplier*>& multipliers = *in.MultipliersList; size_t n = multipliers.size(); vFrictionForces.InitMatrix(); vFrictionMoments.InitMatrix(); // If no gears are in contact with the ground then return if (!n) return; vector<double> a(n*n); // Will contain Jac*M^-1*Jac^T vector<double> rhs(n); // Assemble the linear system of equations for (unsigned int i=0; i < n; i++) { FGColumnVector3 U = multipliers[i]->ForceJacobian; FGColumnVector3 r = multipliers[i]->LeverArm; FGColumnVector3 v1 = U / in.Mass; FGColumnVector3 v2 = in.Jinv * (r*U); // Should be J^-T but J is symmetric and so is J^-1 for (unsigned int j=0; j < i; j++) a[i*n+j] = a[j*n+i]; // Takes advantage of the symmetry of Jac^T*M^-1*Jac for (unsigned int j=i; j < n; j++) { U = multipliers[j]->ForceJacobian; r = multipliers[j]->LeverArm; a[i*n+j] = DotProduct(U, v1 + v2*r); } } // Assemble the RHS member // Translation FGColumnVector3 vdot = vUVWdot; if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground vdot += (in.vUVW - in.Tec2b * in.TerrainVelocity) / dt; // Rotation FGColumnVector3 wdot = vPQRdot; if (dt > 0.) // Zeroes out the relative movement between the aircraft and the ground wdot += (in.vPQR - in.Tec2b * in.TerrainAngularVel) / dt; // Prepare the linear system for the Gauss-Seidel algorithm : // 1. Compute the right hand side member 'rhs' // 2. Divide every line of 'a' and 'rhs' by a[i,i]. This is in order to save // a division computation at each iteration of Gauss-Seidel. for (unsigned int i=0; i < n; i++) { double d = a[i*n+i]; FGColumnVector3 U = multipliers[i]->ForceJacobian; FGColumnVector3 r = multipliers[i]->LeverArm; rhs[i] = -DotProduct(U, vdot + wdot*r)/d; for (unsigned int j=0; j < n; j++) a[i*n+j] /= d; } // Resolve the Lagrange multipliers with the projected Gauss-Seidel method for (int iter=0; iter < 50; iter++) { double norm = 0.; for (unsigned int i=0; i < n; i++) { double lambda0 = multipliers[i]->value; double dlambda = rhs[i]; for (unsigned int j=0; j < n; j++) dlambda -= a[i*n+j]*multipliers[j]->value; multipliers[i]->value = Constrain(multipliers[i]->Min, lambda0+dlambda, multipliers[i]->Max); dlambda = multipliers[i]->value - lambda0; norm += fabs(dlambda); } if (norm < 1E-5) break; } // Calculate the total friction forces and moments for (unsigned int i=0; i< n; i++) { double lambda = multipliers[i]->value; FGColumnVector3 U = multipliers[i]->ForceJacobian; FGColumnVector3 r = multipliers[i]->LeverArm; FGColumnVector3 F = lambda * U; vFrictionForces += F; vFrictionMoments += r * F; } FGColumnVector3 accel = vFrictionForces / in.Mass; FGColumnVector3 omegadot = in.Jinv * vFrictionMoments; vBodyAccel += accel; vUVWdot += accel; vUVWidot += in.Tb2i * accel; vPQRdot += omegadot; vPQRidot += omegadot; }
void Constraint::ConstrainCoincident(hEntity ptA, hEntity ptB) { Constrain(POINTS_COINCIDENT, ptA, ptB, Entity::NO_ENTITY, Entity::NO_ENTITY, false, false); }
void Constraint::Constrain(int type, hEntity ptA, hEntity ptB, hEntity entityA){ Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, false, false); }
hConstraint Constraint::Constrain(Constraint::Type type, hEntity ptA, hEntity ptB, hEntity entityA){ return Constrain(type, ptA, ptB, entityA, Entity::NO_ENTITY, /*other=*/false, /*other2=*/false); }
// 1. read configuration and try to fill holes, ymmv // 2. calculate derived parameters and transforms void FGRotor::rotor::configure(int f, const rotor *xmain) { double estimate; const bool yell = true; const bool silent = false; flags = f; estimate = (xmain) ? 2.0 * xmain->Radius * 0.2 : 42.0; Radius = 0.5 * cnf_elem("diameter", estimate, "FT", yell); estimate = (xmain) ? xmain->BladeNum : 2.0; estimate = Constrain(1.0,estimate,4.0); BladeNum = (int) cnf_elem("numblades", estimate, yell); estimate = (xmain) ? - xmain->Radius * 1.05 - Radius : - 0.025 * Radius ; RelDistance_xhub = cnf_elem("xhub", estimate, "FT", yell); RelShift_yhub = cnf_elem("yhub", 0.0, "FT", silent); estimate = - 0.1 * Radius - 4.0; RelHeight_zhub = cnf_elem("zhub", estimate, "FT", yell); // make sure that v_tip (omega*r) is below 0.7mach ~ 750ft/s estimate = (750.0/Radius)/(2.0*M_PI) * 60.0; // 7160/Radius NominalRPM = cnf_elem("nominalrpm", estimate, yell); MinRPM = cnf_elem("minrpm", 1.0, silent); MinRPM = Constrain(1.0, MinRPM, NominalRPM-1.0); estimate = (xmain) ? 0.12 : 0.07; // guess solidity estimate = estimate * M_PI*Radius/BladeNum; BladeChord = cnf_elem("chord", estimate, "FT", yell); LiftCurveSlope = cnf_elem("liftcurveslope", 6.0, yell); // "1/RAD" estimate = sqr(BladeChord) * sqr(Radius) * 0.57; BladeFlappingMoment = cnf_elem("flappingmoment", estimate, "SLUG*FT2", yell); BladeFlappingMoment = Constrain(0.1, BladeFlappingMoment, 1e9); BladeTwist = cnf_elem("twist", -0.17, "RAD", yell); estimate = sqr(BladeChord) * BladeChord * 15.66; // might be really wrong! BladeMassMoment = cnf_elem("massmoment", estimate, yell); // slug-ft BladeMassMoment = Constrain(0.1, BladeMassMoment, 1e9); TipLossB = cnf_elem("tiplossfactor", 0.98, silent); estimate = 1.1 * BladeFlappingMoment * BladeNum; PolarMoment = cnf_elem("polarmoment", estimate, "SLUG*FT2", silent); PolarMoment = Constrain(0.1, PolarMoment, 1e9); InflowLag = cnf_elem("inflowlag", 0.2, silent); // fixme, depends on size estimate = (xmain) ? 0.0 : -0.06; ShaftTilt = cnf_elem("shafttilt", estimate, "RAD", silent); // ignore differences between teeter/hingeless/fully-articulated constructions estimate = 0.05 * Radius; HingeOffset = cnf_elem("hingeoffset", estimate, "FT", (xmain) ? silent : yell); CantAngleD3 = cnf_elem("cantangle", 0.0, "RAD", silent); // derived parameters // precalc often used powers R[0]=1.0; R[1]=Radius; R[2]=R[1]*R[1]; R[3]=R[2]*R[1]; R[4]=R[3]*R[1]; B[0]=1.0; B[1]=TipLossB; B[2]=B[1]*B[1]; B[3]=B[2]*B[1]; B[4]=B[3]*B[1]; B[5]=B[4]*B[1]; LockNumberByRho = LiftCurveSlope * BladeChord * R[4] / BladeFlappingMoment; solidity = BladeNum * BladeChord / (M_PI * Radius); // use simple orientations at the moment if (flags & eTail) { // axis parallel to Y_body theta_shaft = 0.0; // no tilt phi_shaft = 0.5*M_PI; // opposite direction if main rotor is spinning CW if (xmain && (xmain->flags & eRotCW) ) { phi_shaft = -phi_shaft; } } else { // more or less upright theta_shaft = ShaftTilt; phi_shaft = 0.0; } // setup Shaft-Body transforms, see /SH79/ eqn(17,18) double st = sin(theta_shaft); double ct = cos(theta_shaft); double sp = sin(phi_shaft); double cp = cos(phi_shaft); ShaftToBody.InitMatrix( ct, st*sp, st*cp, 0.0, cp, -sp, -st, ct*sp, ct*cp ); BodyToShaft = ShaftToBody.Inverse(); // misc defaults nu = 0.001; // help the flow solver by providing some moving molecules return; }
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 }
main(int argc, char *argv[]) { InitializeConstrainer(argc,argv); Constrain(); exit(0); }