Implement Fuel Adder % settings #6783 (#7125)

* only:refactoring: extract part of functionality into `NitrousTestBase` class #6783

* Implement `Fuel Adder %` settings #6783
This commit is contained in:
kifir23917 2024-12-05 23:58:40 +02:00 committed by GitHub
parent 6b2dd89657
commit ad027a99d6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 279 additions and 99 deletions

View File

@ -66,4 +66,5 @@ namespace engine_configuration_defaults {
constexpr uint16_t NITROUS_ACTIVATION_RPM = 4000;
constexpr uint16_t NITROUS_DEACTIVATION_RPM = 7000;
constexpr uint16_t NITROUS_DEACTIVATION_RPM_WINDOW = 500;
constexpr int8_t NITROUS_FUEL_ADDER_PERCENT = 0;
}

View File

@ -130,6 +130,7 @@ float getRunningFuel(float baseFuel) {
#if EFI_LAUNCH_CONTROL
correction *= engine->launchController.getFuelCoefficient();
correction *= engine->module<NitrousController>().unmock().getFuelCoefficient();
#endif
correction *= getLimpManager()->getLimitingFuelCorrection();

View File

@ -25,6 +25,14 @@ void NitrousController::onSlowCallback() {
enginePins.nitrousRelay.setValue(isNitrousConditionSatisfied);
}
float NitrousController::getFuelCoefficient() const {
float result = 1.0f;
if (engineConfiguration->nitrousControlEnabled && isNitrousConditionSatisfied) {
result += engineConfiguration->nitrousFuelAdderPercent / 100.0f;
}
return result;
}
void NitrousController::updateArmingState() {
switch (engineConfiguration->nitrousControlArmingMethod) {
case DIGITAL_SWITCH_INPUT: {

View File

@ -11,6 +11,8 @@ public:
using interface_t = NitrousController;
void onSlowCallback() override;
float getFuelCoefficient() const;
private:
void updateArmingState();
void updateTpsConditionSatisfied();

View File

@ -1801,7 +1801,9 @@ uint8_t autoscale knockFuelTrim;Fuel trim when knock, max 30%;"%", 1, 0, 0, 30,
output_pin_e nitrousRelayPin;
pin_output_mode_e nitrousRelayPinMode;
#define END_OF_CALIBRATION_PADDING 73
int8_t nitrousFuelAdderPercent;;"%", 1, 0, 0, 100, 0
#define END_OF_CALIBRATION_PADDING 72
uint8_t[END_OF_CALIBRATION_PADDING] unusedOftenChangesDuringFirmwareUpdate;;"units", 1, 0, 0, 1, 0
! end of engine_configuration_s

View File

@ -4999,6 +4999,7 @@ dialog = tcuControls, "Transmission Settings"
field = "Activation RPM", nitrousActivationRpm
field = "Dectivation RPM", nitrousDeactivationRpm
field = "Dectivation RPM Window", nitrousDeactivationRpmWindow
field = "Fuel Adder", nitrousFuelAdderPercent
dialog = NitrousControlSettingsDialog, "", yAxis
field = "Enable Nitrous Control", nitrousControlEnabled

View File

@ -0,0 +1,120 @@
//
// Created by kifir on 12/5/24.
//
#include "pch.h"
#include "nitrous_test_base.h"
void NitrousTestBase::setUpTestConfiguration(const std::optional<int8_t> nitrousFuelAdderPercent) {
setUpEngineConfiguration(EngineConfig()
.setNitrousControlEnabled({ true })
.setNitrousControlArmingMethod({ DIGITAL_SWITCH_INPUT })
.setNitrousControlTriggerPin({ TEST_NITROUS_CONTROL_ARMING_PIN })
.setNitrousMinimumTps({ TEST_MIN_TPS })
.setNitrousMinimumClt({ TEST_MIN_CLT })
.setNitrousMaximumMap({ TEST_MAX_MAP })
.setNitrousMaximumAfr({ TEST_MAXIMUM_AFR })
.setNitrousActivationRpm({ TEST_ACTIVATION_RPM })
.setNitrousDeactivationRpm({ TEST_DEACTIVATION_RPM })
.setNitrousDeactivationRpmWindow({ TEST_DEACTIVATION_RPM_WINDOW })
.setNitrousFuelAdderPercent(nitrousFuelAdderPercent)
);
}
void NitrousTestBase::armNitrousControl() {
setMockState(TEST_NITROUS_CONTROL_ARMING_PIN, true);
periodicSlowCallback();
EXPECT_TRUE(getModule<NitrousController>().isArmed);
}
void NitrousTestBase::satisfyTpsCondition() {
updateApp(TEST_MIN_TPS, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isTpsConditionSatisfied);
}
void NitrousTestBase::satisfyCltCondition() {
updateClt(TEST_MIN_CLT, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isCltConditionSatisfied);
}
void NitrousTestBase::satisfyMapCondition() {
updateMap(TEST_MAX_MAP, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isMapConditionSatisfied);
}
void NitrousTestBase::satisfyAfrCondition() {
updateLambda1(TEST_LAMBDA1, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isAfrConditionSatisfied);
}
void NitrousTestBase::satisfyRpmCondition() {
updateRpm(TEST_ACTIVATION_RPM, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isNitrousRpmConditionSatisfied);
}
void NitrousTestBase::activateNitrousControl() {
armNitrousControl();
satisfyTpsCondition();
satisfyCltCondition();
satisfyMapCondition();
satisfyAfrCondition();
satisfyRpmCondition();
}
void NitrousTestBase::unarmNitrousControl() {
setMockState(TEST_NITROUS_CONTROL_ARMING_PIN, false);
periodicSlowCallback();
EXPECT_FALSE(getModule<NitrousController>().isArmed);
}
void NitrousTestBase::unsatisfyTpsCondition() {
updateApp(TEST_MIN_TPS - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isTpsConditionSatisfied);
}
void NitrousTestBase::unsatisfyCltCondition() {
updateClt(TEST_MIN_CLT - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isCltConditionSatisfied);
}
void NitrousTestBase::unsatisfyMapCondition() {
updateMap(TEST_MAX_MAP + EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isMapConditionSatisfied);
}
void NitrousTestBase::unsatisfyAfrCondition() {
updateLambda1(TEST_LAMBDA1 + EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isAfrConditionSatisfied);
}
void NitrousTestBase::unsatisfyRpmCondition() {
updateRpm(TEST_ACTIVATION_RPM - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isNitrousRpmConditionSatisfied);
}
void NitrousTestBase::deactivateNitrousControl() {
unarmNitrousControl();
unsatisfyTpsCondition();
unsatisfyCltCondition();
unsatisfyMapCondition();
unsatisfyAfrCondition();
unsatisfyRpmCondition();
}
void NitrousTestBase::checkNitrousCondition(const bool expected, const char* const context) {
EXPECT_EQ(getModule<NitrousController>().isNitrousConditionSatisfied, expected) << context;
EXPECT_EQ(enginePins.nitrousRelay.getLogicValue(), expected) << context;
}

View File

@ -0,0 +1,44 @@
//
// Created by kifir on 12/5/24.
//
#pragma once
#include "util/test_base.h"
class NitrousTestBase : public TestBase {
protected:
static constexpr switch_input_pin_e TEST_NITROUS_CONTROL_ARMING_PIN = Gpio::A13;
static constexpr int TEST_MIN_TPS = 34;
static constexpr uint8_t TEST_MIN_CLT = 51;
static constexpr int TEST_MAX_MAP = 45;
static constexpr float TEST_MAXIMUM_AFR = 12.3;
static constexpr float TEST_LAMBDA1 = TEST_MAXIMUM_AFR / STOICH_RATIO;
static constexpr uint16_t TEST_ACTIVATION_RPM = 239;
static constexpr uint16_t TEST_DEACTIVATION_RPM = 932;
static constexpr uint16_t TEST_DEACTIVATION_RPM_WINDOW = 17;
void setUpTestConfiguration(std::optional<int8_t> nitrousFuelAdderPercent = {});
void armNitrousControl();
void satisfyTpsCondition();
void satisfyCltCondition();
void satisfyMapCondition();
void satisfyAfrCondition();
void satisfyRpmCondition();
void activateNitrousControl();
void unarmNitrousControl();
void unsatisfyTpsCondition();
void unsatisfyCltCondition();
void unsatisfyMapCondition();
void unsatisfyAfrCondition();
void unsatisfyRpmCondition();
void deactivateNitrousControl();
void checkNitrousCondition(bool expected, const char* context);
};

View File

@ -4,52 +4,18 @@
#include "pch.h"
#include "util/test_base.h"
#include "nitrous_test_base.h"
namespace {
class NitrousConditionTest : public TestBase {
class NitrousConditionTest : public NitrousTestBase {
protected:
static constexpr switch_input_pin_e TEST_NITROUS_CONTROL_ARMING_PIN = Gpio::A13;
static constexpr int TEST_MIN_TPS = 34;
static constexpr uint8_t TEST_MIN_CLT = 51;
static constexpr int TEST_MAX_MAP = 45;
static constexpr float TEST_MAXIMUM_AFR = 12.3;
static constexpr float TEST_LAMBDA1 = TEST_MAXIMUM_AFR / STOICH_RATIO;
static constexpr uint16_t TEST_ACTIVATION_RPM = 239;
static constexpr uint16_t TEST_DEACTIVATION_RPM = 932;
static constexpr uint16_t TEST_DEACTIVATION_RPM_WINDOW = 17;
void SetUp() override;
void checkNitrousCondition(bool expected, const char* context);
private:
void armNitrousControl();
void satisfyTpsCondition();
void satisfyCltCondition();
void satisfyMapCondition();
void satisfyAfrCondition();
void satisfyRpmCondition();
};
void NitrousConditionTest::SetUp() {
TestBase::SetUp();
setUpEngineConfiguration(EngineConfig()
.setNitrousControlEnabled({ true })
.setNitrousControlArmingMethod({ DIGITAL_SWITCH_INPUT })
.setNitrousControlTriggerPin({ TEST_NITROUS_CONTROL_ARMING_PIN })
.setNitrousMinimumTps({ TEST_MIN_TPS })
.setNitrousMinimumClt({ TEST_MIN_CLT })
.setNitrousMaximumMap({ TEST_MAX_MAP })
.setNitrousMaximumAfr({ TEST_MAXIMUM_AFR })
.setNitrousActivationRpm({ TEST_ACTIVATION_RPM })
.setNitrousDeactivationRpm({ TEST_DEACTIVATION_RPM })
.setNitrousDeactivationRpmWindow({ TEST_DEACTIVATION_RPM_WINDOW })
);
setUpTestConfiguration();
EXPECT_FALSE(getModule<NitrousController>().isArmed);
EXPECT_FALSE(getModule<NitrousController>().isTpsConditionSatisfied);
@ -79,88 +45,33 @@ namespace {
checkNitrousCondition(true, "Armed + TPS + CLT + MAP + AFR + RPM conditions are satisfied");
}
void NitrousConditionTest::checkNitrousCondition(const bool expected, const char* const context) {
EXPECT_EQ(getModule<NitrousController>().isNitrousConditionSatisfied, expected) << context;
EXPECT_EQ(enginePins.nitrousRelay.getLogicValue(), expected) << context;
}
void NitrousConditionTest::armNitrousControl() {
setMockState(TEST_NITROUS_CONTROL_ARMING_PIN, true);
periodicSlowCallback();
EXPECT_TRUE(getModule<NitrousController>().isArmed);
}
void NitrousConditionTest::satisfyTpsCondition() {
updateApp(TEST_MIN_TPS, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isTpsConditionSatisfied);
}
void NitrousConditionTest::satisfyCltCondition() {
updateClt(TEST_MIN_CLT, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isCltConditionSatisfied);
}
void NitrousConditionTest::satisfyMapCondition() {
updateMap(TEST_MAX_MAP, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isMapConditionSatisfied);
}
void NitrousConditionTest::satisfyAfrCondition() {
updateLambda1(TEST_LAMBDA1, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isAfrConditionSatisfied);
}
void NitrousConditionTest::satisfyRpmCondition() {
updateRpm(TEST_ACTIVATION_RPM, &TestBase::periodicSlowCallback);
EXPECT_TRUE(getModule<NitrousController>().isNitrousRpmConditionSatisfied);
}
TEST_F(NitrousConditionTest, checkWithoutArmedNitrousControl) {
setMockState(TEST_NITROUS_CONTROL_ARMING_PIN, false);
periodicSlowCallback();
EXPECT_FALSE(getModule<NitrousController>().isArmed);
unarmNitrousControl();
checkNitrousCondition(false, "Without armed condition");
}
TEST_F(NitrousConditionTest, checkWithoutSatisfiedTpsCondition) {
updateApp(TEST_MIN_TPS - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isTpsConditionSatisfied);
unsatisfyTpsCondition();
checkNitrousCondition(false, "Without TPS condition");
}
TEST_F(NitrousConditionTest, checkWithoutSatisfiedCltCondition) {
updateClt(TEST_MIN_CLT - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isCltConditionSatisfied);
unsatisfyCltCondition();
checkNitrousCondition(false, "Without CLT condition");
}
TEST_F(NitrousConditionTest, checkWithoutSatisfiedMapCondition) {
updateMap(TEST_MAX_MAP + EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isMapConditionSatisfied);
unsatisfyMapCondition();
checkNitrousCondition(false, "Without MAP condition");
}
TEST_F(NitrousConditionTest, checkWithoutSatisfiedAfrCondition) {
updateLambda1(TEST_LAMBDA1 + EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isAfrConditionSatisfied);
unsatisfyAfrCondition();
checkNitrousCondition(false, "Without AFR condition");
}
TEST_F(NitrousConditionTest, checkWithoutSatisfiedRpmCondition) {
updateRpm(TEST_ACTIVATION_RPM - EPS5D, &TestBase::periodicSlowCallback);
EXPECT_FALSE(getModule<NitrousController>().isNitrousRpmConditionSatisfied);
unsatisfyRpmCondition();
checkNitrousCondition(false, "Without RPM condition");
}
}

View File

@ -0,0 +1,59 @@
//
// Created by kifir on 12/5/24.
//
#include "pch.h"
#include "nitrous_test_base.h"
namespace {
class NitrousFuelAdderTest : public NitrousTestBase {
protected:
static constexpr int8_t TEST_NITROUS_FUEL_ADDER_PERCENT = 17;
static constexpr float DEFAULT_FUEL_CORRECTION = 1.009999f;
static const CltFuelCorrCurve TEST_CLT_FUEL_CORR;
void checkTotalFuelCorrection(
bool expectedNitrousCondition,
float expectedTotalFuelCorrection,
const char* context
);
public:
static CltFuelCorrCurve generateTestCltFuelCorrCurve();
};
void NitrousFuelAdderTest::checkTotalFuelCorrection(
const bool expectedNitrousCondition,
const float expectedTotalFuelCorrection,
const char* const context
) {
periodicFastCallback(); // to recalculate total fuel correction
checkNitrousCondition(expectedNitrousCondition, context);
EXPECT_NEAR(engine->fuelComputer.totalFuelCorrection, expectedTotalFuelCorrection, EPS5D) << context;
}
CltFuelCorrCurve NitrousFuelAdderTest::generateTestCltFuelCorrCurve() {
CltFuelCorrCurve result;
result.fill(DEFAULT_FUEL_CORRECTION);
return result;
}
const CltFuelCorrCurve NitrousFuelAdderTest::TEST_CLT_FUEL_CORR = NitrousFuelAdderTest::generateTestCltFuelCorrCurve();
TEST_F(NitrousFuelAdderTest, checkFuelCorrection) {
setUpTestConfiguration({ TEST_NITROUS_FUEL_ADDER_PERCENT });
getTestPersistentConfiguration().setCltFuelCorrCurve(TEST_CLT_FUEL_CORR); // constant fuel CLT correction
checkTotalFuelCorrection(false, DEFAULT_FUEL_CORRECTION, "Default");
activateNitrousControl();
checkTotalFuelCorrection(
true,
DEFAULT_FUEL_CORRECTION * (1.0f + TEST_NITROUS_FUEL_ADDER_PERCENT / 100.0f),
"All conditions are satisfied"
);
deactivateNitrousControl();
checkTotalFuelCorrection(false, DEFAULT_FUEL_CORRECTION, "No conditions are satisfied");
}
}

View File

@ -71,6 +71,7 @@ TESTS_SRC_CPP = \
tests/shift_torque_reduction/test_shift_torque_reduction_flat_shift_condition.cpp \
tests/shift_torque_reduction/test_shift_torque_reduction_spark_skip_ratio.cpp \
tests/shift_torque_reduction/test_shift_torque_reduction_angle_advance.cpp \
tests/nitrous_control/nitrous_test_base.cpp \
tests/nitrous_control/test_nitrous_arming.cpp \
tests/nitrous_control/test_nitrous_tps_condition.cpp \
tests/nitrous_control/test_nitrous_clt_condition.cpp \
@ -78,6 +79,7 @@ TESTS_SRC_CPP = \
tests/nitrous_control/test_nitrous_afr_condition.cpp \
tests/nitrous_control/test_nitrous_rpm_condition.cpp \
tests/nitrous_control/test_nitrous_condition.cpp \
tests/nitrous_control/test_nitrous_fuel_adder.cpp \
tests/test_fft.cpp \
tests/lua/test_lua_basic.cpp \
tests/lua/test_bit_range_msb.cpp \

View File

@ -253,3 +253,8 @@ EngineConfig EngineConfig::setNitrousDeactivationRpmWindow(const std::optional<u
m_nitrousDeactivationRpmWindow = value;
return *this;
}
EngineConfig EngineConfig::setNitrousFuelAdderPercent(const std::optional<int8_t> value) {
m_nitrousFuelAdderPercent = value;
return *this;
}

View File

@ -76,6 +76,7 @@ public:
std::optional<uint16_t> getNitrousActivationRpm() const { return m_nitrousActivationRpm; }
std::optional<uint16_t> getNitrousDeactivationRpm() const { return m_nitrousDeactivationRpm; }
std::optional<uint16_t> getNitrousDeactivationRpmWindow() const { return m_nitrousDeactivationRpmWindow; }
std::optional<int8_t> getNitrousFuelAdderPercent() const { return m_nitrousFuelAdderPercent; }
// We do not core about performance in tests, but we want to use builder-like style, so setters return new instance
// of configuration:
@ -141,6 +142,7 @@ public:
EngineConfig setNitrousActivationRpm(std::optional<uint16_t> value);
EngineConfig setNitrousDeactivationRpm(std::optional<uint16_t> value);
EngineConfig setNitrousDeactivationRpmWindow(std::optional<uint16_t> value);
EngineConfig setNitrousFuelAdderPercent(std::optional<int8_t> value);
private:
// Launch Control
std::optional<switch_input_pin_e> m_launchActivatePin;
@ -203,4 +205,5 @@ private:
std::optional<uint16_t> m_nitrousActivationRpm;
std::optional<uint16_t> m_nitrousDeactivationRpm;
std::optional<uint16_t> m_nitrousDeactivationRpmWindow;
std::optional<int8_t> m_nitrousFuelAdderPercent;
};

View File

@ -96,6 +96,9 @@ void TestBase::setUpEngineConfiguration(const EngineConfig& config) {
getTestEngineConfiguration().configureNitrousDeactivationRpmWindow(
config.getNitrousDeactivationRpmWindow()
);
getTestEngineConfiguration().configureNitrousFuelAdderPercent(
config.getNitrousFuelAdderPercent()
);
}
void TestBase::periodicFastCallback() {

View File

@ -556,6 +556,17 @@ void TestEngineConfiguration::configureNitrousDeactivationRpmWindow(
}
}
void TestEngineConfiguration::configureNitrousFuelAdderPercent(const std::optional<int8_t> nitrousFuelAdderPercent) {
if (nitrousFuelAdderPercent.has_value()) {
engineConfiguration->nitrousFuelAdderPercent = nitrousFuelAdderPercent.value();
} else {
ASSERT_EQ(
engineConfiguration->nitrousFuelAdderPercent,
engine_configuration_defaults::NITROUS_FUEL_ADDER_PERCENT
); // check default value
}
}
TestEngineConfiguration::TestEngineConfiguration() {
}

View File

@ -79,6 +79,7 @@ public:
void configureNitrousActivationRpm(std::optional<uint16_t> nitrousActivationRpm);
void configureNitrousDeactivationRpm(std::optional<uint16_t> nitrousDeactivationRpm);
void configureNitrousDeactivationRpmWindow(std::optional<uint16_t> nitrousDeactivationRpmWindow);
void configureNitrousFuelAdderPercent(std::optional<int8_t> nitrousFuelAdderPercent);
private:
TestEngineConfiguration();
static TestEngineConfiguration instance;

View File

@ -26,4 +26,8 @@ void TestPersistentConfiguration::setInjectorStagingTable(const InjectorStagingT
}
}
void TestPersistentConfiguration::setCltFuelCorrCurve(const CltFuelCorrCurve& cltFuelCorr) {
std::copy(std::begin(cltFuelCorr), std::end(cltFuelCorr), std::begin(config->cltFuelCorr));
}
TestPersistentConfiguration TestPersistentConfiguration::instance;

View File

@ -6,6 +6,7 @@
using IgnitionTable = std::array<std::array<float, IGN_LOAD_COUNT>, IGN_RPM_COUNT>;
using InjectorStagingTable = std::array<std::array<uint8_t, INJ_STAGING_COUNT>, INJ_STAGING_COUNT>;
using CltFuelCorrCurve = std::array<float, CLT_CURVE_SIZE>;
class TestPersistentConfiguration {
public:
@ -13,6 +14,7 @@ public:
void setIgnitionTable(const IgnitionTable& ignitions);
void setInjectorStagingTable(const InjectorStagingTable& ingectorStaging);
void setCltFuelCorrCurve(const CltFuelCorrCurve& cltFuelCorr);
private:
static TestPersistentConfiguration instance;
};