rusefi/firmware/controllers/algo/fuel_math.cpp

507 lines
17 KiB
C++
Raw Normal View History

2015-07-10 06:01:56 -07:00
/**
* @file fuel_math.cpp
* @brief Fuel amount calculation logic
*
*
* @date May 27, 2013
2020-01-13 18:57:43 -08:00
* @author Andrey Belomutskiy, (c) 2012-2020
2015-07-10 06:01:56 -07:00
*
* This file is part of rusEfi - see http://rusefi.com
*
* rusEfi is free software; you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*
*/
2018-09-16 19:26:57 -07:00
#include "global.h"
#include "airmass.h"
2020-07-23 01:23:57 -07:00
#include "maf_airmass.h"
2020-07-24 19:30:12 -07:00
#include "speed_density_airmass.h"
2015-07-10 06:01:56 -07:00
#include "fuel_math.h"
#include "interpolation.h"
#include "engine_configuration.h"
#include "allsensors.h"
#include "engine_math.h"
#include "rpm_calculator.h"
#include "speed_density.h"
2019-10-13 13:14:08 -07:00
#include "perf_trace.h"
#include "sensor.h"
2020-07-22 13:11:07 -07:00
#include "speed_density_base.h"
2015-07-10 06:01:56 -07:00
EXTERN_ENGINE;
2015-07-10 06:01:56 -07:00
2016-08-26 16:02:56 -07:00
fuel_Map3D_t fuelMap("fuel");
2020-07-20 03:29:43 -07:00
fuel_Map3D_t fuelPhaseMap("fl ph");
extern fuel_Map3D_t veMap;
2016-07-01 16:01:44 -07:00
extern afr_Map3D_t afrMap;
2015-07-10 06:01:56 -07:00
extern baroCorr_Map3D_t baroCorrMap;
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL
2019-01-31 08:57:15 -08:00
2019-09-01 10:56:46 -07:00
DISPLAY_STATE(Engine)
DISPLAY(DISPLAY_FIELD(sparkDwell))
DISPLAY(DISPLAY_FIELD(dwellAngle))
DISPLAY(DISPLAY_FIELD(cltTimingCorrection))
DISPLAY_TEXT(eol);
DISPLAY(DISPLAY_IF(isCrankingState)) floatms_t getCrankingFuel3(
floatms_t baseFuel,
2019-08-26 20:41:04 -07:00
uint32_t revolutionCounterSinceStart DECLARE_ENGINE_PARAMETER_SUFFIX) {
// these magic constants are in Celsius
2020-02-18 07:47:39 -08:00
float baseCrankingFuel;
if (engineConfiguration->useRunningMathForCranking) {
baseCrankingFuel = baseFuel;
2020-02-18 07:47:39 -08:00
} else {
baseCrankingFuel = engineConfiguration->cranking.baseFuel;
}
2019-08-26 20:41:04 -07:00
/**
* Cranking fuel changes over time
*/
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(Duration_coef);
2019-08-26 20:41:04 -07:00
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(durationCoefficient) = interpolate2d("crank", revolutionCounterSinceStart, config->crankingCycleBins,
config->crankingCycleCoef);
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(eol);
2019-08-26 20:41:04 -07:00
/**
* Cranking fuel is different depending on engine coolant temperature
2020-06-18 05:54:02 -07:00
* If the sensor is failed, use 20 deg C
2019-08-26 20:41:04 -07:00
*/
2020-06-18 05:54:02 -07:00
auto clt = Sensor::get(SensorType::Clt);
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(Coolant_coef);
2020-06-18 05:54:02 -07:00
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(coolantTemperatureCoefficient) =
interpolate2d("crank", clt.value_or(20), config->crankingFuelBins, config->crankingFuelCoef);
2019-08-27 21:29:55 -07:00
DISPLAY_SENSOR(CLT);
DISPLAY_TEXT(eol);
2019-08-26 20:41:04 -07:00
auto tps = Sensor::get(SensorType::DriverThrottleIntent);
2019-08-26 20:41:04 -07:00
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(TPS_coef);
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(tpsCoefficient) = tps.Valid ? 1 : interpolate2d("crankTps", tps.Value, engineConfiguration->crankingTpsBins,
2019-08-26 20:41:04 -07:00
engineConfiguration->crankingTpsCoef);
2020-06-18 05:54:02 -07:00
/*
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(tpsCoefficient) =
tps.Valid
? interpolate2d("crankTps", tps.Value, engineConfiguration->crankingTpsBins, engineConfiguration->crankingTpsCoef)
: 1; // in case of failed TPS, don't correct.*/
2019-08-27 21:29:55 -07:00
DISPLAY_SENSOR(TPS);
DISPLAY_TEXT(eol);
2019-08-26 20:41:04 -07:00
2019-10-14 23:06:15 -07:00
floatms_t crankingFuel = baseCrankingFuel
2019-08-26 20:41:04 -07:00
* engine->engineState.cranking.durationCoefficient
* engine->engineState.cranking.coolantTemperatureCoefficient
* engine->engineState.cranking.tpsCoefficient;
2019-10-14 23:06:15 -07:00
DISPLAY_TEXT(Cranking_fuel);
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(fuel) = crankingFuel;
2019-08-26 20:41:04 -07:00
if (crankingFuel <= 0) {
warning(CUSTOM_ERR_ZERO_CRANKING_FUEL, "Cranking fuel value %f", crankingFuel);
}
return crankingFuel;
}
/* DISPLAY_ELSE */
floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
2019-10-13 13:14:08 -07:00
ScopePerf perf(PE::GetRunningFuel);
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(Base_fuel);
ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(baseFuel)) = baseFuel;
DISPLAY_TEXT(eol);
DISPLAY_TEXT(Intake_coef);
float iatCorrection = ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(intakeTemperatureCoefficient));
DISPLAY_SENSOR(IAT);
DISPLAY_TEXT(eol);
DISPLAY_TEXT(Coolant_coef);
float cltCorrection = ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(coolantTemperatureCoefficient));
DISPLAY_SENSOR(CLT);
DISPLAY_TEXT(eol);
DISPLAY_TEXT(Post_cranking_coef);
float postCrankingFuelCorrection = ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(postCrankingFuelCorrection));
DISPLAY_TEXT(eol);
2019-08-26 20:41:04 -07:00
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(iatCorrection), "NaN iatCorrection", 0);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(cltCorrection), "NaN cltCorrection", 0);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(postCrankingFuelCorrection), "NaN postCrankingFuelCorrection", 0);
floatms_t runningFuel = baseFuel * iatCorrection * cltCorrection * postCrankingFuelCorrection * ENGINE(engineState.running.pidCorrection);
2019-08-26 20:41:04 -07:00
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(runningFuel), "NaN runningFuel", 0);
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(eol);
DISPLAY_TEXT(Running_fuel);
ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(fuel)) = runningFuel;
DISPLAY_TEXT(eol);
2019-08-26 20:41:04 -07:00
2019-08-27 21:29:55 -07:00
DISPLAY_TEXT(Injector_lag);
DISPLAY(DISPLAY_PREFIX(running).DISPLAY_FIELD(injectorLag));
DISPLAY_SENSOR(VBATT);
2019-08-26 20:41:04 -07:00
return runningFuel;
}
/* DISPLAY_ENDIF */
2020-06-03 18:12:12 -07:00
constexpr float convertToGramsPerSecond(float ccPerMinute) {
float ccPerSecond = ccPerMinute / 60;
return ccPerSecond * 0.72f; // 0.72g/cc fuel density
}
/**
* @return per cylinder injection time, in seconds
*/
float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PARAMETER_SUFFIX) {
float gPerSec = convertToGramsPerSecond(CONFIG(injector.flow));
return airMass / (afr * gPerSec);
}
2020-07-24 19:30:12 -07:00
static SpeedDensityAirmass sdAirmass(veMap);
2020-07-23 01:23:57 -07:00
static MafAirmass mafAirmass(veMap);
AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
switch (CONFIG(fuelAlgorithm)) {
case LM_SPEED_DENSITY:
2020-07-24 19:30:12 -07:00
return sdAirmass.getAirmass(rpm);
//return getSpeedDensityAirmass(PASS_ENGINE_PARAMETER_SIGNATURE);
case LM_REAL_MAF: {
2020-07-23 01:23:57 -07:00
return mafAirmass.getAirmass(rpm);
} default:
firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm));
return {};
}
}
2017-11-06 19:29:39 -08:00
/**
* per-cylinder fuel amount
* todo: rename this method since it's now base+TPSaccel
*/
2017-05-15 20:28:49 -07:00
floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
2019-10-13 13:14:08 -07:00
ScopePerf perf(PE::GetBaseFuel);
2017-05-15 20:28:49 -07:00
floatms_t tpsAccelEnrich = ENGINE(tpsAccelEnrichment.getTpsEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE));
2018-07-25 20:30:00 -07:00
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
2017-04-12 15:38:18 -07:00
ENGINE(engineState.tpsAccelEnrich) = tpsAccelEnrich;
2015-07-10 06:01:56 -07:00
2017-04-12 19:59:59 -07:00
floatms_t baseFuel;
if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) || (engineConfiguration->fuelAlgorithm == LM_REAL_MAF)) {
// airmass modes - get airmass first, then convert to fuel
auto airmass = getAirmass(rpm PASS_ENGINE_PARAMETER_SUFFIX);
// The airmass mode will tell us how to look up AFR - use the provided Y axis value
float targetAfr = afrMap.getValue(rpm, airmass.EngineLoadPercent);
// TODO: surface airmass.EngineLoadPercent to tunerstudio for proper display
// Plop some state for others to read
ENGINE(engineState.targetAFR) = targetAfr;
ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass;
ENGINE(engineState.fuelingLoad) = airmass.EngineLoadPercent;
// TODO: independently selectable ignition load mode
ENGINE(engineState.ignitionLoad) = airmass.EngineLoadPercent;
baseFuel = getInjectionDurationForAirmass(airmass.CylinderAirmass, targetAfr PASS_ENGINE_PARAMETER_SUFFIX) * 1000;
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN baseFuel", 0);
2015-07-10 06:01:56 -07:00
} else {
float tps = Sensor::get(SensorType::Tps1).value_or(0);
ENGINE(engineState.fuelingLoad) = tps;
// TODO: independently selectable ignition load mode
ENGINE(engineState.ignitionLoad) = tps;
2020-07-21 14:13:29 -07:00
baseFuel = getBaseTableFuel(rpm, getEngineLoadT(PASS_ENGINE_PARAMETER_SIGNATURE));
2018-07-25 20:30:00 -07:00
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN bt baseFuel", 0);
2015-07-10 06:01:56 -07:00
}
2017-04-12 19:59:59 -07:00
engine->engineState.baseFuel = baseFuel;
2015-12-02 17:10:06 -08:00
2017-04-12 19:59:59 -07:00
return tpsAccelEnrich + baseFuel;
2015-07-10 06:01:56 -07:00
}
2020-07-20 23:11:48 -07:00
angle_t getInjectionOffset(float rpm, float load DECLARE_ENGINE_PARAMETER_SUFFIX) {
2018-01-01 15:59:50 -08:00
if (cisnan(rpm)) {
return 0; // error already reported
}
2020-07-20 23:11:48 -07:00
if (cisnan(load)) {
2017-12-03 12:30:42 -08:00
return 0; // error already reported
}
2020-07-20 23:11:48 -07:00
angle_t value = fuelPhaseMap.getValue(rpm, load);
2018-01-01 15:59:50 -08:00
if (cisnan(value)) {
2018-09-01 06:12:33 -07:00
// we could be here while resetting configuration for example
warning(CUSTOM_ERR_6569, "phase map not ready");
2018-01-01 15:59:50 -08:00
return 0;
}
angle_t result = value + CONFIG(extraInjectionOffset);
2018-07-23 18:38:05 -07:00
fixAngle(result, "inj offset#2", CUSTOM_ERR_6553);
2016-08-28 14:02:14 -07:00
return result;
2015-07-10 06:01:56 -07:00
}
/**
2017-11-06 19:29:39 -08:00
* Number of injections using each injector per engine cycle
2017-03-06 22:28:26 -08:00
* @see getNumberOfSparks
2015-07-10 06:01:56 -07:00
*/
2017-05-15 20:28:49 -07:00
int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
switch (mode) {
case IM_SIMULTANEOUS:
2017-11-06 16:00:30 -08:00
case IM_SINGLE_POINT:
2017-11-06 19:29:39 -08:00
return engineConfiguration->specs.cylindersCount;
2015-07-10 06:01:56 -07:00
case IM_BATCH:
2016-07-23 18:04:30 -07:00
return 2;
2017-11-06 19:29:39 -08:00
case IM_SEQUENTIAL:
return 1;
2015-07-10 06:01:56 -07:00
default:
2016-10-10 13:02:39 -07:00
firmwareError(CUSTOM_ERR_INVALID_INJECTION_MODE, "Unexpected injection_mode_e %d", mode);
2015-07-10 06:01:56 -07:00
return 1;
}
}
2017-03-06 22:28:26 -08:00
/**
2017-12-12 14:40:48 -08:00
* This is more like MOSFET duty cycle since durations include injector lag
2017-03-06 22:28:26 -08:00
* @see getCoilDutyCycle
*/
2017-05-15 20:28:49 -07:00
percent_t getInjectorDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
floatms_t totalInjectiorAmountPerCycle = ENGINE(injectionDuration) * getNumberOfInjections(engineConfiguration->injectionMode PASS_ENGINE_PARAMETER_SUFFIX);
2017-05-15 20:28:49 -07:00
floatms_t engineCycleDuration = getEngineCycleDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX);
2017-11-06 19:29:39 -08:00
return 100 * totalInjectiorAmountPerCycle / engineCycleDuration;
2015-07-10 06:01:56 -07:00
}
static floatms_t getFuel(bool isCranking, floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (isCranking) {
return getCrankingFuel(baseFuel PASS_ENGINE_PARAMETER_SUFFIX);
} else {
return getRunningFuel(baseFuel PASS_ENGINE_PARAMETER_SUFFIX);
}
}
2015-07-10 06:01:56 -07:00
/**
2015-08-23 19:01:55 -07:00
* @returns Length of each individual fuel injection, in milliseconds
2017-11-06 19:29:39 -08:00
* in case of single point injection mode the amount of fuel into all cylinders, otherwise the amount for one cylinder
2015-07-10 06:01:56 -07:00
*/
2017-05-15 20:28:49 -07:00
floatms_t getInjectionDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
2019-10-13 13:14:08 -07:00
ScopePerf perf(PE::GetInjectionDuration);
#if EFI_SHAFT_POSITION_INPUT
2017-07-06 05:49:55 -07:00
bool isCranking = ENGINE(rpmCalculator).isCranking(PASS_ENGINE_PARAMETER_SIGNATURE);
2020-07-11 05:05:35 -07:00
injection_mode_e mode = ENGINE(getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
2017-11-06 19:29:39 -08:00
int numberOfInjections = getNumberOfInjections(mode PASS_ENGINE_PARAMETER_SUFFIX);
if (numberOfInjections == 0) {
2017-04-19 19:03:14 -07:00
warning(CUSTOM_CONFIG_NOT_READY, "config not ready");
return 0; // we can end up here during configuration reset
}
// Always update base fuel - some cranking modes use it
floatms_t baseFuel = getBaseFuel(rpm PASS_ENGINE_PARAMETER_SUFFIX);
floatms_t fuelPerCycle = getFuel(isCranking, baseFuel PASS_ENGINE_PARAMETER_SUFFIX);
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(fuelPerCycle), "NaN fuelPerCycle", 0);
2017-11-06 19:29:39 -08:00
if (mode == IM_SINGLE_POINT) {
// here we convert per-cylinder fuel amount into total engine amount since the single injector serves all cylinders
fuelPerCycle *= engineConfiguration->specs.cylindersCount;
}
// Fuel cut-off isn't just 0 or 1, it can be tapered
fuelPerCycle *= ENGINE(engineState.fuelCutoffCorrection);
// If no fuel, don't add injector lag
if (fuelPerCycle == 0.0f)
return 0;
2017-11-06 19:29:39 -08:00
floatms_t theoreticalInjectionLength = fuelPerCycle / numberOfInjections;
2019-08-26 20:41:04 -07:00
floatms_t injectorLag = ENGINE(engineState.running.injectorLag);
2017-04-19 04:58:22 -07:00
if (cisnan(injectorLag)) {
2017-05-24 20:42:27 -07:00
warning(CUSTOM_ERR_INJECTOR_LAG, "injectorLag not ready");
2017-04-19 04:58:22 -07:00
return 0; // we can end up here during configuration reset
}
return theoreticalInjectionLength * engineConfiguration->globalFuelCorrection + injectorLag;
2019-01-31 14:55:23 -08:00
#else
return 0;
#endif
2015-07-10 06:01:56 -07:00
}
/**
* @brief Injector lag correction
* @param vBatt Battery voltage.
* @return Time in ms for injection opening time based on current battery voltage
*/
2017-05-15 20:28:49 -07:00
floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX) {
2015-07-10 06:01:56 -07:00
if (cisnan(vBatt)) {
2018-01-23 09:05:14 -08:00
warning(OBD_System_Voltage_Malfunction, "vBatt=%.2f", vBatt);
2016-07-24 20:02:52 -07:00
return 0;
2015-07-10 06:01:56 -07:00
}
return interpolate2d("lag", vBatt, engineConfiguration->injector.battLagCorrBins, engineConfiguration->injector.battLagCorr);
2015-07-10 06:01:56 -07:00
}
/**
* @brief Initialize fuel map data structure
* @note this method has nothing to do with fuel map VALUES - it's job
* is to prepare the fuel map data structure for 3d interpolation
*/
2019-01-10 20:48:05 -08:00
void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
2020-07-24 19:30:12 -07:00
INJECT_ENGINE_REFERENCE(&sdAirmass);
2020-07-23 01:23:57 -07:00
INJECT_ENGINE_REFERENCE(&mafAirmass);
2015-07-10 06:01:56 -07:00
fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins);
2019-06-10 12:45:18 -07:00
#if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT)
2015-07-10 06:01:56 -07:00
fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins);
2019-06-10 12:45:18 -07:00
#endif /* (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT) */
2015-07-10 06:01:56 -07:00
}
/**
* @brief Engine warm-up fuel correction.
*/
2017-05-15 20:28:49 -07:00
float getCltFuelCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
const auto [valid, clt] = Sensor::get(SensorType::Clt);
if (!valid)
2015-07-10 06:01:56 -07:00
return 1; // this error should be already reported somewhere else, let's just handle it
return interpolate2d("cltf", clt, config->cltFuelCorrBins, config->cltFuelCorr);
2015-07-10 06:01:56 -07:00
}
2017-05-15 20:28:49 -07:00
angle_t getCltTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
const auto [valid, clt] = Sensor::get(SensorType::Clt);
if (!valid)
2017-01-05 18:12:06 -08:00
return 0; // this error should be already reported somewhere else, let's just handle it
return interpolate2d("timc", clt, engineConfiguration->cltTimingBins, engineConfiguration->cltTimingExtra);
2017-01-05 18:12:06 -08:00
}
float getIatFuelCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
const auto [valid, iat] = Sensor::get(SensorType::Iat);
if (!valid)
2015-07-10 06:01:56 -07:00
return 1; // this error should be already reported somewhere else, let's just handle it
return interpolate2d("iatc", iat, config->iatFuelCorrBins, config->iatFuelCorr);
2015-07-10 06:01:56 -07:00
}
/**
* @brief Called from EngineState::periodicFastCallback to update the state.
* @note The returned value is float, not boolean - to implement taper (smoothed correction).
* @return Fuel duration correction for fuel cut-off control (ex. if coasting). No correction if 1.0
*/
float getFuelCutOffCorrection(efitick_t nowNt, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
// no corrections by default
float fuelCorr = 1.0f;
// coasting fuel cut-off correction
if (CONFIG(coastingFuelCutEnabled)) {
auto [tpsValid, tpsPos] = Sensor::get(SensorType::Tps1);
if (!tpsValid) {
return 1.0f;
}
const auto [cltValid, clt] = Sensor::get(SensorType::Clt);
if (!cltValid) {
return 1.0f;
}
float map = getMap(PASS_ENGINE_PARAMETER_SIGNATURE);
// gather events
bool mapDeactivate = (map >= CONFIG(coastingFuelCutMap));
bool tpsDeactivate = (tpsPos >= CONFIG(coastingFuelCutTps));
2019-11-04 19:52:37 -08:00
// If no CLT sensor (or broken), don't allow DFCO
bool cltDeactivate = clt < (float)CONFIG(coastingFuelCutClt);
bool rpmDeactivate = (rpm < CONFIG(coastingFuelCutRpmLow));
bool rpmActivate = (rpm > CONFIG(coastingFuelCutRpmHigh));
// state machine (coastingFuelCutStartTime is also used as a flag)
if (!mapDeactivate && !tpsDeactivate && !cltDeactivate && rpmActivate) {
ENGINE(engineState.coastingFuelCutStartTime) = nowNt;
} else if (mapDeactivate || tpsDeactivate || rpmDeactivate || cltDeactivate) {
ENGINE(engineState.coastingFuelCutStartTime) = 0;
}
// enable fuelcut?
if (ENGINE(engineState.coastingFuelCutStartTime) != 0) {
// todo: add taper - interpolate using (nowNt - coastingFuelCutStartTime)?
fuelCorr = 0.0f;
}
}
// todo: add other fuel cut-off checks here (possibly cutFuelOnHardLimit?)
return fuelCorr;
}
2015-07-10 06:01:56 -07:00
/**
* @return Fuel injection duration injection as specified in the fuel map, in milliseconds
*/
floatms_t getBaseTableFuel(int rpm, float engineLoad) {
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
2015-07-10 06:01:56 -07:00
if (cisnan(engineLoad)) {
2016-11-02 20:01:48 -07:00
warning(CUSTOM_NAN_ENGINE_LOAD_2, "NaN engine load");
2017-04-13 07:43:27 -07:00
return 0;
2015-07-10 06:01:56 -07:00
}
2017-04-13 05:28:41 -07:00
floatms_t result = fuelMap.getValue(rpm, engineLoad);
if (cisnan(result)) {
// result could be NaN in case of invalid table, like during initialization
result = 0;
2017-04-13 07:43:27 -07:00
warning(CUSTOM_ERR_FUEL_TABLE_NOT_READY, "baseFuel table not ready");
2017-04-13 05:28:41 -07:00
}
return result;
2019-01-31 14:55:23 -08:00
#else
return 0;
#endif
2015-07-10 06:01:56 -07:00
}
2017-05-15 20:28:49 -07:00
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
if (hasBaroSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
float correction = baroCorrMap.getValue(GET_RPM(), getBaroPressure(PASS_ENGINE_PARAMETER_SIGNATURE));
if (cisnan(correction) || correction < 0.01) {
warning(OBD_Barometric_Press_Circ_Range_Perf, "Invalid baro correction %f", correction);
return 1;
}
return correction;
2015-07-10 06:01:56 -07:00
} else {
return 1;
}
}
2019-04-12 19:07:03 -07:00
#if EFI_ENGINE_CONTROL
2015-07-10 06:01:56 -07:00
/**
* @return Duration of fuel injection while craning
*/
floatms_t getCrankingFuel(float baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) {
2020-06-18 05:54:02 -07:00
return getCrankingFuel3(baseFuel, engine->rpmCalculator.getRevolutionCounterSinceStart() PASS_ENGINE_PARAMETER_SUFFIX);
2015-07-10 06:01:56 -07:00
}
2020-05-05 05:01:40 -07:00
float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
float totalDisplacement = CONFIG(specs.displacement);
float cylDisplacement = totalDisplacement / CONFIG(specs.cylindersCount);
2020-07-22 13:11:07 -07:00
// Calculation of 100% VE air mass in g/cyl - 1 cylinder filling at 1.204/L
// 101.325kpa, 20C
return idealGasLaw(cylDisplacement, 101.325f, 273.15f + 20.0f);
2020-05-05 05:01:40 -07:00
}
2015-07-10 06:01:56 -07:00
#endif
2017-12-31 16:25:59 -08:00
float getFuelRate(floatms_t totalInjDuration, efitick_t timePeriod DECLARE_ENGINE_PARAMETER_SUFFIX) {
if (timePeriod <= 0.0f)
return 0.0f;
float timePeriodMs = (float)NT2US(timePeriod) / 1000.0f;
float fuelRate = totalInjDuration / timePeriodMs;
const float cc_min_to_L_h = 60.0f / 1000.0f;
return fuelRate * CONFIG(injector.flow) * cc_min_to_L_h;
2017-12-17 18:10:02 -08:00
}
2019-01-31 08:57:15 -08:00
#endif