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"
|
2020-06-06 04:25:19 -07:00
|
|
|
#include "airmass.h"
|
2020-07-25 01:14:35 -07:00
|
|
|
#include "alphan_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"
|
2020-08-10 21:41:03 -07:00
|
|
|
#include "fuel_computer.h"
|
2020-08-17 02:22:25 -07:00
|
|
|
#include "injector_model.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
#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"
|
2020-04-04 05:41:09 -07:00
|
|
|
#include "sensor.h"
|
2020-07-22 13:11:07 -07:00
|
|
|
#include "speed_density_base.h"
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2020-03-29 16:06:03 -07:00
|
|
|
EXTERN_ENGINE;
|
2015-07-10 06:01:56 -07:00
|
|
|
|
2020-07-20 03:29:43 -07:00
|
|
|
fuel_Map3D_t fuelPhaseMap("fl ph");
|
2020-04-13 22:21:48 -07:00
|
|
|
extern fuel_Map3D_t veMap;
|
2020-10-26 15:15:17 -07:00
|
|
|
extern lambda_Map3D_t lambdaMap;
|
2015-07-10 06:01:56 -07:00
|
|
|
extern baroCorr_Map3D_t baroCorrMap;
|
2021-01-31 14:19:06 -08:00
|
|
|
mapEstimate_Map3D_t mapEstimationTable("map est");
|
2015-07-10 06:01:56 -07:00
|
|
|
|
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)
|
|
|
|
|
2019-11-05 20:36:33 -08:00
|
|
|
DISPLAY(DISPLAY_FIELD(sparkDwell))
|
|
|
|
DISPLAY(DISPLAY_FIELD(dwellAngle))
|
|
|
|
DISPLAY(DISPLAY_FIELD(cltTimingCorrection))
|
|
|
|
DISPLAY_TEXT(eol);
|
|
|
|
|
2020-10-26 04:23:13 -07:00
|
|
|
DISPLAY(DISPLAY_IF(isCrankingState)) float getCrankingFuel3(
|
|
|
|
float 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) {
|
2020-06-17 14:15:04 -07:00
|
|
|
baseCrankingFuel = baseFuel;
|
2020-02-18 07:47:39 -08:00
|
|
|
} else {
|
2020-10-26 04:23:13 -07:00
|
|
|
// parameter is in milligrams, convert to grams
|
|
|
|
baseCrankingFuel = engineConfiguration->cranking.baseFuel * 0.001f;
|
2020-02-18 07:47:39 -08:00
|
|
|
}
|
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);
|
2021-02-16 06:32:16 -08:00
|
|
|
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(durationCoefficient) = interpolate2d(revolutionCounterSinceStart, config->crankingCycleBins,
|
2019-08-26 20:41:04 -07:00
|
|
|
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) =
|
2021-02-16 06:32:16 -08:00
|
|
|
interpolate2d(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
|
|
|
|
2020-04-04 05:41:09 -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);
|
2021-02-16 06:32:16 -08:00
|
|
|
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(tpsCoefficient) = tps.Valid ? 1 : interpolate2d(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
|
2021-02-16 06:32:16 -08:00
|
|
|
? interpolate2d(tps.Value, engineConfiguration->crankingTpsBins, engineConfiguration->crankingTpsCoef)
|
2020-06-18 05:54:02 -07:00
|
|
|
: 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);
|
2020-10-26 04:23:13 -07:00
|
|
|
engine->engineState.DISPLAY_PREFIX(cranking).DISPLAY_FIELD(fuel) = crankingFuel * 1000;
|
2019-10-14 23:06:15 -07:00
|
|
|
|
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);
|
|
|
|
|
2020-09-06 16:06:32 -07:00
|
|
|
float baroCorrection = ENGINE(engineState.baroCorrection);
|
|
|
|
|
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);
|
|
|
|
|
2020-09-06 16:06:32 -07:00
|
|
|
floatms_t runningFuel = baseFuel * baroCorrection * 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);
|
2020-10-26 04:23:13 -07:00
|
|
|
ENGINE(engineState.DISPLAY_PREFIX(running).DISPLAY_FIELD(fuel)) = runningFuel * 1000;
|
2019-08-27 21:29:55 -07:00
|
|
|
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 */
|
|
|
|
|
2021-01-31 14:19:06 -08:00
|
|
|
static SpeedDensityAirmass sdAirmass(veMap, mapEstimationTable);
|
2020-07-23 01:23:57 -07:00
|
|
|
static MafAirmass mafAirmass(veMap);
|
2020-07-25 01:14:35 -07:00
|
|
|
static AlphaNAirmass alphaNAirmass(veMap);
|
2020-07-23 01:23:57 -07:00
|
|
|
|
2020-07-24 19:38:48 -07:00
|
|
|
AirmassModelBase* getAirmassModel(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2020-06-06 04:25:19 -07:00
|
|
|
switch (CONFIG(fuelAlgorithm)) {
|
2020-07-24 19:38:48 -07:00
|
|
|
case LM_SPEED_DENSITY: return &sdAirmass;
|
|
|
|
case LM_REAL_MAF: return &mafAirmass;
|
2020-11-27 12:44:31 -08:00
|
|
|
case LM_ALPHA_N: return &alphaNAirmass;
|
2020-07-26 20:33:18 -07:00
|
|
|
#if EFI_UNIT_TEST
|
|
|
|
case LM_MOCK: return engine->mockAirmassModel;
|
|
|
|
#endif
|
2020-08-13 09:49:48 -07:00
|
|
|
default:
|
|
|
|
// this is a bad work-around for https://github.com/rusefi/rusefi/issues/1690 issue
|
|
|
|
warning(CUSTOM_ERR_ASSERT, "Invalid airmass mode %d", CONFIG(fuelAlgorithm));
|
|
|
|
return &sdAirmass;
|
|
|
|
/* todo: this should be the implementation
|
|
|
|
return nullptr;
|
|
|
|
*/
|
2020-06-06 04:25:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-26 04:23:13 -07:00
|
|
|
// Per-cylinder base fuel mass
|
|
|
|
static float getBaseFuelMass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-10-13 13:14:08 -07:00
|
|
|
ScopePerf perf(PE::GetBaseFuel);
|
|
|
|
|
2020-07-30 21:20:10 -07:00
|
|
|
// airmass modes - get airmass first, then convert to fuel
|
|
|
|
auto model = getAirmassModel(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, model != nullptr, "Invalid airmass mode", 0.0f);
|
2020-07-05 13:25:19 -07:00
|
|
|
|
2020-07-30 21:20:10 -07:00
|
|
|
auto airmass = model->getAirmass(rpm);
|
|
|
|
|
|
|
|
// Plop some state for others to read
|
|
|
|
ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass;
|
|
|
|
ENGINE(engineState.fuelingLoad) = airmass.EngineLoadPercent;
|
2020-10-09 14:16:49 -07:00
|
|
|
ENGINE(engineState.ignitionLoad) = getLoadOverride(airmass.EngineLoadPercent, CONFIG(ignOverrideMode) PASS_ENGINE_PARAMETER_SUFFIX);
|
2020-07-30 21:20:10 -07:00
|
|
|
|
2020-08-17 14:03:59 -07:00
|
|
|
float baseFuelMass = ENGINE(fuelComputer)->getCycleFuel(airmass.CylinderAirmass, rpm, airmass.EngineLoadPercent);
|
2020-10-23 17:25:47 -07:00
|
|
|
|
2020-10-24 21:12:05 -07:00
|
|
|
// Fudge it by the global correction factor
|
|
|
|
baseFuelMass *= CONFIG(globalFuelCorrection);
|
2020-09-30 17:19:51 -07:00
|
|
|
engine->engineState.baseFuel = baseFuelMass;
|
2015-12-02 17:10:06 -08:00
|
|
|
|
2020-10-26 11:04:22 -07:00
|
|
|
if (cisnan(baseFuelMass)) {
|
|
|
|
// todo: we should not have this here but https://github.com/rusefi/rusefi/issues/1690
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-10-26 04:23:13 -07:00
|
|
|
return baseFuelMass;
|
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-05 15:14:55 -07:00
|
|
|
|
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-05 15:14:55 -07:00
|
|
|
|
2020-07-20 23:11:48 -07:00
|
|
|
angle_t value = fuelPhaseMap.getValue(rpm, load);
|
2020-07-05 15:14:55 -07:00
|
|
|
|
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;
|
|
|
|
}
|
2020-07-05 15:14:55 -07:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-11 20:35:10 -07:00
|
|
|
float getInjectionModeDurationMultiplier(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
|
|
|
injection_mode_e mode = ENGINE(getCurrentInjectionMode(PASS_ENGINE_PARAMETER_SIGNATURE));
|
|
|
|
|
|
|
|
switch (mode) {
|
2020-08-12 22:18:56 -07:00
|
|
|
case IM_SIMULTANEOUS: {
|
|
|
|
auto cylCount = engineConfiguration->specs.cylindersCount;
|
|
|
|
|
|
|
|
if (cylCount == 0) {
|
|
|
|
// we can end up here during configuration reset
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1.0f / cylCount;
|
|
|
|
}
|
2020-08-11 20:35:10 -07:00
|
|
|
case IM_SEQUENTIAL:
|
|
|
|
case IM_SINGLE_POINT:
|
|
|
|
return 1;
|
|
|
|
case IM_BATCH:
|
|
|
|
return 0.5f;
|
|
|
|
default:
|
|
|
|
firmwareError(CUSTOM_ERR_INVALID_INJECTION_MODE, "Unexpected injection_mode_e %d", mode);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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) {
|
2019-10-16 20:02:24 -07:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2020-10-26 04:23:13 -07:00
|
|
|
static float getCycleFuelMass(bool isCranking, float baseFuelMass DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2020-06-17 14:15:04 -07:00
|
|
|
if (isCranking) {
|
2020-10-26 04:23:13 -07:00
|
|
|
return getCrankingFuel(baseFuelMass PASS_ENGINE_PARAMETER_SUFFIX);
|
2020-06-17 14:15:04 -07:00
|
|
|
} else {
|
2020-10-26 04:23:13 -07:00
|
|
|
return getRunningFuel(baseFuelMass PASS_ENGINE_PARAMETER_SUFFIX);
|
2020-06-17 14:15:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
2021-03-03 04:30:56 -08:00
|
|
|
floatms_t getInjectionMass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
|
2019-10-13 13:14:08 -07:00
|
|
|
ScopePerf perf(PE::GetInjectionDuration);
|
|
|
|
|
2020-02-26 15:16:35 -08:00
|
|
|
#if EFI_SHAFT_POSITION_INPUT
|
2020-06-17 14:15:04 -07:00
|
|
|
// Always update base fuel - some cranking modes use it
|
2020-10-26 04:23:13 -07:00
|
|
|
float baseFuelMass = getBaseFuelMass(rpm PASS_ENGINE_PARAMETER_SUFFIX);
|
2020-06-17 14:15:04 -07:00
|
|
|
|
2020-09-05 15:49:42 -07:00
|
|
|
bool isCranking = ENGINE(rpmCalculator).isCranking();
|
2020-10-26 04:23:13 -07:00
|
|
|
float cycleFuelMass = getCycleFuelMass(isCranking, baseFuelMass PASS_ENGINE_PARAMETER_SUFFIX);
|
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(cycleFuelMass), "NaN cycleFuelMass", 0);
|
2020-06-17 14:15:04 -07:00
|
|
|
|
2018-03-22 10:37:34 -07:00
|
|
|
// Fuel cut-off isn't just 0 or 1, it can be tapered
|
2020-10-26 04:23:13 -07:00
|
|
|
cycleFuelMass *= ENGINE(engineState.fuelCutoffCorrection);
|
2018-03-22 10:37:34 -07:00
|
|
|
|
2020-10-26 04:23:13 -07:00
|
|
|
float durationMultiplier = getInjectionModeDurationMultiplier(PASS_ENGINE_PARAMETER_SIGNATURE);
|
|
|
|
float injectionFuelMass = cycleFuelMass * durationMultiplier;
|
|
|
|
|
2021-03-03 04:30:56 -08:00
|
|
|
// Prepare injector flow rate & deadtime
|
2020-10-26 04:23:13 -07:00
|
|
|
ENGINE(injectorModel)->prepare();
|
|
|
|
|
|
|
|
floatms_t tpsAccelEnrich = ENGINE(tpsAccelEnrichment.getTpsEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE));
|
|
|
|
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
|
|
|
|
ENGINE(engineState.tpsAccelEnrich) = tpsAccelEnrich;
|
|
|
|
|
2021-03-03 04:30:56 -08:00
|
|
|
// For legacy reasons, the TPS accel table is in units of milliseconds, so we have to convert BACK to mass
|
|
|
|
float tpsAccelPerInjection = durationMultiplier * tpsAccelEnrich;
|
|
|
|
|
|
|
|
float tpsFuelMass = ENGINE(injectorModel)->getFuelMassForDuration(tpsAccelPerInjection);
|
|
|
|
|
|
|
|
return injectionFuelMass + tpsFuelMass;
|
2019-01-31 14:55:23 -08:00
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2020-10-26 15:15:17 -07:00
|
|
|
static FuelComputer fuelComputer(lambdaMap);
|
2020-08-17 14:03:59 -07:00
|
|
|
static InjectorModel injectorModel;
|
|
|
|
|
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);
|
2020-08-10 21:53:45 -07:00
|
|
|
INJECT_ENGINE_REFERENCE(&alphaNAirmass);
|
2020-08-17 02:22:25 -07:00
|
|
|
|
2020-08-10 21:53:45 -07:00
|
|
|
INJECT_ENGINE_REFERENCE(&fuelComputer);
|
2020-08-17 02:22:25 -07:00
|
|
|
INJECT_ENGINE_REFERENCE(&injectorModel);
|
2020-07-23 01:23:57 -07:00
|
|
|
|
2020-08-17 14:03:59 -07:00
|
|
|
ENGINE(fuelComputer) = &fuelComputer;
|
|
|
|
ENGINE(injectorModel) = &injectorModel;
|
|
|
|
|
2021-01-31 14:19:06 -08:00
|
|
|
mapEstimationTable.init(config->mapEstimateTable, config->mapEstimateTpsBins, config->mapEstimateRpmBins);
|
|
|
|
|
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) {
|
2020-04-18 15:45:30 -07:00
|
|
|
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
|
2020-04-18 15:45:30 -07:00
|
|
|
|
2021-02-16 06:32:16 -08:00
|
|
|
return interpolate2d(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) {
|
2020-04-18 15:45:30 -07:00
|
|
|
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
|
2020-04-18 15:45:30 -07:00
|
|
|
|
2021-02-16 06:32:16 -08:00
|
|
|
return interpolate2d(clt, engineConfiguration->cltTimingBins, engineConfiguration->cltTimingExtra);
|
2017-01-05 18:12:06 -08:00
|
|
|
}
|
|
|
|
|
2020-04-18 15:45:30 -07: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
|
2020-04-18 15:45:30 -07:00
|
|
|
|
2021-02-16 06:32:16 -08:00
|
|
|
return interpolate2d(iat, config->iatFuelCorrBins, config->iatFuelCorr);
|
2015-07-10 06:01:56 -07:00
|
|
|
}
|
|
|
|
|
2018-03-22 10:37:34 -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
|
2019-12-11 14:48:55 -08:00
|
|
|
if (CONFIG(coastingFuelCutEnabled)) {
|
2020-04-15 06:48:17 -07:00
|
|
|
auto [tpsValid, tpsPos] = Sensor::get(SensorType::Tps1);
|
|
|
|
if (!tpsValid) {
|
|
|
|
return 1.0f;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto [cltValid, clt] = Sensor::get(SensorType::Clt);
|
|
|
|
if (!cltValid) {
|
2020-04-05 06:11:25 -07:00
|
|
|
return 1.0f;
|
|
|
|
}
|
|
|
|
|
2021-01-02 16:13:10 -08:00
|
|
|
const auto [mapValid, map] = Sensor::get(SensorType::Map);
|
|
|
|
if (!mapValid) {
|
|
|
|
return 1.0f;
|
|
|
|
}
|
|
|
|
|
2018-03-22 10:37:34 -07:00
|
|
|
// gather events
|
2018-06-23 06:32:41 -07:00
|
|
|
bool mapDeactivate = (map >= CONFIG(coastingFuelCutMap));
|
2018-03-22 10:37:34 -07:00
|
|
|
bool tpsDeactivate = (tpsPos >= CONFIG(coastingFuelCutTps));
|
2019-11-04 19:52:37 -08:00
|
|
|
// If no CLT sensor (or broken), don't allow DFCO
|
2020-04-15 06:48:17 -07:00
|
|
|
bool cltDeactivate = clt < (float)CONFIG(coastingFuelCutClt);
|
2018-03-22 10:37:34 -07:00
|
|
|
bool rpmDeactivate = (rpm < CONFIG(coastingFuelCutRpmLow));
|
|
|
|
bool rpmActivate = (rpm > CONFIG(coastingFuelCutRpmHigh));
|
|
|
|
|
|
|
|
// state machine (coastingFuelCutStartTime is also used as a flag)
|
2018-06-23 06:32:41 -07:00
|
|
|
if (!mapDeactivate && !tpsDeactivate && !cltDeactivate && rpmActivate) {
|
2018-03-22 10:37:34 -07:00
|
|
|
ENGINE(engineState.coastingFuelCutStartTime) = nowNt;
|
2018-06-23 06:32:41 -07:00
|
|
|
} else if (mapDeactivate || tpsDeactivate || rpmDeactivate || cltDeactivate) {
|
2018-03-22 10:37:34 -07:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-05-15 20:28:49 -07:00
|
|
|
float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
|
2021-02-07 15:54:41 -08:00
|
|
|
if (Sensor::hasSensor(SensorType::BarometricPressure)) {
|
|
|
|
// Default to 1atm if failed
|
|
|
|
float pressure = Sensor::get(SensorType::BarometricPressure).value_or(101.325f);
|
|
|
|
|
|
|
|
float correction = baroCorrMap.getValue(GET_RPM(), pressure);
|
2019-09-29 06:58:29 -07:00
|
|
|
if (cisnan(correction) || correction < 0.01) {
|
|
|
|
warning(OBD_Barometric_Press_Circ_Range_Perf, "Invalid baro correction %f", correction);
|
|
|
|
return 1;
|
|
|
|
}
|
2021-02-07 15:54:41 -08:00
|
|
|
|
2019-09-29 06:58:29 -07:00
|
|
|
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
|
|
|
|
*/
|
2020-10-26 04:23:13 -07:00
|
|
|
float 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
|
2019-01-31 08:57:15 -08:00
|
|
|
#endif
|