Disable closed loop idle during cranking->running taper (#2900)
* implement * dead file? * implement tests * new phase
This commit is contained in:
parent
188bf9bd57
commit
5bd3ae3aaa
|
@ -208,7 +208,7 @@ int IdleController::getTargetRpm(float clt) const {
|
|||
return target;
|
||||
}
|
||||
|
||||
IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, SensorResult tps, float vss) const {
|
||||
IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const {
|
||||
if (!engine->rpmCalculator.isRunning()) {
|
||||
return Phase::Cranking;
|
||||
}
|
||||
|
@ -235,10 +235,19 @@ IIdleController::Phase IdleController::determinePhase(int rpm, int targetRpm, Se
|
|||
return Phase::Running;
|
||||
}
|
||||
|
||||
// If still in the cranking taper, disable closed loop idle
|
||||
if (crankingTaperFraction < 1) {
|
||||
return Phase::CrankToRunTaper;
|
||||
}
|
||||
|
||||
// No other conditions met, we are idling!
|
||||
return Phase::Idling;
|
||||
}
|
||||
|
||||
float IdleController::getCrankingTaperFraction() const {
|
||||
return (float)engine->rpmCalculator.getRevolutionCounterSinceStart() / CONFIG(afterCrankingIACtaperDuration);
|
||||
}
|
||||
|
||||
float IdleController::getCrankingOpenLoop(float clt) const {
|
||||
float mult =
|
||||
CONFIG(overrideCrankingIacSetting)
|
||||
|
@ -270,8 +279,7 @@ float IdleController::getRunningOpenLoop(float clt, SensorResult tps) const {
|
|||
return clampF(0, running, 100);
|
||||
}
|
||||
|
||||
float IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps) const {
|
||||
float running = getRunningOpenLoop(clt, tps);
|
||||
float IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) const {
|
||||
float cranking = getCrankingOpenLoop(clt);
|
||||
|
||||
// if we're cranking, nothing more to do.
|
||||
|
@ -279,16 +287,21 @@ float IdleController::getOpenLoop(Phase phase, float clt, SensorResult tps) cons
|
|||
return cranking;
|
||||
}
|
||||
|
||||
float running = getRunningOpenLoop(clt, tps);
|
||||
|
||||
if (phase == Phase::CrankToRunTaper) {
|
||||
// Interpolate between cranking and running over a short time
|
||||
// This clamps once you fall off the end, so no explicit check for >1 required
|
||||
return interpolateClamped(0, cranking, 1, running, crankingTaperFraction);
|
||||
}
|
||||
|
||||
// If coasting (and enabled), use the coasting position table instead of normal open loop
|
||||
// TODO: this should be a table of open loop mult vs. RPM, not vs. clt
|
||||
if (CONFIG(useIacTableForCoasting) && phase == Phase::Coasting) {
|
||||
return interpolate2d(clt, CONFIG(iacCoastingBins), CONFIG(iacCoasting));
|
||||
}
|
||||
|
||||
// Interpolate between cranking and running over a short time
|
||||
// This clamps once you fall off the end, so no explicit check for running required
|
||||
auto revsSinceStart = engine->rpmCalculator.getRevolutionCounterSinceStart();
|
||||
return interpolateClamped(0, cranking, CONFIG(afterCrankingIACtaperDuration), running, revsSinceStart);
|
||||
return running;
|
||||
}
|
||||
|
||||
float IdleController::getIdleTimingAdjustment(int rpm) {
|
||||
|
@ -473,8 +486,11 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
|
|||
auto targetRpm = getTargetRpm(clt);
|
||||
m_lastTargetRpm = targetRpm;
|
||||
|
||||
// Determine cranking taper
|
||||
float crankingTaper = getCrankingTaperFraction();
|
||||
|
||||
// Determine what operation phase we're in - idling or not
|
||||
auto phase = determinePhase(rpm, targetRpm, tps, getVehicleSpeed());
|
||||
auto phase = determinePhase(rpm, targetRpm, tps, getVehicleSpeed(), crankingTaper);
|
||||
m_lastPhase = phase;
|
||||
|
||||
engine->engineState.isAutomaticIdle = tps.Valid && engineConfiguration->idleMode == IM_AUTO;
|
||||
|
@ -494,7 +510,7 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
|
|||
engine->engineState.idle.idleState = BLIP;
|
||||
} else {
|
||||
// Always apply closed loop correction
|
||||
iacPosition = getOpenLoop(phase, clt, tps);
|
||||
iacPosition = getOpenLoop(phase, clt, tps, crankingTaper);
|
||||
engine->engineState.idle.baseIdlePosition = iacPosition;
|
||||
|
||||
// If TPS is working and automatic mode enabled, add any automatic correction
|
||||
|
@ -505,7 +521,6 @@ float IdleController::getClosedLoop(IIdleController::Phase phase, float tpsPos,
|
|||
iacPosition = clampPercentValue(iacPosition);
|
||||
}
|
||||
|
||||
|
||||
#if EFI_TUNER_STUDIO
|
||||
tsOutputChannels.isIdleClosedLoop = phase == Phase::Idling;
|
||||
tsOutputChannels.isIdleCoasting = phase == Phase::Coasting;
|
||||
|
|
|
@ -18,15 +18,17 @@ struct IIdleController {
|
|||
Cranking, // Below cranking threshold
|
||||
Idling, // Below idle RPM, off throttle
|
||||
Coasting, // Off throttle but above idle RPM
|
||||
CrankToRunTaper, // Taper between cranking and running
|
||||
Running, // On throttle
|
||||
};
|
||||
|
||||
virtual Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss) const = 0;
|
||||
virtual Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const = 0;
|
||||
virtual int getTargetRpm(float clt) const = 0;
|
||||
virtual float getCrankingOpenLoop(float clt) const = 0;
|
||||
virtual float getRunningOpenLoop(float clt, SensorResult tps) const = 0;
|
||||
virtual float getOpenLoop(Phase phase, float clt, SensorResult tps) const = 0;
|
||||
virtual float getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) const = 0;
|
||||
virtual float getClosedLoop(Phase phase, float tps, int rpm, int target) = 0;
|
||||
virtual float getCrankingTaperFraction() const = 0;
|
||||
};
|
||||
|
||||
class IdleController : public IIdleController {
|
||||
|
@ -42,12 +44,13 @@ public:
|
|||
int getTargetRpm(float clt) const override;
|
||||
|
||||
// PHASE DETERMINATION: what is the driver trying to do right now?
|
||||
Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss) const override;
|
||||
Phase determinePhase(int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction) const override;
|
||||
float getCrankingTaperFraction() const override;
|
||||
|
||||
// OPEN LOOP CORRECTIONS
|
||||
float getCrankingOpenLoop(float clt) const override;
|
||||
float getRunningOpenLoop(float clt, SensorResult tps) const override;
|
||||
float getOpenLoop(Phase phase, float clt, SensorResult tps) const override;
|
||||
float getOpenLoop(Phase phase, float clt, SensorResult tps, float crankingTaperFraction) const override;
|
||||
|
||||
float getIdleTimingAdjustment(int rpm);
|
||||
float getIdleTimingAdjustment(int rpm, int targetRpm, Phase phase);
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
|
|
@ -129,33 +129,36 @@ TEST(idle_v2, testDeterminePhase) {
|
|||
|
||||
// First test stopped engine
|
||||
engine->rpmCalculator.setRpmValue(0);
|
||||
EXPECT_EQ(ICP::Cranking, dut.determinePhase(0, 1000, unexpected, 0));
|
||||
EXPECT_EQ(ICP::Cranking, dut.determinePhase(0, 1000, unexpected, 0, 10));
|
||||
|
||||
// Now engine is running!
|
||||
// Controller doesn't need this other than for isCranking()
|
||||
engine->rpmCalculator.setRpmValue(1000);
|
||||
|
||||
// Test invalid TPS, but inside the idle window
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, unexpected, 0));
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, unexpected, 0, 10));
|
||||
|
||||
// Valid TPS should now be inside the zone
|
||||
EXPECT_EQ(ICP::Idling, dut.determinePhase(1000, 1000, 0, 0));
|
||||
EXPECT_EQ(ICP::Idling, dut.determinePhase(1000, 1000, 0, 0, 10));
|
||||
|
||||
// Inside the zone, but vehicle speed too fast
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, 0, 25));
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, 0, 25, 10));
|
||||
|
||||
// Check that shortly after cranking, the cranking taper inhibits closed loop idle
|
||||
EXPECT_EQ(ICP::CrankToRunTaper, dut.determinePhase(1000, 1000, 0, 0, 0.5f));
|
||||
|
||||
// Above TPS threshold should be outside the zone
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, 10, 0));
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1000, 1000, 10, 0, 10));
|
||||
|
||||
// Above target, below (target + upperLimit) should be in idle zone
|
||||
EXPECT_EQ(ICP::Idling, dut.determinePhase(1099, 1000, 0, 0));
|
||||
EXPECT_EQ(ICP::Idling, dut.determinePhase(1099, 1000, 0, 0, 10));
|
||||
|
||||
// above upper limit and on throttle should be out of idle zone
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1101, 1000, 10, 0));
|
||||
EXPECT_EQ(ICP::Running, dut.determinePhase(1101, 1000, 10, 0, 10));
|
||||
|
||||
// Below TPS but above RPM should be outside the zone
|
||||
EXPECT_EQ(ICP::Coasting, dut.determinePhase(1101, 1000, 0, 0));
|
||||
EXPECT_EQ(ICP::Coasting, dut.determinePhase(5000, 1000, 0, 0));
|
||||
EXPECT_EQ(ICP::Coasting, dut.determinePhase(1101, 1000, 0, 0, 10));
|
||||
EXPECT_EQ(ICP::Coasting, dut.determinePhase(5000, 1000, 0, 0, 10));
|
||||
}
|
||||
|
||||
TEST(idle_v2, crankingOpenLoop) {
|
||||
|
@ -274,11 +277,10 @@ TEST(idle_v2, testOpenLoopCranking) {
|
|||
|
||||
CONFIG(overrideCrankingIacSetting) = true;
|
||||
|
||||
EXPECT_CALL(dut, getRunningOpenLoop(30, SensorResult(0))).WillOnce(Return(33));
|
||||
EXPECT_CALL(dut, getCrankingOpenLoop(30)).WillOnce(Return(44));
|
||||
|
||||
// Should return the value from getCrankingOpenLoop, and ignore running numbers
|
||||
EXPECT_FLOAT_EQ(44, dut.getOpenLoop(ICP::Cranking, 30, 0));
|
||||
EXPECT_FLOAT_EQ(44, dut.getOpenLoop(ICP::Cranking, 30, 0, 0));
|
||||
}
|
||||
|
||||
TEST(idle_v2, openLoopRunningTaper) {
|
||||
|
@ -286,31 +288,49 @@ TEST(idle_v2, openLoopRunningTaper) {
|
|||
StrictMock<MockOpenLoopIdler> dut;
|
||||
INJECT_ENGINE_REFERENCE(&dut);
|
||||
|
||||
CONFIG(afterCrankingIACtaperDuration) = 500;
|
||||
|
||||
EXPECT_CALL(dut, getRunningOpenLoop(30, SensorResult(0))).WillRepeatedly(Return(25));
|
||||
EXPECT_CALL(dut, getCrankingOpenLoop(30)).WillRepeatedly(Return(75));
|
||||
|
||||
// 0 cycles - no taper yet, pure cranking value
|
||||
EXPECT_FLOAT_EQ(75, dut.getOpenLoop(ICP::Idling, 30, 0));
|
||||
EXPECT_FLOAT_EQ(75, dut.getOpenLoop(ICP::CrankToRunTaper, 30, 0, 0));
|
||||
|
||||
// 1/2 taper - half way, 50% each value -> outputs 50
|
||||
EXPECT_FLOAT_EQ(50, dut.getOpenLoop(ICP::CrankToRunTaper, 30, 0, 0.5f));
|
||||
|
||||
// 1x taper - fully tapered, should be running value
|
||||
EXPECT_FLOAT_EQ(25, dut.getOpenLoop(ICP::CrankToRunTaper, 30, 0, 1.0f));
|
||||
|
||||
// 2x taper - still fully tapered, should be running value
|
||||
EXPECT_FLOAT_EQ(25, dut.getOpenLoop(ICP::CrankToRunTaper, 30, 0, 2.0f));
|
||||
}
|
||||
|
||||
TEST(idle_v2, getCrankingTaperFraction) {
|
||||
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
|
||||
StrictMock<MockOpenLoopIdler> dut;
|
||||
INJECT_ENGINE_REFERENCE(&dut);
|
||||
|
||||
CONFIG(afterCrankingIACtaperDuration) = 500;
|
||||
|
||||
// 0 cycles - no taper yet, pure cranking value
|
||||
EXPECT_FLOAT_EQ(0, dut.getCrankingTaperFraction());
|
||||
|
||||
// 250 cycles - half way, 50% each value -> outputs 50
|
||||
for (size_t i = 0; i < 250; i++) {
|
||||
engine->rpmCalculator.onNewEngineCycle();
|
||||
}
|
||||
EXPECT_FLOAT_EQ(50, dut.getOpenLoop(ICP::Idling, 30, 0));
|
||||
EXPECT_FLOAT_EQ(0.5f, dut.getCrankingTaperFraction());
|
||||
|
||||
// 500 cycles - fully tapered, should be running value
|
||||
for (size_t i = 0; i < 250; i++) {
|
||||
engine->rpmCalculator.onNewEngineCycle();
|
||||
}
|
||||
EXPECT_FLOAT_EQ(25, dut.getOpenLoop(ICP::Idling, 30, 0));
|
||||
EXPECT_FLOAT_EQ(1, dut.getCrankingTaperFraction());
|
||||
|
||||
// 1000 cycles - still fully tapered, should be running value
|
||||
for (size_t i = 0; i < 500; i++) {
|
||||
engine->rpmCalculator.onNewEngineCycle();
|
||||
}
|
||||
EXPECT_FLOAT_EQ(25, dut.getOpenLoop(ICP::Idling, 30, 0));
|
||||
EXPECT_FLOAT_EQ(2, dut.getCrankingTaperFraction());
|
||||
}
|
||||
|
||||
TEST(idle_v2, openLoopCoastingTable) {
|
||||
|
@ -325,8 +345,8 @@ TEST(idle_v2, openLoopCoastingTable) {
|
|||
CONFIG(iacCoasting)[i] = 5 * i;
|
||||
}
|
||||
|
||||
EXPECT_FLOAT_EQ(10, dut.getOpenLoop(ICP::Coasting, 20, 0));
|
||||
EXPECT_FLOAT_EQ(20, dut.getOpenLoop(ICP::Coasting, 40, 0));
|
||||
EXPECT_FLOAT_EQ(10, dut.getOpenLoop(ICP::Coasting, 20, 0, 2));
|
||||
EXPECT_FLOAT_EQ(20, dut.getOpenLoop(ICP::Coasting, 40, 0, 2));
|
||||
}
|
||||
|
||||
extern int timeNowUs;
|
||||
|
@ -389,9 +409,10 @@ TEST(idle_v2, closedLoopDeadzone) {
|
|||
|
||||
struct IntegrationIdleMock : public IdleController {
|
||||
MOCK_METHOD(int, getTargetRpm, (float clt), (const, override));
|
||||
MOCK_METHOD(ICP, determinePhase, (int rpm, int targetRpm, SensorResult tps, float vss), (const, override));
|
||||
MOCK_METHOD(float, getOpenLoop, (ICP phase, float clt, SensorResult tps), (const, override));
|
||||
MOCK_METHOD(ICP, determinePhase, (int rpm, int targetRpm, SensorResult tps, float vss, float crankingTaperFraction), (const, override));
|
||||
MOCK_METHOD(float, getOpenLoop, (ICP phase, float clt, SensorResult tps, float crankingTaperFraction), (const, override));
|
||||
MOCK_METHOD(float, getClosedLoop, (ICP phase, float tps, int rpm, int target), (override));
|
||||
MOCK_METHOD(float, getCrankingTaperFraction, (), (const, override));
|
||||
};
|
||||
|
||||
TEST(idle_v2, IntegrationManual) {
|
||||
|
@ -410,12 +431,16 @@ TEST(idle_v2, IntegrationManual) {
|
|||
EXPECT_CALL(dut, getTargetRpm(expectedClt))
|
||||
.WillOnce(Return(1000));
|
||||
|
||||
// 30% of the way through cranking taper
|
||||
EXPECT_CALL(dut, getCrankingTaperFraction())
|
||||
.WillOnce(Return(0.3f));
|
||||
|
||||
// Determine phase will claim we're idling
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15))
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15, 0.3f))
|
||||
.WillOnce(Return(ICP::Idling));
|
||||
|
||||
// Open loop should be asked for an open loop position
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps))
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps, 0.3f))
|
||||
.WillOnce(Return(13));
|
||||
|
||||
// getClosedLoop() should not be called!
|
||||
|
@ -441,12 +466,16 @@ TEST(idle_v2, IntegrationAutomatic) {
|
|||
EXPECT_CALL(dut, getTargetRpm(expectedClt))
|
||||
.WillOnce(Return(1000));
|
||||
|
||||
// 40% of the way through cranking taper
|
||||
EXPECT_CALL(dut, getCrankingTaperFraction())
|
||||
.WillOnce(Return(0.4f));
|
||||
|
||||
// Determine phase will claim we're idling
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15))
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15, 0.4f))
|
||||
.WillOnce(Return(ICP::Idling));
|
||||
|
||||
// Open loop should be asked for an open loop position
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps))
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps, 0.4f))
|
||||
.WillOnce(Return(13));
|
||||
|
||||
// Closed loop should get called
|
||||
|
@ -475,12 +504,16 @@ TEST(idle_v2, IntegrationClamping) {
|
|||
EXPECT_CALL(dut, getTargetRpm(expectedClt))
|
||||
.WillOnce(Return(1000));
|
||||
|
||||
// 50% of the way through cranking taper
|
||||
EXPECT_CALL(dut, getCrankingTaperFraction())
|
||||
.WillOnce(Return(0.5f));
|
||||
|
||||
// Determine phase will claim we're idling
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15))
|
||||
EXPECT_CALL(dut, determinePhase(950, 1000, expectedTps, 15, 0.5f))
|
||||
.WillOnce(Return(ICP::Idling));
|
||||
|
||||
// Open loop should be asked for an open loop position
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps))
|
||||
EXPECT_CALL(dut, getOpenLoop(ICP::Idling, expectedClt, expectedTps, 0.5f))
|
||||
.WillOnce(Return(75));
|
||||
|
||||
// Closed loop should get called
|
||||
|
|
Loading…
Reference in New Issue