diff --git a/firmware/config/boards/kinetis/config/controllers/algo/auto_generated_enums.cpp b/firmware/config/boards/kinetis/config/controllers/algo/auto_generated_enums.cpp index c33a1762a9..125570e968 100644 --- a/firmware/config/boards/kinetis/config/controllers/algo/auto_generated_enums.cpp +++ b/firmware/config/boards/kinetis/config/controllers/algo/auto_generated_enums.cpp @@ -595,6 +595,8 @@ case LM_REAL_MAF: return "LM_REAL_MAF"; case LM_SPEED_DENSITY: return "LM_SPEED_DENSITY"; +case LM_ALPHA_N_2: + return "LM_ALPHA_N_2"; } return NULL; } diff --git a/firmware/controllers/algo/airmass/alphan_airmass.cpp b/firmware/controllers/algo/airmass/alphan_airmass.cpp new file mode 100644 index 0000000000..e9c081edb4 --- /dev/null +++ b/firmware/controllers/algo/airmass/alphan_airmass.cpp @@ -0,0 +1,27 @@ +#include "alphan_airmass.h" +#include "sensor.h" + +AirmassResult AlphaNAirmass::getAirmass(int rpm) { + auto tps = Sensor::get(SensorType::Tps1); + + if (!tps.Valid) { + // We are fully reliant on TPS - if the TPS fails, stop the engine. + return {}; + } + + // In this case, VE directly describes the cylinder filling relative to the ideal + float ve = getVe(rpm, tps.Value); + + // TODO: should this be barometric pressure and/or temperature compensated? + float airmass = getAirmassImpl( + ve / 100.0f, + 101.325f, // std atmosphere pressure + 273.0f + 20.0f // std atmosphere pressure + PASS_ENGINE_PARAMETER_SUFFIX + ); + + return { + airmass, + tps.Value + }; +} diff --git a/firmware/controllers/algo/airmass/alphan_airmass.h b/firmware/controllers/algo/airmass/alphan_airmass.h new file mode 100644 index 0000000000..aeb0201a83 --- /dev/null +++ b/firmware/controllers/algo/airmass/alphan_airmass.h @@ -0,0 +1,10 @@ +#pragma once + +#include "speed_density_base.h" + +class AlphaNAirmass : public SpeedDensityBase { +public: + AlphaNAirmass(const ValueProvider3D& veTable) : SpeedDensityBase(veTable) {} + + AirmassResult getAirmass(int rpm) override; +}; diff --git a/firmware/controllers/algo/algo.mk b/firmware/controllers/algo/algo.mk index e0ae8d0ff0..e9ed7484b1 100644 --- a/firmware/controllers/algo/algo.mk +++ b/firmware/controllers/algo/algo.mk @@ -12,6 +12,7 @@ CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/advance_map.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/alphan_airmass.cpp \ $(PROJECT_DIR)/controllers/algo/airmass/maf_airmass.cpp \ $(PROJECT_DIR)/controllers/algo/airmass/speed_density_airmass.cpp \ $(PROJECT_DIR)/controllers/algo/airmass/speed_density_base.cpp \ diff --git a/firmware/controllers/algo/auto_generated_enums.cpp b/firmware/controllers/algo/auto_generated_enums.cpp index 3290b2a344..3174670568 100644 --- a/firmware/controllers/algo/auto_generated_enums.cpp +++ b/firmware/controllers/algo/auto_generated_enums.cpp @@ -733,6 +733,8 @@ case LM_REAL_MAF: return "LM_REAL_MAF"; case LM_SPEED_DENSITY: return "LM_SPEED_DENSITY"; +case LM_ALPHA_N_2: + return "LM_ALPHA_N_2"; } return NULL; } diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp index 2eda621de7..7de041a545 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 "alphan_airmass.h" #include "maf_airmass.h" #include "speed_density_airmass.h" #include "fuel_math.h" @@ -171,11 +172,13 @@ float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PAR static SpeedDensityAirmass sdAirmass(veMap); static MafAirmass mafAirmass(veMap); +static AlphaNAirmass alphaNAirmass(veMap); AirmassModelBase* getAirmassModel(DECLARE_ENGINE_PARAMETER_SIGNATURE) { switch (CONFIG(fuelAlgorithm)) { case LM_SPEED_DENSITY: return &sdAirmass; case LM_REAL_MAF: return &mafAirmass; + case LM_ALPHA_N_2: return &alphaNAirmass; default: return nullptr; } } @@ -193,7 +196,9 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { floatms_t baseFuel; - if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) || (engineConfiguration->fuelAlgorithm == LM_REAL_MAF)) { + if ((CONFIG(fuelAlgorithm) == LM_SPEED_DENSITY) || + (engineConfiguration->fuelAlgorithm == LM_REAL_MAF) || + (engineConfiguration->fuelAlgorithm == LM_ALPHA_N_2)) { // airmass modes - get airmass first, then convert to fuel auto model = getAirmassModel(PASS_ENGINE_PARAMETER_SIGNATURE); efiAssert(CUSTOM_ERR_ASSERT, model != nullptr, "Invalid airmass mode", 0.0f); diff --git a/firmware/controllers/algo/rusefi_enums.h b/firmware/controllers/algo/rusefi_enums.h index 891a0fb55f..9736c26d92 100644 --- a/firmware/controllers/algo/rusefi_enums.h +++ b/firmware/controllers/algo/rusefi_enums.h @@ -443,6 +443,9 @@ typedef enum { */ LM_REAL_MAF = 4, + // todo: rename after LM_ALPHA_N is removed + LM_ALPHA_N_2 = 5, + Force_4_bytes_size_engine_load_mode = ENUM_32_BITS, } engine_load_mode_e; diff --git a/firmware/controllers/math/engine_math.cpp b/firmware/controllers/math/engine_math.cpp index 7021cba6aa..b96015b859 100644 --- a/firmware/controllers/math/engine_math.cpp +++ b/firmware/controllers/math/engine_math.cpp @@ -75,6 +75,7 @@ float getEngineLoadT(DECLARE_ENGINE_PARAMETER_SIGNATURE) { case LM_SPEED_DENSITY: return getMap(PASS_ENGINE_PARAMETER_SIGNATURE); case LM_ALPHA_N: + case LM_ALPHA_N_2: return Sensor::get(SensorType::Tps1).value_or(0); case LM_REAL_MAF: return getRealMaf(PASS_ENGINE_PARAMETER_SIGNATURE); diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index eeae813e95..0ed08df76a 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -457,7 +457,7 @@ int sensorSnifferRpmThreshold;+Disable sensor sniffer above this rpm;"RPM", int rpmHardLimit;set rpm_hard_limit X;"rpm", 1, 0, 0, 20000.0, 2 -#define engine_load_mode_e_enum "MAF", "Alpha-N/TPS", "INVALID", "SPEED DENSITY", "MAF Air Charge", "INVALID", "INVALID" +#define engine_load_mode_e_enum "MAF", "Alpha-N/TPS", "INVALID", "SPEED DENSITY", "MAF Air Charge", "Alpha-N", "INVALID" custom engine_load_mode_e 4 bits, U32, @OFFSET@, [0:2], @@engine_load_mode_e_enum@@ diff --git a/unit_tests/tests/test_fuel_map.cpp b/unit_tests/tests/test_fuel_map.cpp index 896960cc22..1736cc7c89 100644 --- a/unit_tests/tests/test_fuel_map.cpp +++ b/unit_tests/tests/test_fuel_map.cpp @@ -6,7 +6,6 @@ */ #include "fuel_math.h" -#include "maf_airmass.h" #include "trigger_structure.h" #include "allsensors.h" #include "engine_math.h" @@ -19,27 +18,6 @@ using ::testing::FloatNear; -TEST(misc, testMafFuelMath) { - WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); - engineConfiguration->fuelAlgorithm = LM_REAL_MAF; - engineConfiguration->injector.flow = 200; - setAfrMap(config->afrTable, 13); - - 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); - EXPECT_NEAR(70.9814f, airmass.EngineLoadPercent, EPS4D); -} - TEST(misc, testFuelMap) { printf("Setting up FORD_ASPIRE_1996\r\n"); WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); diff --git a/unit_tests/tests/test_fuel_math.cpp b/unit_tests/tests/test_fuel_math.cpp index 9278203044..b95084e603 100644 --- a/unit_tests/tests/test_fuel_math.cpp +++ b/unit_tests/tests/test_fuel_math.cpp @@ -1,8 +1,14 @@ #include "engine_test_helper.h" #include "fuel_math.h" +#include "alphan_airmass.h" +#include "maf_airmass.h" +#include "mocks.h" #include "gtest/gtest.h" +using ::testing::StrictMock; +using ::testing::FloatNear; + TEST(FuelMath, getStandardAirCharge) { WITH_ENGINE_TEST_HELPER(TEST_ENGINE); @@ -29,3 +35,66 @@ TEST(FuelMath, getStandardAirCharge) { EXPECT_FLOAT_EQ(4.782959f, getStandardAirCharge(PASS_ENGINE_PARAMETER_SIGNATURE)); } + +TEST(AirmassModes, AlphaNNormal) { + WITH_ENGINE_TEST_HELPER(TEST_ENGINE); + // 4 cylinder 4 liter = easy math + engineConfiguration->specs.displacement = 4.0f; + engineConfiguration->specs.cylindersCount = 4; + + StrictMock veTable; + + EXPECT_CALL(veTable, getValue(1200, FloatNear(0.71f, EPS4D))) + .WillOnce(Return(35.0f)); + + AlphaNAirmass dut(veTable); + INJECT_ENGINE_REFERENCE(&dut); + + Sensor::setMockValue(SensorType::Tps1, 0.71f); + + // Mass of 1 liter of air * VE + float expectedAirmass = 1.2047f * 0.35f; + + auto result = dut.getAirmass(1200); + EXPECT_NEAR(result.CylinderAirmass, expectedAirmass, EPS4D); + EXPECT_NEAR(result.EngineLoadPercent, 0.71f, EPS4D); +} + +TEST(AirmassModes, AlphaNFailedTps) { + WITH_ENGINE_TEST_HELPER(TEST_ENGINE); + + // Shouldn't get called + StrictMock veTable; + + AlphaNAirmass dut(veTable); + INJECT_ENGINE_REFERENCE(&dut); + + // explicitly reset the sensor + Sensor::resetMockValue(SensorType::Tps1); + // Ensure that it's actually failed + ASSERT_FALSE(Sensor::get(SensorType::Tps1).Valid); + + auto result = dut.getAirmass(1200); + EXPECT_EQ(result.CylinderAirmass, 0); +} + +TEST(misc, MafNormal) { + WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); + engineConfiguration->fuelAlgorithm = LM_REAL_MAF; + engineConfiguration->injector.flow = 200; + setAfrMap(config->afrTable, 13); + + 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); + EXPECT_NEAR(70.9814f, airmass.EngineLoadPercent, EPS4D); +}