From 49ed4d40a24fd371f9914b09eeeb1120e83daa49 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Thu, 23 Jul 2020 01:12:38 -0700 Subject: [PATCH 1/4] base class --- firmware/controllers/algo/airmass/airmass.cpp | 10 ++++++++++ firmware/controllers/algo/airmass/airmass.h | 16 ++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 firmware/controllers/algo/airmass/airmass.cpp diff --git a/firmware/controllers/algo/airmass/airmass.cpp b/firmware/controllers/algo/airmass/airmass.cpp new file mode 100644 index 0000000000..3ede8519e1 --- /dev/null +++ b/firmware/controllers/algo/airmass/airmass.cpp @@ -0,0 +1,10 @@ +#include "airmass.h" + +AirmassModelBase::AirmassModelBase(const ValueProvider3D& veTable) : m_veTable(&veTable) {} + +float AirmassModelBase::getVe(int rpm, float load) const { + efiAssert(OBD_PCM_Processor_Fault, m_veTable != nullptr, "VE table null", 0); + + // TODO: allow override of the Y axis value based on a config field + return m_veTable->getValue(rpm, load); +} diff --git a/firmware/controllers/algo/airmass/airmass.h b/firmware/controllers/algo/airmass/airmass.h index a5ad25950c..338741da5f 100644 --- a/firmware/controllers/algo/airmass/airmass.h +++ b/firmware/controllers/algo/airmass/airmass.h @@ -1,6 +1,22 @@ #pragma once +#include "engine.h" + struct AirmassResult { float CylinderAirmass = 0; float EngineLoadPercent = 100; }; + +struct AirmassModelBase { + DECLARE_ENGINE_PTR; + + AirmassModelBase(const ValueProvider3D& veTable); + virtual AirmassResult getAirmass(int rpm) = 0; + +protected: + // Retrieve the user-calibrated volumetric efficiency from the table + float getVe(int rpm, percent_t load) const; + +private: + const ValueProvider3D* const m_veTable; +}; From 5c156ebada55043e3e94966e0f348ab4e93b3c63 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Thu, 23 Jul 2020 01:23:57 -0700 Subject: [PATCH 2/4] move impl --- .../controllers/algo/airmass/maf_airmass.cpp | 46 +++++++++++++++++++ .../controllers/algo/airmass/maf_airmass.h | 13 ++++++ firmware/controllers/algo/algo.mk | 2 + firmware/controllers/algo/fuel_math.cpp | 43 +++-------------- firmware/controllers/algo/fuel_math.h | 2 - 5 files changed, 67 insertions(+), 39 deletions(-) create mode 100644 firmware/controllers/algo/airmass/maf_airmass.cpp create mode 100644 firmware/controllers/algo/airmass/maf_airmass.h diff --git a/firmware/controllers/algo/airmass/maf_airmass.cpp b/firmware/controllers/algo/airmass/maf_airmass.cpp new file mode 100644 index 0000000000..c9324858d6 --- /dev/null +++ b/firmware/controllers/algo/airmass/maf_airmass.cpp @@ -0,0 +1,46 @@ +#include "global.h" +#include "engine.h" +#include "maf_airmass.h" +#include "maf.h" + +EXTERN_ENGINE; + +AirmassResult MafAirmass::getAirmass(int rpm) { + float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); + return getAirmassImpl(maf, rpm); +} + +/** + * 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 { + // If the engine is stopped, MAF is meaningless + if (rpm == 0) { + return {}; + } + + // kg/hr -> g/s + float gramPerSecond = massAirFlow * 1000 / 3600; + + // 1/min -> 1/s + float revsPerSecond = rpm / 60.0f; + float airPerRevolution = gramPerSecond / revsPerSecond; + + // Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev + // This math is floating point to work properly on engines with odd cyl count + float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f; + + float cylinderAirmass = airPerRevolution / halfCylCount; + + //Create % load for fuel table using relative naturally aspiratedcylinder filling + float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge); + + //Correct air mass by VE table + float correctedAirmass = cylinderAirmass * getVe(rpm, airChargeLoad) / 100; + + return { + correctedAirmass, + airChargeLoad, // AFR/VE/ignition table Y axis + }; +} diff --git a/firmware/controllers/algo/airmass/maf_airmass.h b/firmware/controllers/algo/airmass/maf_airmass.h new file mode 100644 index 0000000000..ac557bd084 --- /dev/null +++ b/firmware/controllers/algo/airmass/maf_airmass.h @@ -0,0 +1,13 @@ +#pragma once + +#include "airmass.h" + +class MafAirmass final : public AirmassModelBase { +public: + MafAirmass(const ValueProvider3D& veTable) : AirmassModelBase(veTable) {} + + AirmassResult getAirmass(int rpm) override; + + // Compute airmass based on flow & engine speed + AirmassResult getAirmassImpl(float massAirFlow, int rpm) const; +}; diff --git a/firmware/controllers/algo/algo.mk b/firmware/controllers/algo/algo.mk index b4f6ed9fd6..e76d082c9f 100644 --- a/firmware/controllers/algo/algo.mk +++ b/firmware/controllers/algo/algo.mk @@ -11,4 +11,6 @@ CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/advance_map.cpp \ $(PROJECT_DIR)/controllers/algo/engine2.cpp \ $(PROJECT_DIR)/controllers/gauges/lcd_menu_tree.cpp \ $(PROJECT_DIR)/controllers/algo/event_registry.cpp \ + $(PROJECT_DIR)/controllers/algo/airmass/airmass.cpp \ + $(PROJECT_DIR)/controllers/algo/airmass/maf_airmass.cpp \ $(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \ diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp index 02d31504b5..4f56c7eba8 100644 --- a/firmware/controllers/algo/fuel_math.cpp +++ b/firmware/controllers/algo/fuel_math.cpp @@ -23,6 +23,7 @@ #include "global.h" #include "airmass.h" +#include "maf_airmass.h" #include "fuel_math.h" #include "interpolation.h" #include "engine_configuration.h" @@ -153,41 +154,6 @@ floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX) { /* DISPLAY_ENDIF */ -/** - * 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 getRealMafAirmass(float airSpeed, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { - // If the engine is stopped, MAF is meaningless - if (rpm == 0) { - return {}; - } - - // kg/hr -> g/s - float gramPerSecond = airSpeed * 1000 / 3600; - - // 1/min -> 1/s - float revsPerSecond = rpm / 60.0f; - float airPerRevolution = gramPerSecond / revsPerSecond; - - // Now we have to divide among cylinders - on a 4 stroke, half of the cylinders happen every rev - // This math is floating point to work properly on engines with odd cyl count - float halfCylCount = CONFIG(specs.cylindersCount) / 2.0f; - - float cylinderAirmass = airPerRevolution / halfCylCount; - - //Create % load for fuel table using relative naturally aspiratedcylinder filling - float airChargeLoad = 100 * cylinderAirmass / ENGINE(standardAirCharge); - - //Correct air mass by VE table - float correctedAirmass = cylinderAirmass * veMap.getValue(rpm, airChargeLoad) / 100; - - return { - correctedAirmass, - airChargeLoad, // AFR/VE table Y axis - }; -} - constexpr float convertToGramsPerSecond(float ccPerMinute) { float ccPerSecond = ccPerMinute / 60; return ccPerSecond * 0.72f; // 0.72g/cc fuel density @@ -202,13 +168,14 @@ float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PAR return airMass / (afr * gPerSec); } +static MafAirmass mafAirmass(veMap); + AirmassResult getAirmass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { switch (CONFIG(fuelAlgorithm)) { case LM_SPEED_DENSITY: return getSpeedDensityAirmass(PASS_ENGINE_PARAMETER_SIGNATURE); case LM_REAL_MAF: { - float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); - return getRealMafAirmass(maf, rpm PASS_ENGINE_PARAMETER_SUFFIX); + return mafAirmass.getAirmass(rpm); } default: firmwareError(CUSTOM_ERR_ASSERT, "Fuel mode %d is not airmass mode", CONFIG(fuelAlgorithm)); return {}; @@ -384,6 +351,8 @@ floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX) { * is to prepare the fuel map data structure for 3d interpolation */ void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) { + INJECT_ENGINE_REFERENCE(&mafAirmass); + fuelMap.init(config->fuelTable, config->fuelLoadBins, config->fuelRpmBins); #if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT) fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins); diff --git a/firmware/controllers/algo/fuel_math.h b/firmware/controllers/algo/fuel_math.h index d0b52aebff..28a88787a5 100644 --- a/firmware/controllers/algo/fuel_math.h +++ b/firmware/controllers/algo/fuel_math.h @@ -22,8 +22,6 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); */ floatms_t getRunningFuel(floatms_t baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX); -AirmassResult getRealMafAirmass(float airMass, int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); - floatms_t getBaseTableFuel(int rpm, float engineLoad); float getBaroCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE); int getNumberOfInjections(injection_mode_e mode DECLARE_ENGINE_PARAMETER_SUFFIX); From aeb5c8e8d6894ed4e9bf72c407749d300b1d6f23 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Thu, 23 Jul 2020 01:24:02 -0700 Subject: [PATCH 3/4] improve test --- unit_tests/tests/test_fuel_map.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/unit_tests/tests/test_fuel_map.cpp b/unit_tests/tests/test_fuel_map.cpp index 8d4d53c938..896960cc22 100644 --- a/unit_tests/tests/test_fuel_map.cpp +++ b/unit_tests/tests/test_fuel_map.cpp @@ -6,6 +6,7 @@ */ #include "fuel_math.h" +#include "maf_airmass.h" #include "trigger_structure.h" #include "allsensors.h" #include "engine_math.h" @@ -14,18 +15,25 @@ #include "efi_gpio.h" #include "advance_map.h" #include "sensor.h" +#include "mocks.h" + +using ::testing::FloatNear; TEST(misc, testMafFuelMath) { WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); - extern fuel_Map3D_t veMap; - veMap.setAll(75); - engineConfiguration->fuelAlgorithm = LM_REAL_MAF; engineConfiguration->injector.flow = 200; - setAfrMap(config->afrTable, 13); - auto airmass = getRealMafAirmass(200, 6000 PASS_ENGINE_PARAMETER_SUFFIX); + MockVp3d veTable; + // Ensure that the correct cell is read from the VE table + EXPECT_CALL(veTable, getValue(6000, FloatNear(70.9814f, EPS4D))) + .WillOnce(Return(75.0f)); + + MafAirmass dut(veTable); + INJECT_ENGINE_REFERENCE(&dut); + + auto airmass = dut.getAirmassImpl(200, 6000); // Check results EXPECT_NEAR(0.277777f * 0.75f, airmass.CylinderAirmass, EPS4D); From fceac1b131fac79b13df7f45baf6e0ff8c30662d Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Fri, 24 Jul 2020 11:44:54 -0700 Subject: [PATCH 4/4] format --- firmware/controllers/algo/airmass/maf_airmass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/firmware/controllers/algo/airmass/maf_airmass.cpp b/firmware/controllers/algo/airmass/maf_airmass.cpp index c9324858d6..7129b0746c 100644 --- a/firmware/controllers/algo/airmass/maf_airmass.cpp +++ b/firmware/controllers/algo/airmass/maf_airmass.cpp @@ -6,8 +6,8 @@ EXTERN_ENGINE; AirmassResult MafAirmass::getAirmass(int rpm) { - float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); - return getAirmassImpl(maf, rpm); + float maf = getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE) + engine->engineLoadAccelEnrichment.getEngineLoadEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE); + return getAirmassImpl(maf, rpm); } /**