Coasting Fuel Cut-off - Implementation (basic) (#585)

* Use getRpm() instead of rpmValue - needed for unit-tests

* Impl.

* Defaults

* Unit-tests
This commit is contained in:
andreika-git 2018-03-22 19:37:34 +02:00 committed by rusefi
parent e8def93d2e
commit 16c4f84d8d
9 changed files with 188 additions and 4 deletions

View File

@ -64,7 +64,7 @@ int MockAdcState::getMockAdcValue(int hwChannel) {
* See also periodicFastCallback
*/
void Engine::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
int rpm = rpmCalculator.rpmValue;
int rpm = rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
isEngineChartEnabled = CONFIG(isEngineChartEnabled) && rpm < CONFIG(engineSnifferRpmThreshold);
sensorChartMode = rpm < CONFIG(sensorSnifferRpmThreshold) ? boardConfiguration->sensorChartMode : SC_OFF;
@ -206,6 +206,8 @@ EngineState::EngineState() {
sparkDwell = mapAveragingDuration = 0;
totalLoggedBytes = injectionOffset = 0;
auxValveStart = auxValveEnd = 0;
fuelCutoffCorrection = 0;
coastingFuelCutStartTime = 0;
}
void EngineState::updateSlowSensors(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
@ -227,7 +229,7 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
}
updateAuxValves(PASS_ENGINE_PARAMETER_SIGNATURE);
int rpm = GET_RPM();
int rpm = ENGINE(rpmCalculator).getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
sparkDwell = getSparkDwell(rpm PASS_ENGINE_PARAMETER_SUFFIX);
dwellAngle = sparkDwell / getOneDegreeTimeMs(rpm);
if (hasAfrSensor(PASS_ENGINE_PARAMETER_SIGNATURE)) {
@ -258,6 +260,9 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
// update fuel consumption states
fuelConsumption.update(nowNt PASS_ENGINE_PARAMETER_SUFFIX);
// Fuel cut-off isn't just 0 or 1, it can be tapered
fuelCutoffCorrection = getFuelCutOffCorrection(nowNt, rpm PASS_ENGINE_PARAMETER_SUFFIX);
// post-cranking fuel enrichment.
// for compatibility reasons, apply only if the factor is greater than zero (0.01 margin used)
if (engineConfiguration->postCrankingFactor > 0.01f) {
@ -408,7 +413,7 @@ void Engine::watchdog() {
void Engine::checkShutdown() {
#if EFI_MAIN_RELAY_CONTROL || defined(__DOXYGEN__)
int rpm = rpmCalculator.rpmValue;
int rpm = rpmCalculator.getRpm();
const float vBattThreshold = 5.0f;
if (isValidRpm(rpm) && sensors.vBatt < vBattThreshold && stopEngineRequestTimeNt == 0) {
@ -449,7 +454,7 @@ void Engine::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
engineState.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
engine->m.beforeFuelCalc = GET_TIMESTAMP();
int rpm = rpmCalculator.rpmValue;
int rpm = rpmCalculator.getRpm(PASS_ENGINE_PARAMETER_SIGNATURE);
ENGINE(injectionDuration) = getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX);
engine->m.fuelCalcTime = GET_TIMESTAMP() - engine->m.beforeFuelCalc;

View File

@ -175,6 +175,8 @@ public:
float iatFuelCorrection;
float cltFuelCorrection;
float postCrankingFuelCorrection;
float fuelCutoffCorrection;
efitick_t coastingFuelCutStartTime;
/**
* injectorLag(VBatt)
*

View File

@ -487,6 +487,14 @@ static void setDefaultWarmupFuelEnrichment(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
setCurveValue(WARMUP_CLT_EXTRA_FUEL_CURVE, 70, 101);
}
static void setDefaultFuelCutParameters(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
boardConfiguration->coastingFuelCutEnabled = false;
engineConfiguration->coastingFuelCutRpmLow = 1300;
engineConfiguration->coastingFuelCutRpmHigh = 1500;
engineConfiguration->coastingFuelCutTps = 2;
engineConfiguration->coastingFuelCutClt = 30;
}
static void setDefaultCrankingSettings(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
setLinearCurve(engineConfiguration->crankingTpsCoef, CRANKING_CURVE_SIZE, 1, 1, 1);
setLinearCurve(engineConfiguration->crankingTpsBins, CRANKING_CURVE_SIZE, 0, 100, 1);
@ -663,6 +671,8 @@ void setDefaultConfiguration(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
setDefaultWarmupFuelEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE);
setDefaultFuelCutParameters(PASS_ENGINE_PARAMETER_SIGNATURE);
setMazdaMiataNbTpsTps(PASS_ENGINE_PARAMETER_SIGNATURE);
setConstantDwell(4 PASS_ENGINE_PARAMETER_SUFFIX); // 4ms is global default dwell

View File

@ -175,6 +175,12 @@ floatms_t getInjectionDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
// 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;
floatms_t theoreticalInjectionLength = fuelPerCycle / numberOfInjections;
floatms_t injectorLag = ENGINE(engineState.injectorLag);
if (cisnan(injectorLag)) {
@ -244,6 +250,42 @@ float getIatFuelCorrection(float iat DECLARE_ENGINE_PARAMETER_SUFFIX) {
return interpolate2d("iatc", iat, IAT_FUEL_CORRECTION_CURVE);
}
/**
* @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 (boardConfiguration->coastingFuelCutEnabled) {
percent_t tpsPos = getTPS(PASS_ENGINE_PARAMETER_SIGNATURE);
// gather events
bool tpsDeactivate = (tpsPos >= CONFIG(coastingFuelCutTps));
bool cltDeactivate = cisnan(engine->sensors.clt) ? false : (engine->sensors.clt < (float)CONFIG(coastingFuelCutClt));
bool rpmDeactivate = (rpm < CONFIG(coastingFuelCutRpmLow));
bool rpmActivate = (rpm > CONFIG(coastingFuelCutRpmHigh));
// state machine (coastingFuelCutStartTime is also used as a flag)
if (!tpsDeactivate && !cltDeactivate && rpmActivate) {
ENGINE(engineState.coastingFuelCutStartTime) = nowNt;
} else if (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;
}
/**
* @return Fuel injection duration injection as specified in the fuel map, in milliseconds
*/

View File

@ -31,6 +31,7 @@ angle_t getinjectionOffset(float rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
float getIatFuelCorrection(float iat DECLARE_ENGINE_PARAMETER_SUFFIX);
floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX);
float getCltFuelCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
float getFuelCutOffCorrection(efitick_t nowNt, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
angle_t getCltTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
floatms_t getCrankingFuel(DECLARE_ENGINE_PARAMETER_SIGNATURE);
floatms_t getCrankingFuel3(float coolantTemperature, uint32_t revolutionCounterSinceStart DECLARE_ENGINE_PARAMETER_SUFFIX);

View File

@ -20,6 +20,7 @@
#include "test_fuel_map.h"
#include "fuel_math.h"
#include "test_fuelCut.h"
#include "test_logic_expression.h"
#include "test_pid_auto.h"
#include "engine_configuration.h"
@ -80,6 +81,7 @@ int main(void) {
testGpsParser();
testMisc();
testFuelMap();
testFuelCut();
testEngineMath();
testIgnitionPlanning();
testSensors();

View File

@ -9,6 +9,7 @@ TEST_SRC_CPP = test_util.cpp \
test_idle_controller.cpp \
test_trigger_decoder.cpp \
test_fuel_map.cpp \
test_fuelCut.cpp \
engine_test_helper.cpp \
test_logic_expression.cpp \
test_speed_density.cpp \

107
unit_tests/test_fuelCut.cpp Normal file
View File

@ -0,0 +1,107 @@
/*
* test_fuelCut.cpp
*
* Created on: Mar 22, 2018
*/
#include "engine_math.h"
#include "test_fuelCut.h"
#include "test_trigger_decoder.h"
#include "event_queue.h"
#include "unit_test_framework.h"
#include "tps.h"
extern EventQueue schedulingQueue;
extern int timeNowUs;
extern EnginePins enginePins;
void testCoastingFuelCut() {
// this is just a reference unit test implementation
printf("*************************************************** testCoastingFuelCut\r\n");
EngineTestHelper eth(TEST_ENGINE);
EXPAND_EngineTestHelper
// configure coastingFuelCut
engineConfiguration->bc.coastingFuelCutEnabled = true;
engineConfiguration->coastingFuelCutRpmLow = 1300;
engineConfiguration->coastingFuelCutRpmHigh = 1500;
engineConfiguration->coastingFuelCutTps = 2;
engineConfiguration->coastingFuelCutClt = 30;
// set cranking threshold
engineConfiguration->cranking.rpm = 999;
// configure TPS
eth.engine.engineConfiguration->tpsMin = 0;
eth.engine.engineConfiguration->tpsMax = 10;
// basic engine setup
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);
// mock CLT - just above threshold ('hot engine')
float hotClt = engine->sensors.clt = engineConfiguration->coastingFuelCutClt + 1;
// mock TPS - throttle is opened
setMockTpsPosition(6);
// set 'running' RPM - just above RpmHigh threshold
engine->rpmCalculator.mockRpm = engineConfiguration->coastingFuelCutRpmHigh + 1;
// 'advance' time (amount doesn't matter)
timeNowUs += 1000;
const float normalInjDuration = 1.5f;
/*
* We need to pass through all rpm changes (high-mid-low-mid-high) because of state-machine
*/
// process
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// this is normal injection mode (the throttle is opened), no fuel cut-off
assertEqualsM("inj dur#1 norm", normalInjDuration, ENGINE(injectionDuration));
// 'releasing' the throttle
setMockTpsPosition(0);
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off is enabled now
assertEqualsM("inj dur#2 cut", 0.0f, ENGINE(injectionDuration));
// Now drop the CLT below threshold
engine->sensors.clt = engineConfiguration->coastingFuelCutClt - 1;
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off should be diactivated - the engine is 'cold'
assertEqualsM("inj dur#3 clt", normalInjDuration, ENGINE(injectionDuration));
// restore CLT
engine->sensors.clt = hotClt;
// And set RPM - somewhere between RpmHigh and RpmLow threshold
engine->rpmCalculator.mockRpm = (engineConfiguration->coastingFuelCutRpmHigh + engineConfiguration->coastingFuelCutRpmLow) / 2;
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off is enabled - nothing should change
assertEqualsM("inj dur#4 mid", normalInjDuration, ENGINE(injectionDuration));
// Now drop RPM just below RpmLow threshold
engine->rpmCalculator.mockRpm = engineConfiguration->coastingFuelCutRpmLow - 1;
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off is now disabled (the engine is idling)
assertEqualsM("inj dur#5 idle", normalInjDuration, ENGINE(injectionDuration));
// Now change RPM just below RpmHigh threshold
engine->rpmCalculator.mockRpm = engineConfiguration->coastingFuelCutRpmHigh - 1;
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off is still disabled
assertEqualsM("inj dur#6 mid", normalInjDuration, ENGINE(injectionDuration));
// Now set RPM just above RpmHigh threshold
engine->rpmCalculator.mockRpm = engineConfiguration->coastingFuelCutRpmHigh + 1;
eth.engine.periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Fuel cut-off is active again!
assertEqualsM("inj dur#7 cut", 0.0f, ENGINE(injectionDuration));
}
void testFuelCut() {
testCoastingFuelCut();
}

14
unit_tests/test_fuelCut.h Normal file
View File

@ -0,0 +1,14 @@
/*
* test_fuelCut.h
*
* Created on: Mar 22, 2018
*/
#ifndef TEST_FUELCUT_H_
#define TEST_FUELCUT_H_
#include "main.h"
void testFuelCut();
#endif /* TEST_FUELCUT_H_ */