add postState flag to airmass model calls #107

This commit is contained in:
Matthew Kennedy 2023-05-15 17:18:35 -07:00
parent 115612c8e9
commit b1b1ec7fdf
16 changed files with 71 additions and 62 deletions

View File

@ -14,7 +14,7 @@ static float getVeLoadAxis(ve_override_e mode, float passedLoad) {
}
}
float AirmassVeModelBase::getVe(int rpm, float load) const {
float AirmassVeModelBase::getVe(int rpm, float load, bool postState) const {
efiAssert(ObdCode::OBD_PCM_Processor_Fault, m_veTable != nullptr, "VE table null", 0);
// Override the load value if necessary
@ -48,9 +48,11 @@ float AirmassVeModelBase::getVe(int rpm, float load) const {
for (size_t i = 0; i < efi::size(config->veBlends); i++) {
auto result = calculateBlend(config->veBlends[i], rpm, load);
engine->outputChannels.veBlendParameter[i] = result.BlendParameter;
engine->outputChannels.veBlendBias[i] = result.Bias;
engine->outputChannels.veBlendOutput[i] = result.Value;
if (postState) {
engine->outputChannels.veBlendParameter[i] = result.BlendParameter;
engine->outputChannels.veBlendBias[i] = result.Bias;
engine->outputChannels.veBlendOutput[i] = result.Value;
}
if (result.Value == 0) {
continue;
@ -61,7 +63,10 @@ float AirmassVeModelBase::getVe(int rpm, float load) const {
ve *= ((100 + result.Value) * 0.01f);
}
engine->engineState.currentVe = ve;
engine->engineState.veTableYAxis = load;
if (postState) {
engine->engineState.currentVe = ve;
engine->engineState.veTableYAxis = load;
}
return ve * PERCENT_DIV;
}

View File

@ -9,7 +9,7 @@ struct AirmassResult {
};
struct AirmassModelBase {
virtual AirmassResult getAirmass(int rpm) = 0;
virtual AirmassResult getAirmass(int rpm, bool postState) = 0;
};
class AirmassVeModelBase : public AirmassModelBase {
@ -17,7 +17,7 @@ public:
explicit AirmassVeModelBase(const ValueProvider3D& veTable);
// Retrieve the user-calibrated volumetric efficiency from the table
float getVe(int rpm, percent_t load) const;
float getVe(int rpm, percent_t load, bool postState) const;
private:
const ValueProvider3D* const m_veTable;

View File

@ -2,7 +2,7 @@
#include "alphan_airmass.h"
AirmassResult AlphaNAirmass::getAirmass(int rpm) {
AirmassResult AlphaNAirmass::getAirmass(int rpm, bool postState) {
auto tps = Sensor::get(SensorType::Tps1);
if (!tps.Valid) {
@ -11,7 +11,7 @@ AirmassResult AlphaNAirmass::getAirmass(int rpm) {
}
// In this case, VE directly describes the cylinder filling relative to the ideal
float ve = getVe(rpm, tps.Value);
float ve = getVe(rpm, tps.Value, postState);
// TODO: should this be barometric pressure and/or temperature compensated?
mass_t airmass = getAirmassImpl(

View File

@ -6,5 +6,5 @@ class AlphaNAirmass : public SpeedDensityBase {
public:
explicit AlphaNAirmass(const ValueProvider3D& veTable) : SpeedDensityBase(veTable) {}
AirmassResult getAirmass(int rpm) override;
AirmassResult getAirmass(int rpm, bool postState) override;
};

View File

@ -4,7 +4,7 @@
class LuaAirmass final : public AirmassModelBase {
public:
AirmassResult getAirmass(int /*rpm*/) override {
AirmassResult getAirmass(int /*rpm*/, bool /*postState*/) override {
return m_airmass;
}

View File

@ -31,17 +31,17 @@ float MafAirmass::getMaf() const {
}
}
AirmassResult MafAirmass::getAirmass(int rpm) {
AirmassResult MafAirmass::getAirmass(int rpm, bool postState) {
float maf = getMaf();
return getAirmassImpl(maf, rpm);
return getAirmassImpl(maf, rpm, postState);
}
/**
* Function block now works to create a standardised load from the cylinder filling as well as tune fuel via VE table.
* @return total duration of fuel injection per engine cycle, in milliseconds
*/
AirmassResult MafAirmass::getAirmassImpl(float massAirFlow, int rpm) const {
AirmassResult MafAirmass::getAirmassImpl(float massAirFlow, int rpm, bool postState) const {
// If the engine is stopped, MAF is meaningless
if (rpm == 0) {
return {};
@ -64,7 +64,7 @@ AirmassResult MafAirmass::getAirmassImpl(float massAirFlow, int rpm) const {
float airChargeLoad = 100 * cylinderAirmass / getStandardAirCharge();
//Correct air mass by VE table
mass_t correctedAirmass = cylinderAirmass * getVe(rpm, airChargeLoad);
mass_t correctedAirmass = cylinderAirmass * getVe(rpm, airChargeLoad, postState);
return {
correctedAirmass,

View File

@ -6,10 +6,10 @@ class MafAirmass final : public AirmassVeModelBase {
public:
explicit MafAirmass(const ValueProvider3D& veTable) : AirmassVeModelBase(veTable) {}
AirmassResult getAirmass(int rpm) override;
AirmassResult getAirmass(int rpm, bool postState) override;
// Compute airmass based on flow & engine speed
AirmassResult getAirmassImpl(float massAirFlow, int rpm) const;
AirmassResult getAirmassImpl(float massAirFlow, int rpm, bool postState) const;
private:
float getMaf() const;

View File

@ -1,15 +1,15 @@
#include "pch.h"
#include "speed_density_airmass.h"
AirmassResult SpeedDensityAirmass::getAirmass(int rpm) {
AirmassResult SpeedDensityAirmass::getAirmass(int rpm, bool postState) {
ScopePerf perf(PE::GetSpeedDensityFuel);
auto map = getMap(rpm);
auto map = getMap(rpm, postState);
return getAirmass(rpm, map);
return getAirmass(rpm, map, postState);
}
AirmassResult SpeedDensityAirmass::getAirmass(float rpm, float map) {
AirmassResult SpeedDensityAirmass::getAirmass(float rpm, float map, bool postState) {
/**
* most of the values are pre-calculated for performance reasons
*/
@ -19,7 +19,7 @@ AirmassResult SpeedDensityAirmass::getAirmass(float rpm, float map) {
return {};
}
float ve = getVe(rpm, map);
float ve = getVe(rpm, map, postState);
float airMass = getAirmassImpl(ve, map, tChargeK);
if (cisnan(airMass)) {
@ -33,8 +33,8 @@ AirmassResult SpeedDensityAirmass::getAirmass(float rpm, float map) {
};
}
float SpeedDensityAirmass::getAirflow(float rpm, float map) {
auto airmassResult = getAirmass(rpm, map);
float SpeedDensityAirmass::getAirflow(float rpm, float map, bool postState) {
auto airmassResult = getAirmass(rpm, map, postState);
float massPerCycle = airmassResult.CylinderAirmass * engineConfiguration->cylindersCount;
@ -47,11 +47,13 @@ float SpeedDensityAirmass::getAirflow(float rpm, float map) {
return massPerCycle * rpm / 60;
}
float SpeedDensityAirmass::getMap(int rpm) const {
float SpeedDensityAirmass::getMap(int rpm, bool postState) const {
float fallbackMap = m_mapEstimationTable->getValue(rpm, Sensor::getOrZero(SensorType::Tps1));
#if EFI_TUNER_STUDIO
engine->outputChannels.fallbackMap = fallbackMap;
if (postState) {
engine->outputChannels.fallbackMap = fallbackMap;
}
#endif // EFI_TUNER_STUDIO
return Sensor::get(SensorType::Map).value_or(fallbackMap);

View File

@ -9,11 +9,11 @@ public:
, m_mapEstimationTable(&mapEstimationTable)
{}
AirmassResult getAirmass(int rpm) override;
AirmassResult getAirmass(float rpm, float map);
float getAirflow(float rpm, float map);
AirmassResult getAirmass(int rpm, bool postState) override;
AirmassResult getAirmass(float rpm, float map, bool postState);
float getAirflow(float rpm, float map, bool postState);
float getMap(int rpm) const;
float getMap(int rpm, bool postState) const;
private:
const ValueProvider3D* const m_mapEstimationTable;

View File

@ -163,7 +163,7 @@ AirmassModelBase* getAirmassModel(engine_load_mode_e mode) {
}
float getMaxAirflowAtMap(float map) {
return sdAirmass.getAirflow(Sensor::getOrZero(SensorType::Rpm), map);
return sdAirmass.getAirflow(Sensor::getOrZero(SensorType::Rpm), map, false);
}
// Per-cylinder base fuel mass
@ -174,7 +174,7 @@ static float getBaseFuelMass(int rpm) {
auto model = getAirmassModel(engineConfiguration->fuelAlgorithm);
efiAssert(ObdCode::CUSTOM_ERR_ASSERT, model != nullptr, "Invalid airmass mode", 0.0f);
auto airmass = model->getAirmass(rpm);
auto airmass = model->getAirmass(rpm, true);
// Plop some state for others to read
engine->fuelComputer.sdAirMassInOneCylinder = airmass.CylinderAirmass;

View File

@ -92,7 +92,7 @@ public:
MockVp3d veTable;
MOCK_METHOD(AirmassResult, getAirmass, (int rpm), (override));
MOCK_METHOD(AirmassResult, getAirmass, (int rpm, bool postState), (override));
};
class MockInjectorModel2 : public IInjectorModel {

View File

@ -7,6 +7,8 @@
#include "pch.h"
using ::testing::_;
static void doRevolution(EngineTestHelper& eth, int periodMs) {
float halfToothTime = (periodMs / 6.0f) / 2;
@ -32,7 +34,7 @@ TEST(fuelControl, transitionIssue1592) {
engine->tdcMarkEnabled = false;
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth, IM_SEQUENTIAL);
EXPECT_CALL(*eth.mockAirmass, getAirmass(500))
EXPECT_CALL(*eth.mockAirmass, getAirmass(500, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
// This is easiest to trip on a wheel that requires sync

View File

@ -17,7 +17,7 @@ using ::testing::_;
TEST(fuelCut, coasting) {
EngineTestHelper eth(TEST_ENGINE);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
// configure coastingFuelCut
@ -132,7 +132,7 @@ TEST(fuelCut, coasting) {
TEST(fuelCut, delay) {
EngineTestHelper eth(TEST_ENGINE);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
// configure coastingFuelCut

View File

@ -55,7 +55,7 @@ TEST(AirmassModes, AlphaNNormal) {
// Mass of 1 liter of air * VE
mass_t expectedAirmass = 1.2047f * 0.35f;
auto result = dut.getAirmass(1200);
auto result = dut.getAirmass(1200, false);
EXPECT_NEAR(result.CylinderAirmass, expectedAirmass, EPS4D);
EXPECT_NEAR(result.EngineLoadPercent, 0.71f, EPS4D);
}
@ -73,7 +73,7 @@ TEST(AirmassModes, AlphaNFailedTps) {
// Ensure that it's actually failed
ASSERT_FALSE(Sensor::get(SensorType::Tps1).Valid);
auto result = dut.getAirmass(1200);
auto result = dut.getAirmass(1200, false);
EXPECT_EQ(result.CylinderAirmass, 0);
}
@ -89,7 +89,7 @@ TEST(AirmassModes, MafNormal) {
MafAirmass dut(veTable);
auto airmass = dut.getAirmassImpl(200, 6000);
auto airmass = dut.getAirmassImpl(200, 6000, false);
// Check results
EXPECT_NEAR(0.277777f * 0.75f, airmass.CylinderAirmass, EPS4D);
@ -111,9 +111,9 @@ TEST(AirmassModes, VeOverride) {
struct DummyAirmassModel : public AirmassVeModelBase {
DummyAirmassModel(const ValueProvider3D& veTable) : AirmassVeModelBase(veTable) {}
AirmassResult getAirmass(int rpm) override {
AirmassResult getAirmass(int rpm, bool postState) override {
// Default load value 10, will be overriden
getVe(rpm, 10.0f);
getVe(rpm, 10.0f, postState);
return {};
}
@ -123,13 +123,13 @@ TEST(AirmassModes, VeOverride) {
DummyAirmassModel dut(veTable);
// Use default mode - will call with 10
dut.getAirmass(0);
dut.getAirmass(0, true);
EXPECT_FLOAT_EQ(engine->engineState.veTableYAxis, 10.0f);
// Override to TPS
engineConfiguration->veOverrideMode = VE_TPS;
Sensor::setMockValue(SensorType::Tps1, 30.0f);
dut.getAirmass(0);
dut.getAirmass(0, true);
EXPECT_FLOAT_EQ(engine->engineState.veTableYAxis, 30.0f);
}
@ -157,11 +157,11 @@ TEST(AirmassModes, FallbackMap) {
// Working MAP sensor at 40 kPa
Sensor::setMockValue(SensorType::Map, 40);
EXPECT_FLOAT_EQ(dut.getMap(1234), 40);
EXPECT_FLOAT_EQ(dut.getMap(1234, false), 40);
// Failed MAP sensor, should use table
Sensor::resetMockValue(SensorType::Map);
EXPECT_FLOAT_EQ(dut.getMap(5678), 75);
EXPECT_FLOAT_EQ(dut.getMap(5678, false), 75);
}
void setInjectionMode(int value);
@ -170,7 +170,7 @@ TEST(FuelMath, testDifferentInjectionModes) {
EngineTestHelper eth(TEST_ENGINE);
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{1.3440001f, 50.0f}));
setInjectionMode((int)IM_BATCH);
@ -196,7 +196,7 @@ TEST(FuelMath, deadtime) {
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{1.3440001f, 50.0f}));
// First test with no deadtime
@ -214,7 +214,7 @@ TEST(FuelMath, deadtime) {
TEST(FuelMath, CylinderFuelTrim) {
EngineTestHelper eth(TEST_ENGINE);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{1, 50.0f}));
setTable(config->fuelTrims[0].table, -4);
@ -265,26 +265,26 @@ TEST(FuelMath, IdleVeTable) {
// Gets normal VE table
idler.isIdling = false;
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.5f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.5f);
// Gets idle VE table
idler.isIdling = true;
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.4f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.4f);
// Below half threshold, fully use idle VE table
Sensor::setMockValue(SensorType::Tps1, 0);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.4f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.4f);
Sensor::setMockValue(SensorType::Tps1, 2);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.4f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.4f);
Sensor::setMockValue(SensorType::Tps1, 5);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.4f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.4f);
// As TPS approaches idle threshold, phase-out the idle VE table
Sensor::setMockValue(SensorType::Tps1, 6);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.42f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.42f);
Sensor::setMockValue(SensorType::Tps1, 8);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.46f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.46f);
Sensor::setMockValue(SensorType::Tps1, 10);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50), 0.5f);
EXPECT_FLOAT_EQ(dut.getVe(1000, 50, false), 0.5f);
}

View File

@ -52,7 +52,7 @@ TEST(ignition, trailingSpark) {
*/
engine->enableOverdwellProtection = false;
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);

View File

@ -178,7 +178,7 @@ TEST(misc, testRpmCalculator) {
// These tests were written when the default target AFR was 14.0, so replicate that
engineConfiguration->stoichRatioPrimary = 14;
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
IgnitionEventList *ilist = &engine->ignitionEvents;
@ -416,7 +416,7 @@ static void assertInjectionEventBatch(const char *msg, InjectionEvent *ev, int i
static void setTestBug299(EngineTestHelper *eth) {
setupSimpleTestEngineWithMafAndTT_ONE_trigger(eth);
EXPECT_CALL(*eth->mockAirmass, getAirmass(_))
EXPECT_CALL(*eth->mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008001f, 50.0f}));
Engine *engine = &eth->engine;
@ -791,7 +791,7 @@ TEST(big, testTwoWireBatch) {
EngineTestHelper eth(TEST_ENGINE);
setTable(config->injectionPhase, -180.0f);
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
engineConfiguration->injectionMode = IM_BATCH;
@ -819,7 +819,7 @@ TEST(big, testTwoWireBatch) {
TEST(big, testSequential) {
EngineTestHelper eth(TEST_ENGINE);
setTable(config->injectionPhase, -180.0f);
EXPECT_CALL(*eth.mockAirmass, getAirmass(_))
EXPECT_CALL(*eth.mockAirmass, getAirmass(_, _))
.WillRepeatedly(Return(AirmassResult{0.1008f, 50.0f}));
setupSimpleTestEngineWithMafAndTT_ONE_trigger(&eth);