diff --git a/firmware/controllers/algo/engine_configuration_defaults.h b/firmware/controllers/algo/engine_configuration_defaults.h index be01589170..b35a96f2bd 100644 --- a/firmware/controllers/algo/engine_configuration_defaults.h +++ b/firmware/controllers/algo/engine_configuration_defaults.h @@ -4,6 +4,8 @@ #pragma once +#include "batt_lag_corr_curve.h" + namespace engine_configuration_defaults { /* A/C Settings: */ constexpr float AC_DELAY = 0.5f; @@ -28,4 +30,16 @@ namespace engine_configuration_defaults { constexpr bool LAUNCH_ACTIVATE_PIN_INVERTED = false; constexpr launchActivationMode_e LAUNCH_ACTIVATION_MODE = SWITCH_INPUT_LAUNCH; constexpr int LAUNCH_SPEED_THRESHOLD = 0; + + /* Injector */ + constexpr bool INJECTOR_FLOW_AS_MASS_FLOW = false; + constexpr float INJECTOR_FLOW = 200.0f; + constexpr BattLagCorrCurve INJECTOR_BATT_LAG_CURR { 3.371f, 1.974f, 1.383f, 1.194f, 1.04f, 0.914f, 0.797f, 0.726 }; + + /* Secondary injector: */ + constexpr float INJECTOR_SECONDARY_FLOW = INJECTOR_FLOW; + constexpr BattLagCorrCurve INJECTOR_SECONDARY_BATT_LAG_CURR = INJECTOR_BATT_LAG_CURR; + + /* Staged injection: */ + constexpr bool ENABLE_STAGED_INJECTION = false; } diff --git a/firmware/controllers/algo/fuel/batt_lag_corr_curve.h b/firmware/controllers/algo/fuel/batt_lag_corr_curve.h new file mode 100644 index 0000000000..9f7effa8e3 --- /dev/null +++ b/firmware/controllers/algo/fuel/batt_lag_corr_curve.h @@ -0,0 +1,9 @@ +// +// Created by kifir on 11/19/24. +// + +#pragma once + +#include + +using BattLagCorrCurve = std::array; \ No newline at end of file diff --git a/unit_tests/tests/ignition_injection/test_staged_injection.cpp b/unit_tests/tests/ignition_injection/test_staged_injection.cpp new file mode 100644 index 0000000000..e53e0a9789 --- /dev/null +++ b/unit_tests/tests/ignition_injection/test_staged_injection.cpp @@ -0,0 +1,92 @@ +// +// Created by kifir on 11/18/24. +// + +#include "pch.h" + +#include "util/test_base.h" + +namespace { + constexpr uint8_t TEST_STAGE2_FRACTION = 17; + constexpr float TEST_INJECTION_MASS = 12.34f; + + constexpr float TEST_PRIMARY_INJECTOR_DEAD_TIME = 5.6f; + constexpr float TEST_PRIMARY_INJECTOR_FLOW = 239.17f; + constexpr float TEST_PRIMARY_INJECTOR_BASE_DURATION = TEST_INJECTION_MASS / TEST_PRIMARY_INJECTOR_FLOW * 1000; + + constexpr float TEST_SECONDARY_INJECTOR_DEAD_TIME = 7.8; + constexpr float TEST_SECONDARY_INJECTOR_FLOW = 174.17f; + constexpr float TEST_SECONDARY_INJECTOR_BASE_DURATION = TEST_INJECTION_MASS / TEST_SECONDARY_INJECTOR_FLOW * 1000; + + BattLagCorrCurve generateConstantBattLagCorrCurve(const float value) { + BattLagCorrCurve testBattLagCorrCurve; + testBattLagCorrCurve.fill(value); + return testBattLagCorrCurve; + } + + const BattLagCorrCurve TEST_PRIMARY_INJECTOR_BATT_LAG_CORR_CURVE = generateConstantBattLagCorrCurve( + TEST_PRIMARY_INJECTOR_DEAD_TIME + ); + const BattLagCorrCurve TEST_SECONDARY_INJECTOR_BATT_LAG_CORR_CURVE = generateConstantBattLagCorrCurve( + TEST_SECONDARY_INJECTOR_DEAD_TIME + ); + + const EngineConfig TEST_ENGINE_CONFIG = EngineConfig() + .setInjectorFlowAsMassFlow(true) + .setInjectorFlow(TEST_PRIMARY_INJECTOR_FLOW) + .setInjectorBattLagCorr(TEST_PRIMARY_INJECTOR_BATT_LAG_CORR_CURVE) + .setInjectorSecondaryFlow(TEST_SECONDARY_INJECTOR_FLOW) + .setInjectorSecondaryBattLagCorr(TEST_SECONDARY_INJECTOR_BATT_LAG_CORR_CURVE); + + class StagedInjectionTest : public TestBase { + protected: + void SetUp() override; + }; + + void StagedInjectionTest::SetUp() { + TestBase::SetUp(); + + InjectorStagingTable testInjectorStagingTable; + for (int i = 0; i < INJ_STAGING_COUNT; i++) { + for (int j = 0; j < INJ_STAGING_COUNT; j++) { + testInjectorStagingTable[i][j] = TEST_STAGE2_FRACTION; + } + }; + getTestPersistentConfiguration().setInjectorStagingTable(testInjectorStagingTable); + + getTestEngineState().setLuaFuelAdd(TEST_INJECTION_MASS); + } + + TEST_F(StagedInjectionTest, checkDisabledStagedInjection) { + setUpEngineConfiguration(TEST_ENGINE_CONFIG); + + periodicFastCallback(); + + EXPECT_EQ(getTestEngineState().getInjectionStage2Fraction(), 0.0f); + EXPECT_NEAR( + getTestEngineState().getInjectionDuration(), + TEST_PRIMARY_INJECTOR_BASE_DURATION + TEST_PRIMARY_INJECTOR_DEAD_TIME, + EPS5D + ); + EXPECT_EQ(getTestEngineState().getInjectionDurationStage2(), 0.0f); + } + + TEST_F(StagedInjectionTest, checkEnabledStagedInjection) { + setUpEngineConfiguration(TEST_ENGINE_CONFIG.clone().setStagedInjectionEnabled(true)); + + periodicFastCallback(); + + const float injectionStage2Fraction = getTestEngineState().getInjectionStage2Fraction(); + EXPECT_EQ(injectionStage2Fraction, TEST_STAGE2_FRACTION * 0.01f); + EXPECT_NEAR( + getTestEngineState().getInjectionDuration(), + TEST_PRIMARY_INJECTOR_BASE_DURATION * (1 - injectionStage2Fraction) + TEST_PRIMARY_INJECTOR_DEAD_TIME, + EPS5D + ); + EXPECT_NEAR( + getTestEngineState().getInjectionDurationStage2(), + TEST_SECONDARY_INJECTOR_BASE_DURATION * injectionStage2Fraction + TEST_SECONDARY_INJECTOR_DEAD_TIME, + EPS5D + ); + } +} \ No newline at end of file diff --git a/unit_tests/tests/tests.mk b/unit_tests/tests/tests.mk index 596feb7a82..80f7b17f71 100644 --- a/unit_tests/tests/tests.mk +++ b/unit_tests/tests/tests.mk @@ -48,8 +48,10 @@ TESTS_SRC_CPP = \ tests/ignition_injection/test_injector_model.cpp \ tests/ignition_injection/test_odd_firing_engine.cpp \ tests/ignition_injection/test_three_cylinder.cpp \ + testa/ignition_injection/test_staged_injection.cpp \ tests/util/test_base.cpp \ tests/util/test_engine_configuration.cpp \ + tests/util/engine_config.cpp \ tests/util/test_persistent_configuration.cpp \ tests/util/test_engine_state.cpp \ tests/ac/ac_test_base.cpp \ diff --git a/unit_tests/tests/util/engine_config.cpp b/unit_tests/tests/util/engine_config.cpp new file mode 100644 index 0000000000..108454a14d --- /dev/null +++ b/unit_tests/tests/util/engine_config.cpp @@ -0,0 +1,41 @@ +// +// Created by kifir on 11/18/24. +// + +#include "pch.h" + +#include "engine_config.h" + +EngineConfig EngineConfig::clone() const { + return *this; +} + +EngineConfig EngineConfig::setInjectorFlowAsMassFlow(const std::optional injectorFlowAsMassFlow) { + m_injectorFlowAsMassFlow = injectorFlowAsMassFlow; + return *this; +} + +EngineConfig EngineConfig::setInjectorFlow(const std::optional flow) { + m_injectorFlow = flow; + return *this; +} + +EngineConfig EngineConfig::setInjectorBattLagCorr(const std::optional battLagCorr) { + m_injectorBattLagCorrCurve = battLagCorr; + return *this; +} + +EngineConfig EngineConfig::setInjectorSecondaryFlow(const std::optional flow) { + m_injectorSecondaryFlow = flow; + return *this; +} + +EngineConfig EngineConfig::setInjectorSecondaryBattLagCorr(const std::optional battLagCorr) { + m_injectorSecondaryBattLagCorrCurve = battLagCorr; + return *this; +} + +EngineConfig EngineConfig::setStagedInjectionEnabled(const std::optional value) { + m_isStagedInjectionEnabled = value; + return *this; +} \ No newline at end of file diff --git a/unit_tests/tests/util/engine_config.h b/unit_tests/tests/util/engine_config.h new file mode 100644 index 0000000000..590ae63e4d --- /dev/null +++ b/unit_tests/tests/util/engine_config.h @@ -0,0 +1,51 @@ +// +// Created by kifir on 11/18/24. +// + +#pragma once + +#include "batt_lag_corr_curve.h" + +class EngineConfig { +public: + EngineConfig clone() const; + + // Injector + std::optional getInjectorFlow() const { return m_injectorFlow; } + std::optional getInjectorBattLagCorr() const { return m_injectorBattLagCorrCurve; } + std::optional getInjectorFlowAsMassFlow() const { return m_injectorFlowAsMassFlow; } + + // Secondary injector + std::optional getInjectorSecondaryFlow() const { return m_injectorSecondaryFlow; } + std::optional getInjectorSecondaryBattLagCorr() const { return m_injectorSecondaryBattLagCorrCurve; } + + // Staged injection + std::optional getStagedInjectionEnabled() const { return m_isStagedInjectionEnabled; } + + // We do not core about performance in tests, but we want to use builder-like style, so setters return new instance + // of configuration: + + // Injector + EngineConfig setInjectorFlowAsMassFlow(std::optional injectorFlowAsMassFlow); + EngineConfig setInjectorFlow(std::optional flow); + EngineConfig setInjectorBattLagCorr(std::optional battLagCorr); + + // Secondary injector + EngineConfig setInjectorSecondaryFlow(std::optional flow); + EngineConfig setInjectorSecondaryBattLagCorr(std::optional battLagCorr); + + // Staged injection + EngineConfig setStagedInjectionEnabled(std::optional value); +private: + // Injector + std::optional m_injectorFlow; + std::optional m_injectorBattLagCorrCurve; + std::optional m_injectorFlowAsMassFlow;; + + // Secondary injector + std::optional m_injectorSecondaryFlow; + std::optional m_injectorSecondaryBattLagCorrCurve; + + // Staged injection + std::optional m_isStagedInjectionEnabled; +}; \ No newline at end of file diff --git a/unit_tests/tests/util/test_base.cpp b/unit_tests/tests/util/test_base.cpp index 73bb49c125..c2999481d3 100644 --- a/unit_tests/tests/util/test_base.cpp +++ b/unit_tests/tests/util/test_base.cpp @@ -26,6 +26,20 @@ TestEngineState& TestBase::getTestEngineState() { return TestEngineState::getInstance(); } +void TestBase::setUpEngineConfiguration(const EngineConfig& config) { + // Injector + getTestEngineConfiguration().configureInjectorFlowAsMassFlow(config.getInjectorFlowAsMassFlow()); + getTestEngineConfiguration().configureInjectorFlow(config.getInjectorFlow()); + getTestEngineConfiguration().configureInjectorBattLagCorr(config.getInjectorBattLagCorr()); + + // Secondary injector + getTestEngineConfiguration().configureInjectorSecondaryFlow(config.getInjectorSecondaryFlow()); + getTestEngineConfiguration().configureInjectorSecondaryBattLagCorr(config.getInjectorSecondaryBattLagCorr()); + + // Staged injection + getTestEngineConfiguration().configureEnableStagedInjection(config.getStagedInjectionEnabled()); +} + void TestBase::periodicFastCallback() { // run the ignition math engine->periodicFastCallback(); diff --git a/unit_tests/tests/util/test_base.h b/unit_tests/tests/util/test_base.h index 600238d818..9b488af7c3 100644 --- a/unit_tests/tests/util/test_base.h +++ b/unit_tests/tests/util/test_base.h @@ -7,6 +7,7 @@ #include "test_engine_configuration.h" #include "test_engine_state.h" #include "test_persistent_configuration.h" +#include "engine_config.h" class TestBase : public testing::Test { protected: @@ -17,6 +18,8 @@ protected: TestEngineState& getTestEngineState(); TestPersistentConfiguration& getTestPersistentConfiguration(); + void setUpEngineConfiguration(const EngineConfig& config); + void periodicFastCallback(); void periodicSlowCallback(); diff --git a/unit_tests/tests/util/test_engine_configuration.cpp b/unit_tests/tests/util/test_engine_configuration.cpp index fd2a97d804..c2ac314df0 100644 --- a/unit_tests/tests/util/test_engine_configuration.cpp +++ b/unit_tests/tests/util/test_engine_configuration.cpp @@ -252,6 +252,77 @@ void TestEngineConfiguration::configureTorqueReductionIgnitionRetard(const std:: } } +void TestEngineConfiguration::configureInjectorFlowAsMassFlow(const std::optional injectorFlowAsMassFlow) { + if (injectorFlowAsMassFlow.has_value()) { + engineConfiguration->injectorFlowAsMassFlow = injectorFlowAsMassFlow.value(); + } else { + ASSERT_EQ( + engineConfiguration->injectorFlowAsMassFlow, + engine_configuration_defaults::INJECTOR_FLOW_AS_MASS_FLOW + ); // check default value + } +} + +void TestEngineConfiguration::configureInjectorFlow(const std::optional flow) { + if (flow.has_value()) { + engineConfiguration->injector.flow = flow.value(); + } else { + ASSERT_EQ(engineConfiguration->injector.flow, engine_configuration_defaults::INJECTOR_FLOW); // check default value + } +} + +void TestEngineConfiguration::configureInjectorBattLagCorr(const std::optional battLagCorr) { + if (battLagCorr.has_value()) { + std::copy( + std::begin(battLagCorr.value()), + std::end(battLagCorr.value()), + std::begin(engineConfiguration->injector.battLagCorr) + ); + } else { + EXPECT_THAT( + engineConfiguration->injector.battLagCorr, + testing::ElementsAreArray(engine_configuration_defaults::INJECTOR_BATT_LAG_CURR) + ); + } +} + +void TestEngineConfiguration::configureInjectorSecondaryFlow(const std::optional flow) { + if (flow.has_value()) { + engineConfiguration->injectorSecondary.flow = flow.value(); + } else { + ASSERT_EQ( + engineConfiguration->injectorSecondary.flow, + engine_configuration_defaults::INJECTOR_SECONDARY_FLOW + ); // check default value + } +} + +void TestEngineConfiguration::configureInjectorSecondaryBattLagCorr(const std::optional battLagCorr) { + if (battLagCorr.has_value()) { + std::copy( + std::begin(battLagCorr.value()), + std::end(battLagCorr.value()), + std::begin(engineConfiguration->injectorSecondary.battLagCorr) + ); + } else { + EXPECT_THAT( + engineConfiguration->injectorSecondary.battLagCorr, + testing::ElementsAreArray(engine_configuration_defaults::INJECTOR_SECONDARY_BATT_LAG_CURR) + ); + } +} + +void TestEngineConfiguration::configureEnableStagedInjection(const std::optional isStagedInjectionEnabled) { + if (isStagedInjectionEnabled.has_value()) { + engineConfiguration->enableStagedInjection = isStagedInjectionEnabled.value(); + } else { + ASSERT_EQ( + engineConfiguration->enableStagedInjection, + engine_configuration_defaults::ENABLE_STAGED_INJECTION + ); // check default value + } +} + TestEngineConfiguration::TestEngineConfiguration() { } diff --git a/unit_tests/tests/util/test_engine_configuration.h b/unit_tests/tests/util/test_engine_configuration.h index cefa101e3e..c52787043f 100644 --- a/unit_tests/tests/util/test_engine_configuration.h +++ b/unit_tests/tests/util/test_engine_configuration.h @@ -4,7 +4,7 @@ #pragma once -#include "pch.h" +#include "batt_lag_corr_curve.h" class TestEngineConfiguration { public: @@ -41,6 +41,18 @@ public: void configureTorqueReductionArmingApp(std::optional armingApp); void configureTorqueReductionIgnitionCut(std::optional ignitionCut); void configureTorqueReductionIgnitionRetard(std::optional ignitionRetard); + + // Injector + void configureInjectorFlow(std::optional flow); + void configureInjectorBattLagCorr(std::optional battLagCorr); + void configureInjectorFlowAsMassFlow(std::optional injectorFlowAsMassFlow); + + // Secondary Injector + void configureInjectorSecondaryFlow(std::optional flow); + void configureInjectorSecondaryBattLagCorr(std::optional battLagCorr); + + // Staged injection + void configureEnableStagedInjection(std::optional isStagedInjectionEnabled); private: TestEngineConfiguration(); static TestEngineConfiguration instance; diff --git a/unit_tests/tests/util/test_engine_state.cpp b/unit_tests/tests/util/test_engine_state.cpp index 04dd261755..4ac8bdba37 100644 --- a/unit_tests/tests/util/test_engine_state.cpp +++ b/unit_tests/tests/util/test_engine_state.cpp @@ -10,6 +10,18 @@ TestEngineState& TestEngineState::getInstance() { return instance; } +float TestEngineState::getInjectionStage2Fraction() const { + return engine->engineState.injectionStage2Fraction; +} + +floatms_t TestEngineState::getInjectionDuration() const { + return engine->engineState.injectionDuration; +} + +floatms_t TestEngineState::getInjectionDurationStage2() const { + return engine->engineState.injectionDurationStage2; +} + void TestEngineState::setLuaSoftSparkSkip(const float value) { engine->engineState.luaSoftSparkSkip = value; } @@ -18,6 +30,10 @@ void TestEngineState::setLuaHardSparkSkip(const float value) { engine->engineState.luaHardSparkSkip = value; } +void TestEngineState::setLuaFuelAdd(const float value) { + engine->engineState.lua.fuelAdd = value; +} + TestEngineState::TestEngineState() { } diff --git a/unit_tests/tests/util/test_engine_state.h b/unit_tests/tests/util/test_engine_state.h index 8e7da2c47f..aee6d6a6e3 100644 --- a/unit_tests/tests/util/test_engine_state.h +++ b/unit_tests/tests/util/test_engine_state.h @@ -8,8 +8,13 @@ class TestEngineState { public: static TestEngineState& getInstance(); + float getInjectionStage2Fraction() const; + floatms_t getInjectionDuration() const; + floatms_t getInjectionDurationStage2() const; + void setLuaSoftSparkSkip(float value); void setLuaHardSparkSkip(float value); + void setLuaFuelAdd(float value); private: TestEngineState(); static TestEngineState instance; diff --git a/unit_tests/tests/util/test_persistent_configuration.cpp b/unit_tests/tests/util/test_persistent_configuration.cpp index 2d95e456cc..b823112a27 100644 --- a/unit_tests/tests/util/test_persistent_configuration.cpp +++ b/unit_tests/tests/util/test_persistent_configuration.cpp @@ -18,4 +18,12 @@ void TestPersistentConfiguration::setIgnitionTable(const IgnitionTable& ignition } } +void TestPersistentConfiguration::setInjectorStagingTable(const InjectorStagingTable& injectorStaging) { + for (int i = 0; i < INJ_STAGING_COUNT; i++) { + for (int j = 0; j < INJ_STAGING_COUNT; j++) { + config->injectorStagingTable[i][j] = injectorStaging[i][j]; + } + } +} + TestPersistentConfiguration TestPersistentConfiguration::instance; \ No newline at end of file diff --git a/unit_tests/tests/util/test_persistent_configuration.h b/unit_tests/tests/util/test_persistent_configuration.h index 5024f0bd2f..b3a2fe1cf5 100644 --- a/unit_tests/tests/util/test_persistent_configuration.h +++ b/unit_tests/tests/util/test_persistent_configuration.h @@ -5,12 +5,14 @@ #pragma once using IgnitionTable = std::array, IGN_RPM_COUNT>; +using InjectorStagingTable = std::array, INJ_STAGING_COUNT>; class TestPersistentConfiguration { public: static TestPersistentConfiguration& getInstance(); void setIgnitionTable(const IgnitionTable& ignitions); + void setInjectorStagingTable(const InjectorStagingTable& ingectorStaging); private: static TestPersistentConfiguration instance; };