extract logic from wall wetting AE (#3848)

* extract logic

* extract interface

* explicitly test wall fuel
This commit is contained in:
Matthew Kennedy 2022-01-27 04:45:50 -08:00 committed by GitHub
parent 4892eb4650
commit 50c31b78ae
4 changed files with 149 additions and 48 deletions

View File

@ -184,6 +184,7 @@ public:
AcController,
PrimeController,
DfcoController,
Mockable<WallFuelController>,
EngineModule // dummy placeholder so the previous entries can all have commas
> engineModules;

View File

@ -17,11 +17,6 @@ float WallFuel::adjust(float desiredMassGrams) {
return desiredMassGrams;
}
// disable this correction for cranking
if (engine->rpmCalculator.isCranking()) {
return desiredMassGrams;
}
ScopePerf perf(PE::WallFuelAdjust);
/*
@ -46,49 +41,15 @@ float WallFuel::adjust(float desiredMassGrams) {
the time the fuel spent on the wall this cycle, (recriprocal RPM).
beta describes the amount of fuel that hits the wall.
TODO: these parameters, tau and beta vary with various engine parameters,
most notably manifold pressure (as a proxy for air speed), and coolant
temperature (as a proxy for the intake valve and runner temperature).
TAU: decreases with increasing temperature.
decreases with decreasing manifold pressure.
BETA: decreases with increasing temperature.
decreases with decreasing manifold pressure.
*/
// if tau or beta is really small, we get div/0.
// you probably meant to disable wwae.
float tau = engineConfiguration->wwaeTau;
float beta = engineConfiguration->wwaeBeta;
if (tau < 0.01f || beta < 0.01f) {
// If disabled, pass value through
if (!engine->module<WallFuelController>()->getEnable()) {
return desiredMassGrams;
}
// Ignore really slow RPM
int rpm = Sensor::getOrZero(SensorType::Rpm);
if (rpm < 100) {
return desiredMassGrams;
}
float alpha = expf_taylor(-120 / (rpm * tau));
#if EFI_TUNER_STUDIO
if (engineConfiguration->debugMode == DBG_KNOCK) {
engine->outputChannels.debugFloatField1 = alpha;
engine->outputChannels.debugFloatField2 = beta;
}
#endif // EFI_TUNER_STUDIO
// If beta is larger than alpha, the system is underdamped.
// For reasonable values {tau, beta}, this should only be possible
// at extremely low engine speeds (<300rpm ish)
// Clamp beta to less than alpha.
if (beta > alpha) {
beta = alpha;
}
float alpha = engine->module<WallFuelController>()->getAlpha();
float beta = engine->module<WallFuelController>()->getBeta();
float fuelFilmMass = wallFuel;
float M_cmd = (desiredMassGrams - (1 - alpha) * fuelFilmMass) / (1 - beta);
@ -124,3 +85,72 @@ float WallFuel::adjust(float desiredMassGrams) {
float WallFuel::getWallFuel() const {
return wallFuel;
}
/*
TODO: these parameters, tau and beta vary with various engine parameters,
most notably manifold pressure (as a proxy for air speed), and coolant
temperature (as a proxy for the intake valve and runner temperature).
TAU: decreases with increasing temperature.
decreases with decreasing manifold pressure.
BETA: decreases with increasing temperature.
decreases with decreasing manifold pressure.
*/
float WallFuelController::computeTau() const {
return engineConfiguration->wwaeTau;
}
float WallFuelController::computeBeta() const {
return engineConfiguration->wwaeBeta;
}
void WallFuelController::onFastCallback() {
// disable wall wetting cranking
// TODO: is this correct? Why not correct for cranking?
if (engine->rpmCalculator.isCranking()) {
m_enable = false;
return;
}
float tau = computeTau();
float beta = computeBeta();
// if tau or beta is really small, we get div/0.
// you probably meant to disable wwae.
if (tau < 0.01f || beta < 0.01f) {
m_enable = false;
return;
}
auto rpm = Sensor::getOrZero(SensorType::Rpm);
// Ignore low RPM
if (rpm < 100) {
m_enable = false;
return;
}
float alpha = expf_taylor(-120 / (rpm * tau));
// If beta is larger than alpha, the system is underdamped.
// For reasonable values {tau, beta}, this should only be possible
// at extremely low engine speeds (<300rpm ish)
// Clamp beta to less than alpha.
if (beta > alpha) {
beta = alpha;
}
// Store parameters so the model can read them
m_alpha = alpha;
m_beta = beta;
m_enable = true;
#if EFI_TUNER_STUDIO
// TODO: why DBG_KNOCK? That seems wrong.
if (engineConfiguration->debugMode == DBG_KNOCK) {
engine->outputChannels.debugFloatField1 = alpha;
engine->outputChannels.debugFloatField2 = beta;
}
#endif // EFI_TUNER_STUDIO
}

View File

@ -20,3 +20,37 @@ public:
void resetWF();
int invocationCounter = 0;
};
struct IWallFuelController {
virtual bool getEnable() const = 0;
virtual float getAlpha() const = 0;
virtual float getBeta() const = 0;
};
class WallFuelController : public IWallFuelController, public EngineModule {
public:
using interface_t = IWallFuelController;
void onFastCallback() override;
bool getEnable() const override {
return m_enable;
}
float getAlpha() const override {
return m_alpha;
}
float getBeta() const override {
return m_beta;
}
protected:
float computeTau() const;
float computeBeta() const;
private:
bool m_enable = false;
float m_alpha = 0;
float m_beta = 0;
};

View File

@ -9,19 +9,55 @@
#include "pch.h"
struct MockWallController : public IWallFuelController {
MOCK_METHOD(bool, getEnable, (), (const, override));
MOCK_METHOD(float, getAlpha, (), (const, override));
MOCK_METHOD(float, getBeta, (), (const, override));
};
TEST(fuel, testWallWettingEnrichmentMath) {
EngineTestHelper eth(FORD_ASPIRE_1996);
engineConfiguration->wwaeTau = 1.0f;
engineConfiguration->wwaeBeta = 0.40f;
MockWallController wallController;
engine->rpmCalculator.setRpmValue(3000);
// WW is enabled!
EXPECT_CALL(wallController, getEnable()).WillRepeatedly(Return(true));
// 1/2 of fuel remains on walls
EXPECT_CALL(wallController, getAlpha()).WillRepeatedly(Return(0.5f));
// 1/4 of fuel is lands on walls
EXPECT_CALL(wallController, getBeta()).WillRepeatedly(Return(0.25f));
// install our mock in to the engine
engine->engineModules.get<WallFuelController>().set(&wallController);
WallFuel wallFuel;
// each invocation of 'adjust' changes WallWetting internal state
ASSERT_NEAR(16.6666, wallFuel.adjust(10.0), EPS4D);
ASSERT_NEAR(16.198, wallFuel.adjust(10.0), EPS4D);
EXPECT_NEAR(1.3333, wallFuel.adjust(1), EPS4D);
EXPECT_NEAR(1.1111, wallFuel.adjust(1), EPS4D);
EXPECT_NEAR(1.0370, wallFuel.adjust(1), EPS4D);
EXPECT_NEAR(1.0123, wallFuel.adjust(1), EPS4D);
// get to steady state
for (size_t i = 0; i < 50; i++) {
wallFuel.adjust(1);
}
EXPECT_NEAR(1, wallFuel.adjust(1), EPS4D);
// now run half the fuel
EXPECT_NEAR(0.3333, wallFuel.adjust(0.5), EPS4D);
EXPECT_NEAR(0.4444, wallFuel.adjust(0.5), EPS4D);
EXPECT_NEAR(0.4815, wallFuel.adjust(0.5), EPS4D);
EXPECT_NEAR(0.4938, wallFuel.adjust(0.5), EPS4D);
for (size_t i = 0; i < 50; i++) {
wallFuel.adjust(0.5);
}
EXPECT_NEAR(0.5, wallFuel.adjust(0.5), EPS4D);
}
TEST(fuel, testWallWettingEnrichmentScheduling) {