From 1d0a244fa4693b3c8e1f8b3157cd4b3fe7d0793a Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Mon, 10 Aug 2020 21:40:19 -0700 Subject: [PATCH 1/4] add fuel computer --- firmware/controllers/algo/algo.mk | 1 + .../controllers/algo/fuel/fuel_computer.cpp | 27 ++++++++++++++ .../controllers/algo/fuel/fuel_computer.h | 36 +++++++++++++++++++ firmware/controllers/controllers.mk | 1 + 4 files changed, 65 insertions(+) create mode 100644 firmware/controllers/algo/fuel/fuel_computer.cpp create mode 100644 firmware/controllers/algo/fuel/fuel_computer.h diff --git a/firmware/controllers/algo/algo.mk b/firmware/controllers/algo/algo.mk index e9ed7484b1..45a14ecd2b 100644 --- a/firmware/controllers/algo/algo.mk +++ b/firmware/controllers/algo/algo.mk @@ -16,3 +16,4 @@ CONTROLLERS_ALGO_SRC_CPP = $(PROJECT_DIR)/controllers/algo/advance_map.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 \ + $(PROJECT_DIR)/controllers/algo/fuel/fuel_computer.cpp \ diff --git a/firmware/controllers/algo/fuel/fuel_computer.cpp b/firmware/controllers/algo/fuel/fuel_computer.cpp new file mode 100644 index 0000000000..98d4153722 --- /dev/null +++ b/firmware/controllers/algo/fuel/fuel_computer.cpp @@ -0,0 +1,27 @@ +#include "fuel_computer.h" + +EXTERN_ENGINE; + +mass_t FuelComputerBase::getCycleFuel(mass_t airmass, int rpm, float load) const { + float stoich = getStoichiometricRatio(); + float lambda = getTargetLambda(rpm, load); + float afr = stoich * lambda; + + ENGINE(engineState.targetAFR) = afr; + + return airmass / afr; +} + +FuelComputer::FuelComputer(const ValueProvider3D& afrTable) : m_afrTable(&afrTable) {} + +float FuelComputer::getStoichiometricRatio() const { + // TODO: vary this with ethanol content/configured setting/whatever + return 14.7f; +} + +float FuelComputer::getTargetLambda(int rpm, float load) const { + efiAssert(OBD_PCM_Processor_Fault, m_afrTable != nullptr, "AFR table null", 0); + + // TODO: set the table value in lambda instead of afr + return m_afrTable->getValue(rpm, load) / getStoichiometricRatio(); +}; diff --git a/firmware/controllers/algo/fuel/fuel_computer.h b/firmware/controllers/algo/fuel/fuel_computer.h new file mode 100644 index 0000000000..44864f9a27 --- /dev/null +++ b/firmware/controllers/algo/fuel/fuel_computer.h @@ -0,0 +1,36 @@ +#pragma once + +#include "engine.h" + +class ValueProvider3D; + +using mass_t = float; + +struct IFuelComputer { + virtual mass_t getCycleFuel(mass_t airmass, int rpm, float load) const = 0; +}; + +// This contains the math of the fuel model, but doesn't actually read any configuration +class FuelComputerBase : public IFuelComputer { +public: + mass_t getCycleFuel(mass_t airmass, int rpm, float load) const override; + +protected: + virtual float getStoichiometricRatio() const = 0; + virtual float getTargetLambda(int rpm, float load) const = 0; +}; + +// This class is a usable implemenation of a fuel model that reads real configuration +class FuelComputer final : public FuelComputerBase { + DECLARE_ENGINE_PTR; + +public: + FuelComputer(const ValueProvider3D& afrTable); + +protected: + float getStoichiometricRatio() const override; + float getTargetLambda(int rpm, float load) const override; + +private: + const ValueProvider3D* const m_afrTable; +}; diff --git a/firmware/controllers/controllers.mk b/firmware/controllers/controllers.mk index 4fb3a605ee..73a5cac59e 100644 --- a/firmware/controllers/controllers.mk +++ b/firmware/controllers/controllers.mk @@ -56,6 +56,7 @@ CONTROLLERS_INC=\ $(CONTROLLERS_DIR)/system/timer \ $(CONTROLLERS_DIR)/algo \ $(CONTROLLERS_DIR)/algo/airmass \ + $(CONTROLLERS_DIR)/algo/fuel \ $(CONTROLLERS_DIR)/engine_cycle \ $(CONTROLLERS_DIR)/trigger/decoders \ $(CONTROLLERS_DIR)/trigger \ From 1f64754b57b00ee45ba24e509730a6bf6400edb4 Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Mon, 10 Aug 2020 21:41:03 -0700 Subject: [PATCH 2/4] consume --- firmware/console/status_loop.cpp | 1 - firmware/controllers/algo/fuel_math.cpp | 14 +++++----- firmware/controllers/algo/fuel_math.h | 1 - unit_tests/tests/test_speed_density.cpp | 34 ------------------------- unit_tests/tests/tests.mk | 1 - 5 files changed, 7 insertions(+), 44 deletions(-) delete mode 100644 unit_tests/tests/test_speed_density.cpp diff --git a/firmware/console/status_loop.cpp b/firmware/console/status_loop.cpp index a085b1cb32..064f7cb2f5 100644 --- a/firmware/console/status_loop.cpp +++ b/firmware/console/status_loop.cpp @@ -290,7 +290,6 @@ static void showFuelInfo2(float rpm, float engineLoad) { float magicAir = SpeedDensityBase::getAirmassImpl(1, 100, convertCelsiusToKelvin(20) PASS_ENGINE_PARAMETER_SUFFIX); - scheduleMsg(&logger, "SD magic fuel %.2f", getInjectionDurationForAirmass(magicAir, 14.7 PASS_ENGINE_PARAMETER_SUFFIX)); scheduleMsg(&logger, "inj flow %.2fcc/min displacement %.2fL", engineConfiguration->injector.flow, engineConfiguration->specs.displacement); diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp index 06d01fc034..bccd9f01b7 100644 --- a/firmware/controllers/algo/fuel_math.cpp +++ b/firmware/controllers/algo/fuel_math.cpp @@ -27,6 +27,7 @@ #include "maf_airmass.h" #include "speed_density_airmass.h" #include "fuel_math.h" +#include "fuel_computer.h" #include "interpolation.h" #include "engine_configuration.h" #include "allsensors.h" @@ -163,10 +164,10 @@ constexpr float convertToGramsPerSecond(float ccPerMinute) { /** * @return per cylinder injection time, in seconds */ -float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PARAMETER_SUFFIX) { +static float getInjectionDurationForFuelMass(float fuelMass DECLARE_ENGINE_PARAMETER_SUFFIX) { float gPerSec = convertToGramsPerSecond(CONFIG(injector.flow)); - return airMass / (afr * gPerSec); + return fuelMass / gPerSec; } static SpeedDensityAirmass sdAirmass(veMap); @@ -185,6 +186,8 @@ AirmassModelBase* getAirmassModel(DECLARE_ENGINE_PARAMETER_SIGNATURE) { } } +static FuelComputer fuelComputer(afrMap); + /** * per-cylinder fuel amount * todo: rename this method since it's now base+TPSaccel @@ -202,17 +205,14 @@ floatms_t getBaseFuel(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { auto airmass = model->getAirmass(rpm); - // The airmass mode will tell us how to look up AFR - use the provided Y axis value - float targetAfr = afrMap.getValue(rpm, airmass.EngineLoadPercent); - // Plop some state for others to read - ENGINE(engineState.targetAFR) = targetAfr; ENGINE(engineState.sd.airMassInOneCylinder) = airmass.CylinderAirmass; ENGINE(engineState.fuelingLoad) = airmass.EngineLoadPercent; // TODO: independently selectable ignition load mode ENGINE(engineState.ignitionLoad) = airmass.EngineLoadPercent; - float baseFuel = getInjectionDurationForAirmass(airmass.CylinderAirmass, targetAfr PASS_ENGINE_PARAMETER_SUFFIX) * 1000; + float baseFuelMass = fuelComputer.getCycleFuel(airmass.CylinderAirmass, rpm, airmass.EngineLoadPercent); + float baseFuel = getInjectionDurationForFuelMass(baseFuelMass PASS_ENGINE_PARAMETER_SUFFIX) * 1000; efiAssert(CUSTOM_ERR_ASSERT, !cisnan(baseFuel), "NaN baseFuel", 0); engine->engineState.baseFuel = baseFuel; diff --git a/firmware/controllers/algo/fuel_math.h b/firmware/controllers/algo/fuel_math.h index d39f226229..e424369a41 100644 --- a/firmware/controllers/algo/fuel_math.h +++ b/firmware/controllers/algo/fuel_math.h @@ -39,4 +39,3 @@ float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE); // convert injection duration (Ms/Nt) to fuel rate (L/h) float getFuelRate(floatms_t totalInjDuration, efitick_t timePeriod DECLARE_ENGINE_PARAMETER_SUFFIX); -float getInjectionDurationForAirmass(float airMass, float afr DECLARE_ENGINE_PARAMETER_SUFFIX); diff --git a/unit_tests/tests/test_speed_density.cpp b/unit_tests/tests/test_speed_density.cpp deleted file mode 100644 index a212a93bb2..0000000000 --- a/unit_tests/tests/test_speed_density.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file test_speed_density.cpp - * - * @date Jun 26, 2014 - * @author Andrey Belomutskiy, (c) 2012-2020 - */ - -#include "engine_test_helper.h" -#include "speed_density.h" -#include "fuel_math.h" - -TEST(big, testSpeedDensity) { - printf("*************************************************** testSpeedDensity\r\n"); - WITH_ENGINE_TEST_HELPER(FORD_INLINE_6_1995); - - engineConfiguration->trigger.customTotalToothCount = 8; - eth.applyTriggerWaveform(); - - eth.fireTriggerEvents(36); - ASSERT_EQ( 1500, GET_RPM()) << "RPM"; - - // 427 cubic inches, that's a LOT of engine - engineConfiguration->specs.displacement = 6.99728; - engineConfiguration->specs.cylindersCount = 8; - - engineConfiguration->injector.flow = gramm_second_to_cc_minute(5.303); - - float airMass = SpeedDensityBase::getAirmassImpl(0.92, 98, 293.16 PASS_ENGINE_PARAMETER_SUFFIX); - - ASSERT_FLOAT_EQ(0.9371106624, airMass); - - // 0.01414 sec or 14.14 ms - EXPECT_NEAR(0.014137, getInjectionDurationForAirmass(airMass, 12.5 PASS_ENGINE_PARAMETER_SUFFIX), EPS4D); -} diff --git a/unit_tests/tests/tests.mk b/unit_tests/tests/tests.mk index 2051f8fc50..bac33b4443 100644 --- a/unit_tests/tests/tests.mk +++ b/unit_tests/tests/tests.mk @@ -29,7 +29,6 @@ TESTS_SRC_CPP = \ tests/test_one_cylinder_logic.cpp \ tests/test_pwm_generator.cpp \ tests/test_logic_expression.cpp \ - tests/test_speed_density.cpp \ tests/test_signal_executor.cpp \ tests/test_cpp_memory_layout.cpp \ tests/test_sensors.cpp \ From 3c72fa93e4df3d396647aefa3ef4820be32cd39c Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Mon, 10 Aug 2020 21:53:45 -0700 Subject: [PATCH 3/4] fix --- firmware/controllers/algo/fuel/fuel_computer.h | 4 ++-- firmware/controllers/algo/fuel_math.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware/controllers/algo/fuel/fuel_computer.h b/firmware/controllers/algo/fuel/fuel_computer.h index 44864f9a27..430076fa75 100644 --- a/firmware/controllers/algo/fuel/fuel_computer.h +++ b/firmware/controllers/algo/fuel/fuel_computer.h @@ -13,6 +13,8 @@ struct IFuelComputer { // This contains the math of the fuel model, but doesn't actually read any configuration class FuelComputerBase : public IFuelComputer { public: + DECLARE_ENGINE_PTR; + mass_t getCycleFuel(mass_t airmass, int rpm, float load) const override; protected: @@ -22,8 +24,6 @@ protected: // This class is a usable implemenation of a fuel model that reads real configuration class FuelComputer final : public FuelComputerBase { - DECLARE_ENGINE_PTR; - public: FuelComputer(const ValueProvider3D& afrTable); diff --git a/firmware/controllers/algo/fuel_math.cpp b/firmware/controllers/algo/fuel_math.cpp index bccd9f01b7..221d725f67 100644 --- a/firmware/controllers/algo/fuel_math.cpp +++ b/firmware/controllers/algo/fuel_math.cpp @@ -345,6 +345,8 @@ floatms_t getInjectorLag(float vBatt DECLARE_ENGINE_PARAMETER_SUFFIX) { void initFuelMap(DECLARE_ENGINE_PARAMETER_SIGNATURE) { INJECT_ENGINE_REFERENCE(&sdAirmass); INJECT_ENGINE_REFERENCE(&mafAirmass); + INJECT_ENGINE_REFERENCE(&alphaNAirmass); + INJECT_ENGINE_REFERENCE(&fuelComputer); #if (IGN_LOAD_COUNT == FUEL_LOAD_COUNT) && (IGN_RPM_COUNT == FUEL_RPM_COUNT) fuelPhaseMap.init(config->injectionPhase, config->injPhaseLoadBins, config->injPhaseRpmBins); From b961944790e036f7157e245ff06bef06d28b9c7b Mon Sep 17 00:00:00 2001 From: Matthew Kennedy Date: Mon, 10 Aug 2020 22:11:25 -0700 Subject: [PATCH 4/4] tests --- .../ignition_injection/test_fuel_computer.cpp | 41 +++++++++++++++++++ unit_tests/tests/test_fuel_math.cpp | 2 +- unit_tests/tests/tests.mk | 1 + 3 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 unit_tests/tests/ignition_injection/test_fuel_computer.cpp diff --git a/unit_tests/tests/ignition_injection/test_fuel_computer.cpp b/unit_tests/tests/ignition_injection/test_fuel_computer.cpp new file mode 100644 index 0000000000..9b666f1237 --- /dev/null +++ b/unit_tests/tests/ignition_injection/test_fuel_computer.cpp @@ -0,0 +1,41 @@ +#include "engine_test_helper.h" + +// sneaky... +#define protected public +#include "fuel_computer.h" +#include "mocks.h" + +#include "gtest/gtest.h" + +using ::testing::FloatEq; + +class MockFuelComputer : public FuelComputerBase { +public: + MOCK_METHOD(float, getStoichiometricRatio, (), (const, override)); + MOCK_METHOD(float, getTargetLambda, (int rpm, float load), (const, override)); +}; + +TEST(FuelComputer, getCycleFuel) { + WITH_ENGINE_TEST_HELPER(TEST_ENGINE); + + MockFuelComputer dut; + INJECT_ENGINE_REFERENCE(&dut); + + EXPECT_CALL(dut, getStoichiometricRatio()) + .WillOnce(Return(3.0f)); + EXPECT_CALL(dut, getTargetLambda(1000, FloatEq(0.8f))) + .WillOnce(Return(5.0f)); + + auto result = dut.getCycleFuel(7.0f, 1000, 0.8f); + EXPECT_FLOAT_EQ(result, 7.0f / (5 * 3)); +} + +TEST(FuelComputer, LambdaLookup) { + MockVp3d afrTable; + FuelComputer dut(afrTable); + + EXPECT_CALL(afrTable, getValue(1500, FloatEq(0.7f))) + .WillOnce(Return(14.7f)); + + EXPECT_FLOAT_EQ(dut.getTargetLambda(1500, 0.7f), 1.0f); +} diff --git a/unit_tests/tests/test_fuel_math.cpp b/unit_tests/tests/test_fuel_math.cpp index b95084e603..a80d17ad5a 100644 --- a/unit_tests/tests/test_fuel_math.cpp +++ b/unit_tests/tests/test_fuel_math.cpp @@ -78,7 +78,7 @@ TEST(AirmassModes, AlphaNFailedTps) { EXPECT_EQ(result.CylinderAirmass, 0); } -TEST(misc, MafNormal) { +TEST(AirmassModes, MafNormal) { WITH_ENGINE_TEST_HELPER(FORD_ASPIRE_1996); engineConfiguration->fuelAlgorithm = LM_REAL_MAF; engineConfiguration->injector.flow = 200; diff --git a/unit_tests/tests/tests.mk b/unit_tests/tests/tests.mk index bac33b4443..470c410b12 100644 --- a/unit_tests/tests/tests.mk +++ b/unit_tests/tests/tests.mk @@ -12,6 +12,7 @@ TESTS_SRC_CPP = \ tests/ignition_injection/test_multispark.cpp \ tests/ignition_injection/test_ignition_scheduling.cpp \ tests/ignition_injection/test_fuelCut.cpp \ + tests/ignition_injection/test_fuel_computer.cpp \ tests/test_util.cpp \ tests/test_ion.cpp \ tests/test_aux_valves.cpp \