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!
This commit is contained in:
andreika-git 2019-03-29 04:47:20 +02:00 committed by rusefi
parent c34d7cac48
commit 4b5ed3e0e1
6 changed files with 148 additions and 4 deletions

View File

@ -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) {

View File

@ -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<float> 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;
};
/**

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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<floatms_t> &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<floatms_t> 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";
}