virtual public void CreateEngine() { engineSolver = new EngineSolver(); }
/// <summary> /// Update method for piston engine /// </summary> /// <param name="solver"></param> /// <param name="shaftRPM"></param> /// <param name="deltaTime">the current delta time for updates</param> public void Update(EngineSolver solver, double shaftRPM, double deltaTime) { double pAmb = solver.p0 + _ramAir * solver.Q; _throttle = solver.throttle; _fuel = solver.ffFraction > 0d; _engineThrust = 0d; _status = "Windmilling"; bool canStart = _running = solver.running; // in precedence order, set running = false (highest priority message last) if (shaftRPM < 60d) // 60RPM min for engine running { _status = "Too Low RPM"; // status should never be seen because _running = false; // the starter motor bit at the end will happen // or another case will trump this. } if (_throttle == 0d || !canStart) { _status = "Magnetos Off"; _running = false; canStart = false; } if (!_fuel) { _status = "Out of Fuel"; _running = false; canStart = false; } if (!solver.oxygen || solver.rho <= 0d) { _status = "No Oxygen"; _running = false; canStart = false; } if (_running) { _status = "Nominal"; shaftRPM = Math.Max(shaftRPM, 30d); // 30RPM min when running to avoid NaNs // check if we need to switch boost modes double power; double MAP; float fuelRatio = FuelAirRatio(_mixture); double efficiency = mixtureEfficiency.Evaluate(fuelRatio); if (_boostSwitch > 0) { if (pAmb < _boostSwitch - 1000d && _boostMode < 1) { _boostMode++; } if (pAmb > _boostSwitch + 1000d && _boostMode > 0) { _boostMode--; } _chargeTarget = GetChargeTarget(shaftRPM, _boostMode); _charge = GetCharge(_chargeTarget, deltaTime); MAP = CalcMAP(pAmb, _boostMode, _charge); // Compute fuel flow _airFlow = GetAirflow(pAmb, solver.t0, solver.R_c, solver.gamma_c, shaftRPM, MAP); _fuelFlow = _airFlow * fuelRatio; power = _fuelFlow * efficiency * _bsfcRecip - _boostCosts[_boostMode]; } else // auto switch { // assume supercharger for now, so charge = target double target0 = GetChargeTarget(shaftRPM, 0); double target1 = GetChargeTarget(shaftRPM, 1); double charge0 = GetCharge(target0, deltaTime); double charge1 = GetCharge(target1, deltaTime); double MAP0 = CalcMAP(pAmb, 0, charge0); double MAP1 = CalcMAP(pAmb, 1, charge1); double airflow0 = GetAirflow(pAmb, solver.t0, solver.R_c, solver.gamma_c, shaftRPM, MAP0); double m_dot_fuel0 = airflow0 * fuelRatio; double power0 = m_dot_fuel0 * efficiency * _bsfcRecip - _boostCosts[0]; double airflow1 = GetAirflow(pAmb, solver.t0, solver.R_c, solver.gamma_c, shaftRPM, MAP1); double m_dot_fuel1 = airflow1 * fuelRatio; double power1 = m_dot_fuel1 * efficiency * _bsfcRecip - _boostCosts[1]; if (power0 >= power1) { MAP = MAP0; _chargeTarget = target0; _charge = charge0; power = power0; _airFlow = airflow0; _fuelFlow = m_dot_fuel0; _boostMode = 0; _chargeTemp = GetCAT(MAP, pAmb, solver.t0); // duplication of effort, but oh well, this needs to be done to reset _chargeTemp } else { MAP = MAP1; _chargeTarget = target1; _charge = charge1; power = power1; _airFlow = airflow1; _fuelFlow = m_dot_fuel1; _boostMode = 1; } } _mp = MAP; //_chargeTemp = GetCAT(MAP, pAmb, solver.t0); // duplication of effort, but oh well // The "boost" is the delta above ambient _boostPressure = _mp - pAmb; _power = power; _totalPower = _power + _boostCosts[_boostMode]; _egt = GetEGT(efficiency, solver.t0, solver.Cp_c); // Additional thrust from engine // FIXME now that we calculate EGT should use that. // heat from mixture, heat hack for over-RPM, multiply by relative manifold pressure double tmpRatio = shaftRPM / _rpm0; if (tmpRatio > 1d) { tmpRatio *= tmpRatio; } float mixRatio = (1.5f - _mixture); mixRatio *= mixRatio; mixRatio = (mixRatio + 0.2f) * 1.2f; double tempDelta = tmpRatio * mixRatio * _mp / _maxMP; // exhaust thrust, normalized to "10% HP in lbf" if (_exhaustThrust != 0d) { _netExhaustThrust = _exhaustThrust * (_totalPower * W2HP * 0.1d * LBFTON) * tempDelta; _engineThrust += _netExhaustThrust; } // Meredith Effect radiator thrust, scaled by Q and by how hot the engine is running and the ambient temperature // CTOK is there because tempdelta is expressed as a multiple of 0C-in-K (FIXME) if (_meredithEffect != 0d) { _netMeredithEffect = _meredithEffect * solver.Q * (tempDelta * 273.15d / solver.t0); _engineThrust += _netMeredithEffect; } //MonoBehaviour.print("Engine running: HP " + power * W2HP + "/" + _totalPower * W2HP + ", rpm " + shaftRPM + ", mp " + _mp + ", charge " + _charge + ", cat " + _chargeTemp + ", chargeRho " + _chargeDensity + ", egt " + _egt + ", fuel " + _fuelFlow + ", airflow " + _airFlow + ", effic " + efficiency); } else { _mp = solver.p0; _boostPressure = 0d; // assume a starter motor of 15% power if (canStart) { _status = "Starter On"; _power = _totalPower = 0.2d * _power0; _fuelFlow = _power * _bsfc * 0.2d; _airFlow = _fuelFlow * 10d; _mp = 0.15d * _maxMP; _egt = GetEGT(1d, solver.t0, solver.Cp_c); } else { _fuelFlow = _airFlow = _power = _totalPower = 0d; if (_egt > solver.t0 + 1d) { _egt += (solver.t0 - _egt) * 0.05d * deltaTime; } else { _egt = solver.t0; } } } // unused /* * // Oil temperature. * // Assume a linear variation between ~90degC at idle and ~120degC * // at full power. No attempt to correct for airflow over the * // engine is made. Make the time constant to attain target steady- * // state oil temp greater at engine off than on to reflect no * // circulation. Nothing fancy, but populates the guage with a * // plausible value. * double tau; // secs * if (_running) * { * _oilTempTarget = 363.0f + (30.0f * (power / _power0)); * tau = 600; * // Reduce tau linearly to 300 at max power * tau -= (power / _power0) * 300.0f; * } * else * { * _oilTempTarget = temp; * tau = 1500; * } * _dOilTempdt = (_oilTempTarget - _oilTemp) / tau;*/ }