From 08d2aee17b51739836ebdf5b22cb87c58ec6c6f9 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Thu, 25 Aug 2022 18:26:17 -0700 Subject: [PATCH] dfco delay (#4482) * dfco delay * config * using a function that doesn't exist? who does that * test new behavior * clearer test --- firmware/controllers/algo/fuel/dfco.cpp | 10 ++- firmware/controllers/algo/fuel/dfco.h | 1 + firmware/integration/rusefi_config.txt | 4 +- firmware/tunerstudio/rusefi.input | 1 + .../tests/ignition_injection/test_fuelCut.cpp | 78 ++++++++++++++++++- 5 files changed, 88 insertions(+), 6 deletions(-) diff --git a/firmware/controllers/algo/fuel/dfco.cpp b/firmware/controllers/algo/fuel/dfco.cpp index 8b4183e507..09254893f2 100644 --- a/firmware/controllers/algo/fuel/dfco.cpp +++ b/firmware/controllers/algo/fuel/dfco.cpp @@ -51,13 +51,21 @@ void DfcoController::update() { // If fuel is cut, reset the timer if (newState) { m_timeSinceCut.reset(); + } else { + // If fuel is not cut, reset the not-cut timer + m_timeSinceNoCut.reset(); } m_isDfco = newState; } bool DfcoController::cutFuel() const { - return m_isDfco; + float cutDelay = engineConfiguration->dfcoDelay; + + // 0 delay means cut immediately, aka timer has always expired + bool hasBeenDelay = (cutDelay == 0) || m_timeSinceNoCut.hasElapsedSec(cutDelay); + + return m_isDfco && hasBeenDelay; } float DfcoController::getTimeSinceCut() const { diff --git a/firmware/controllers/algo/fuel/dfco.h b/firmware/controllers/algo/fuel/dfco.h index 0c9047633a..5375f7bf59 100644 --- a/firmware/controllers/algo/fuel/dfco.h +++ b/firmware/controllers/algo/fuel/dfco.h @@ -14,4 +14,5 @@ private: bool m_isDfco = false; Timer m_timeSinceCut; + Timer m_timeSinceNoCut; }; diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 490aa94c54..e15ddcecc9 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -1429,7 +1429,9 @@ tChargeMode_e tChargeMode; uint8_t autoscale maxCamPhaseResolveRpm;Below this RPM, use camshaft information to synchronize the crank's position for full sequential operation. Use this if your cam sensor does weird things at high RPM. Set to 0 to disable, and always use cam to help sync crank.;"rpm", 50, 0, 0, 12500, 0 - int8_t[11] unused4080;;"", 1, 0, 0, 0, 0 + uint8_t autoscale dfcoDelay;Delay before cutting fuel. Set to 0 to cut immediately with no delay. May cause rumbles and pops out of your exhaust...;"sec", 0.1, 0, 0, 10, 1 + + int8_t[10] unused4080;;"", 1, 0, 0, 0, 0 ! Someday there will be a 6th option for BMW S55 that uses a separate shaft just for HPFP #define hpfp_cam_e_enum "NONE", "Intake 1", "Exhaust 1", "Intake 2", "Exhaust 2" diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index cce42974d6..5144d270ca 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -3886,6 +3886,7 @@ dialog = tcuControls, "Transmission Settings" field = "Vehicle speed restore below", coastingFuelCutVssLow, {coastingFuelCutEnabled == 1} field = "Cut fuel below TPS", coastingFuelCutTps, {coastingFuelCutEnabled == 1} field = "Cut fuel below MAP", coastingFuelCutMap, {coastingFuelCutEnabled == 1} + field = "Fuel cut delay", dfcoDelay, {coastingFuelCutEnabled == 1} field = "Inhibit closed loop fuel after cut", noFuelTrimAfterDfcoTime, {coastingFuelCutEnabled == 1} dialog = rotaryDialog, "Rotary" diff --git a/unit_tests/tests/ignition_injection/test_fuelCut.cpp b/unit_tests/tests/ignition_injection/test_fuelCut.cpp index 94b22dcf42..8ecc9692ab 100644 --- a/unit_tests/tests/ignition_injection/test_fuelCut.cpp +++ b/unit_tests/tests/ignition_injection/test_fuelCut.cpp @@ -11,6 +11,10 @@ using ::testing::_; +// Define some helpers for not-cut and cut +#define EXPECT_NORMAL() EXPECT_FLOAT_EQ(normalInjDuration, engine->injectionDuration) +#define EXPECT_CUT() EXPECT_FLOAT_EQ(0, engine->injectionDuration) + TEST(fuelCut, coasting) { EngineTestHelper eth(TEST_ENGINE); EXPECT_CALL(*eth.mockAirmass, getAirmass(_)) @@ -49,10 +53,6 @@ TEST(fuelCut, coasting) { // process eth.engine.periodicFastCallback(); - // Define some helpers for not-cut and cut - #define EXPECT_NORMAL() EXPECT_FLOAT_EQ(normalInjDuration, engine->injectionDuration) - #define EXPECT_CUT() EXPECT_FLOAT_EQ(0, engine->injectionDuration) - // this is normal injection mode (the throttle is opened), no fuel cut-off EXPECT_NORMAL(); @@ -130,3 +130,73 @@ TEST(fuelCut, coasting) { EXPECT_CUT(); } +TEST(fuelCut, delay) { + EngineTestHelper eth(TEST_ENGINE); + EXPECT_CALL(*eth.mockAirmass, getAirmass(_)) + .WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f})); + + // configure coastingFuelCut + engineConfiguration->coastingFuelCutEnabled = true; + engineConfiguration->coastingFuelCutRpmLow = 1300; + engineConfiguration->coastingFuelCutRpmHigh = 1500; + engineConfiguration->coastingFuelCutTps = 2; + engineConfiguration->coastingFuelCutClt = 30; + engineConfiguration->coastingFuelCutMap = 100; + // set cranking threshold + engineConfiguration->cranking.rpm = 999; + + // delay is 1 second + engineConfiguration->dfcoDelay = 1.0f; + + // basic engine setup + setupSimpleTestEngineWithMafAndTT_ONE_trigger(ð); + + Sensor::setMockValue(SensorType::Map, 0); + + // mock CLT - just above threshold ('hot engine') + float hotClt = engineConfiguration->coastingFuelCutClt + 1; + Sensor::setMockValue(SensorType::Clt, hotClt); + // mock TPS - throttle is opened + Sensor::setMockValue(SensorType::DriverThrottleIntent, 60); + // set 'running' RPM - just above RpmHigh threshold + Sensor::setMockValue(SensorType::Rpm, engineConfiguration->coastingFuelCutRpmHigh + 1); + // 'advance' time (amount doesn't matter) + eth.moveTimeForwardUs(1000); + + const float normalInjDuration = 1.5f; + + extern int timeNowUs; + timeNowUs = 1e6; + + // process + eth.engine.periodicFastCallback(); + + // this is normal injection mode (the throttle is opened), no fuel cut-off + EXPECT_NORMAL(); + + // 'releasing' the throttle + Sensor::setMockValue(SensorType::DriverThrottleIntent, 0); + eth.engine.periodicFastCallback(); + + // Shouldn't cut yet, since not enough time has elapsed + EXPECT_NORMAL(); + + // Change nothing else, but advance time and update again + timeNowUs += 0.9e6; + eth.engine.periodicFastCallback(); + + // too soon, still no cut + EXPECT_NORMAL(); + + // Change nothing else, but advance time and update again + timeNowUs += 0.2e6; + eth.engine.periodicFastCallback(); + + // Should now be cut! + EXPECT_CUT(); + + // Put the throtle back, and it should come back instantly + Sensor::setMockValue(SensorType::DriverThrottleIntent, 30); + eth.engine.periodicFastCallback(); + EXPECT_NORMAL(); +}