From 4b5ed3e0e18ed75ea301274a1902eabcaecc5fda Mon Sep 17 00:00:00 2001 From: andreika-git Date: Fri, 29 Mar 2019 04:47:20 +0200 Subject: [PATCH] Tps-accel. fractional mode (#715) * Add new config settings for tps_accel_fractional_mode * Add new variables for the fractional algo * Add resetFractionValues() method * Call onEngineCycleTps() and fix onEngineCycle() * Implement the fractional algo * More debug outputs for tps-enrich * Nice & juicy unit-test! --- .../controllers/algo/accel_enrichment.cpp | 62 ++++++++++++++++- firmware/controllers/algo/accel_enrichment.h | 9 +++ .../trigger/main_trigger_callback.cpp | 5 +- firmware/integration/rusefi_config.txt | 7 +- firmware/tunerstudio/rusefi.input | 3 + unit_tests/tests/test_accel_enrichment.cpp | 66 +++++++++++++++++++ 6 files changed, 148 insertions(+), 4 deletions(-) diff --git a/firmware/controllers/algo/accel_enrichment.cpp b/firmware/controllers/algo/accel_enrichment.cpp index 9a6f12579e..0208131eeb 100644 --- a/firmware/controllers/algo/accel_enrichment.cpp +++ b/firmware/controllers/algo/accel_enrichment.cpp @@ -190,12 +190,38 @@ floatms_t AccelEnrichmemnt::getTpsEnrichment(DECLARE_ENGINE_PARAMETER_SIGNATURE) extraFuel = 0; } + // Fractional enrichment (fuel portions are accumulated and split between several engine cycles. + // This is a crude imitation of carburetor's acceleration pump. + if (CONFIG(tpsAccelFractionPeriod) > 1 || CONFIG(tpsAccelFractionDivisor) > 1.0f) { + // make sure both values are non-zero + float periodF = (float)maxI(CONFIG(tpsAccelFractionPeriod), 1); + float divisor = maxF(CONFIG(tpsAccelFractionDivisor), 1.0f); + + // if current extra fuel portion is not "strong" enough, then we keep up the "pump pressure" with the accumulated portion + floatms_t maxExtraFuel = maxF(extraFuel, accumulatedValue); + // use only a fixed fraction of the accumulated portion + floatms_t injFuel = maxExtraFuel / divisor; + + // update max counters + maxExtraPerCycle = maxF(extraFuel, maxExtraPerCycle); + maxInjectedPerPeriod = maxF(injFuel, maxInjectedPerPeriod); + + // evenly split it between several engine cycles + extraFuel = injFuel / periodF; + } else { + resetFractionValues(); + } + if (engineConfiguration->debugMode == DBG_TPS_ACCEL) { #if EFI_TUNER_STUDIO || defined(__DOXYGEN__) tsOutputChannels.debugFloatField1 = tpsFrom; tsOutputChannels.debugFloatField2 = tpsTo; tsOutputChannels.debugFloatField3 = valueFromTable; tsOutputChannels.debugFloatField4 = extraFuel; + tsOutputChannels.debugFloatField5 = accumulatedValue; + tsOutputChannels.debugFloatField6 = maxExtraPerPeriod; + tsOutputChannels.debugFloatField7 = maxInjectedPerPeriod; + tsOutputChannels.debugIntField1 = cycleCnt; #endif /* EFI_TUNER_STUDIO */ } @@ -236,6 +262,15 @@ float AccelEnrichmemnt::getEngineLoadEnrichment(DECLARE_ENGINE_PARAMETER_SIGNATU void AccelEnrichmemnt::reset() { cb.clear(); previousValue = NAN; + resetFractionValues(); +} + +void AccelEnrichmemnt::resetFractionValues() { + accumulatedValue = 0; + maxExtraPerCycle = 0; + maxExtraPerPeriod = 0; + maxInjectedPerPeriod = 0; + cycleCnt = 0; } void AccelEnrichmemnt::setLength(int length) { @@ -247,7 +282,32 @@ void AccelEnrichmemnt::onNewValue(float currentValue DECLARE_ENGINE_PARAMETER_SU } void AccelEnrichmemnt::onEngineCycleTps(DECLARE_ENGINE_PARAMETER_SIGNATURE) { - onNewValue(getTPS(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX); + // we update values in handleFuel() directly + //onNewValue(getTPS(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX); + + // we used some extra fuel during the current cycle, so we "charge" our "acceleration pump" with it + accumulatedValue -= maxExtraPerPeriod; + maxExtraPerPeriod = maxF(maxExtraPerCycle, maxExtraPerPeriod); + maxExtraPerCycle = 0; + accumulatedValue += maxExtraPerPeriod; + + // update the accumulated value every 'Period' engine cycles + if (--cycleCnt <= 0) { + maxExtraPerPeriod = 0; + + // we've injected this portion during the cycle, so we set what's left for the next cycle + accumulatedValue -= maxInjectedPerPeriod; + maxInjectedPerPeriod = 0; + + // it's an infinitely convergent series, so we set a limit at some point + // (also make sure that accumulatedValue is positive, for safety) + static const floatms_t smallEpsilon = 0.001f; + if (accumulatedValue < smallEpsilon) + accumulatedValue = 0; + + // reset the counter + cycleCnt = CONFIG(tpsAccelFractionPeriod); + } } void AccelEnrichmemnt::onEngineCycle(DECLARE_ENGINE_PARAMETER_SIGNATURE) { diff --git a/firmware/controllers/algo/accel_enrichment.h b/firmware/controllers/algo/accel_enrichment.h index eab48862b9..0e7185712f 100644 --- a/firmware/controllers/algo/accel_enrichment.h +++ b/firmware/controllers/algo/accel_enrichment.h @@ -36,12 +36,21 @@ public: void onEngineCycle(DECLARE_ENGINE_PARAMETER_SIGNATURE); void onEngineCycleTps(DECLARE_ENGINE_PARAMETER_SIGNATURE); void reset(); + void resetFractionValues(); void setLength(int length); cyclic_buffer cb; void onNewValue(float currentValue DECLARE_ENGINE_PARAMETER_SUFFIX); private: float previousValue; + /** + * Used for Fractional TPS enrichment. + */ + floatms_t accumulatedValue; + floatms_t maxExtraPerCycle; + floatms_t maxExtraPerPeriod; + floatms_t maxInjectedPerPeriod; + int cycleCnt; }; /** diff --git a/firmware/controllers/trigger/main_trigger_callback.cpp b/firmware/controllers/trigger/main_trigger_callback.cpp index 3413480e63..3ce824752f 100644 --- a/firmware/controllers/trigger/main_trigger_callback.cpp +++ b/firmware/controllers/trigger/main_trigger_callback.cpp @@ -391,7 +391,10 @@ static ALWAYS_INLINE void handleFuel(const bool limitedFuel, uint32_t trgEventIn #endif /* FUEL_MATH_EXTREME_LOGGING */ ENGINE(tpsAccelEnrichment.onNewValue(getTPS(PASS_ENGINE_PARAMETER_SIGNATURE) PASS_ENGINE_PARAMETER_SUFFIX)); - ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE)); + if (trgEventIndex == 0) { + ENGINE(tpsAccelEnrichment.onEngineCycleTps(PASS_ENGINE_PARAMETER_SIGNATURE)); + ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE)); + } /** * we have same assignment of 'getInjectionDuration' to 'injectionDuration' in periodicFastCallback() diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 7e96fd4391..e626cc3f5e 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -991,10 +991,13 @@ tChargeMode_e tChargeMode; int16_t idleTimingPidWorkZone;The timing correction works only if RPM is close enough, otherwise the IAC correction works;"RPM", 1, 0, 0, 1000, 0 int16_t idleTimingPidDeadZone;If RPM is too perfect, let's leave the advance angle alone to avoid oscillation;"RPM", 1, 0, 0, 1000, 0 int16_t idlePidFalloffDeltaRpm;Added to the work zone for smooth correction falloff;"RPM", 1, 0, 0, 1000, 0 - int16_t unusedIdleTimingPid; + + int16_t tpsAccelFractionPeriod;+A delay in cycles between fuel-enrich. portions;"cycles", 1, 0, 0, 500, 0 + float tpsAccelFractionDivisor;+A fraction divisor: 1 or less = entire portion at once, or split into diminishing fractions;"coef", 1, 0, 0, 100, 2 + spi_device_e tle8888spiDevice; - int[614] mainUnusedEnd; + int[613] mainUnusedEnd; end_struct diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 565fd1232d..ebe08f6fad 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -2160,6 +2160,9 @@ cmd_set_engine_type_default = "w\x00\x31\x00\x00" field = "Accel Threshold", tpsAccelEnrichmentThreshold field = "Decel Threshold", tpsDecelEnleanmentThreshold ; field = "Decel Multiplier", tpsDecelEnleanmentMultiplier + field = "#Accelerator Pump model:" + field = "Fraction Period", tpsAccelFractionPeriod + field = "Fraction Divisor", tpsAccelFractionDivisor dialog = WallWettingAccelPanel, "Wall Wetting (alpha version)" field = "evaporation time constant", wwaeTau diff --git a/unit_tests/tests/test_accel_enrichment.cpp b/unit_tests/tests/test_accel_enrichment.cpp index 17d373505c..087c6fe67f 100644 --- a/unit_tests/tests/test_accel_enrichment.cpp +++ b/unit_tests/tests/test_accel_enrichment.cpp @@ -37,3 +37,69 @@ TEST(big, testAccelEnrichment) { engine->tpsAccelEnrichment.onNewValue(0 PASS_ENGINE_PARAMETER_SUFFIX); ASSERT_EQ( 0, engine->tpsAccelEnrichment.getMaxDelta(PASS_ENGINE_PARAMETER_SIGNATURE)) << "maxDelta"; } + +static void doFractionalTpsIteration(int period, int divisor, int numCycles, std::vector &tpsEnrich DECLARE_ENGINE_PARAMETER_SUFFIX) { + // every cycle + engineConfiguration->tpsAccelFractionPeriod = period; + // split into 2 portions + engineConfiguration->tpsAccelFractionDivisor = divisor; + + engine->tpsAccelEnrichment.reset(); + engine->tpsAccelEnrichment.onNewValue(0 PASS_ENGINE_PARAMETER_SUFFIX); + for (int i = 0; i < numCycles; i++) { + engine->tpsAccelEnrichment.onNewValue(10 PASS_ENGINE_PARAMETER_SUFFIX); + engine->tpsAccelEnrichment.onEngineCycleTps(PASS_ENGINE_PARAMETER_SIGNATURE); + tpsEnrich[i] = engine->tpsAccelEnrichment.getTpsEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); + } +} + +TEST(big, testAccelEnrichmentFractionalTps) { + printf("====================================================================================== testAccelEnrichmentFractionalTps\r\n"); + + WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); + + // setup + engineConfiguration->tpsAccelEnrichmentThreshold = 5; + + // fill tps2tps map (todo: there should be a better way?) + static const float tpsTpsConst = 1.0f; + for (int loadIndex = 0; loadIndex < TPS_TPS_ACCEL_TABLE; loadIndex++) { + for (int rpmIndex = 0; rpmIndex < TPS_TPS_ACCEL_TABLE; rpmIndex++) { + config->tpsTpsAccelTable[loadIndex][rpmIndex] = tpsTpsConst; + } + } + + Logging logger; + initAccelEnrichment(&logger PASS_ENGINE_PARAMETER_SUFFIX); + + engine->rpmCalculator.setRpmValue(600 PASS_ENGINE_PARAMETER_SUFFIX); + engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE); + + engine->tpsAccelEnrichment.setLength(2); + + + const int numCycles = 4; + std::vector tpsEnrich(numCycles); + + // first, test the default behavior without fractional division + doFractionalTpsIteration(1, 1, numCycles, tpsEnrich PASS_ENGINE_PARAMETER_SUFFIX); + // the portion for the first cycle is full-enriched and there's no enrichment for next cycles + EXPECT_THAT(tpsEnrich, testing::ElementsAre(1.0f, 0.0f, 0.0f, 0.0f)) << "fractionalTps#1"; + + // divide into 2 each cycle + doFractionalTpsIteration(1, 2, numCycles, tpsEnrich PASS_ENGINE_PARAMETER_SUFFIX); + // we have half-portion for the first cycle, then 1/4-th and 1/8th and so on... + EXPECT_THAT(tpsEnrich, testing::ElementsAre(0.5f, 0.25f, 0.125f, 0.0625f)) << "fractionalTps#2"; + + // split every portion into 3 sub-portions (so the whole enrichment takes longer) + doFractionalTpsIteration(3, 1, numCycles, tpsEnrich PASS_ENGINE_PARAMETER_SUFFIX); + // we have 1/3rd-portion for the first three cycles + const float th = (1.0f / 3.0f); + EXPECT_THAT(tpsEnrich, testing::ElementsAre(testing::FloatEq(th), testing::FloatEq(th), testing::FloatEq(th), 0.0f)) << "fractionalTps#3"; + + // split every divided portion into 2 sub-portions (so the whole enrichment takes longer) + doFractionalTpsIteration(2, 2, numCycles, tpsEnrich PASS_ENGINE_PARAMETER_SUFFIX); + // we have half-portion for the first two cycles, and 1/4-th portion for the next 2 cycles, and so on... + EXPECT_THAT(tpsEnrich, testing::ElementsAre(0.25f, 0.25f, 0.125f, 0.125f)) << "fractionalTps#4"; + +}