diff --git a/firmware/controllers/actuators/electronic_throttle.cpp b/firmware/controllers/actuators/electronic_throttle.cpp index f800b62c52..49f9711fa5 100644 --- a/firmware/controllers/actuators/electronic_throttle.cpp +++ b/firmware/controllers/actuators/electronic_throttle.cpp @@ -316,6 +316,14 @@ expected EtbController::getSetpointEtb() const { // 100% target from table -> 100% target position percent_t targetPosition = interpolateClamped(0, etbIdleAddition, 100, 100, targetFromTable); + // Lastly, apply ETB rev limiter + auto etbRpmLimit = CONFIG(etbRevLimitStart); + if (etbRpmLimit != 0) { + auto fullyLimitedRpm = etbRpmLimit + CONFIG(etbRevLimitRange); + // Linearly taper throttle to closed from the limit across the range + targetPosition = interpolateClamped(etbRpmLimit, targetPosition, fullyLimitedRpm, 0, rpm); + } + #if EFI_TUNER_STUDIO if (m_function == ETB_Throttle1) { tsOutputChannels.etbTarget = targetPosition; diff --git a/firmware/integration/rusefi_config.txt b/firmware/integration/rusefi_config.txt index 71213bc3f7..d8cba4033d 100644 --- a/firmware/integration/rusefi_config.txt +++ b/firmware/integration/rusefi_config.txt @@ -569,8 +569,8 @@ int16_t tpsErrorDetectionTooHigh;+TPS error detection: what throttle % is unreal cranking_parameters_s cranking float primingSquirtDurationMs;;"*C", 1, 0, -40, 200, 1 float ignitionDwellForCrankingMs;+Dwell duration while cranking;"ms", 1, 0, 0, 200, 1 -float unused104;;"deg", 1, 0, 0, 3000.0, 0 - + uint16_t etbRevLimitStart;+Once engine speed passes this value, start reducing ETB angle.;1, 0, 0, 15000, 0 + uint16_t etbRevLimitRange;+This far above 'Soft limiter start', fully close the throttle. At the bottom of the range, throttle control is normal. At the top of the range, the throttle is fully closed.;1, 0, 0, 2000, 0 MAP_sensor_config_s map;@see hasMapSensor\n@see isMapAveragingEnabled diff --git a/firmware/tunerstudio/rusefi.input b/firmware/tunerstudio/rusefi.input index 76d6cfcc07..021736579d 100644 --- a/firmware/tunerstudio/rusefi.input +++ b/firmware/tunerstudio/rusefi.input @@ -3046,13 +3046,19 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00" field = "Cut spark on RPM limit", cutSparkOnHardLimit field = "RPM hard limit", rpmHardLimit, { cutFuelOnHardLimit || cutSparkOnHardLimit } field = "Boost cut pressure", boostCutPressure - + + dialog = etbLimits, "Electronic Throttle Limiting" + field = "Smoothly close the throttle to limit RPM." + field = "Soft limiter start", etbRevLimitStart + field = "Soft limiter range", etbRevLimitRange + dialog = fallbacks, "Fallbacks" field = "Use MAP estimation table as fallback", enableMapEstimationTableFallback field = "Failed MAP sensor fallback", failedMapFallback, { !enableMapEstimationTableFallback } dialog = limitsAndFallback, "Limits and fallbacks" panel = limits + panel = etbLimits panel = fallbacks ; Engine->Base Engine Settings diff --git a/unit_tests/tests/test_etb.cpp b/unit_tests/tests/test_etb.cpp index 3e510288f0..2df9548735 100644 --- a/unit_tests/tests/test_etb.cpp +++ b/unit_tests/tests/test_etb.cpp @@ -346,6 +346,49 @@ TEST(etb, setpointIdle) { EXPECT_FLOAT_EQ(55, etb.getSetpoint().value_or(-1)); } +TEST(etb, setpointRevLimit) { + WITH_ENGINE_TEST_HELPER(TEST_ENGINE); + + // Configure 5000 limit start, with 750 rpm taper + engineConfiguration->etbRevLimitStart = 5000; + engineConfiguration->etbRevLimitRange = 750; + + // Must have TPS & PPS initialized for ETB setup + Sensor::setMockValue(SensorType::Tps1, 0.0f, true); + Sensor::setMockValue(SensorType::AcceleratorPedal, 0.0f, true); + + EtbController etb; + INJECT_ENGINE_REFERENCE(&etb); + + // Mock pedal map to just return 80% + StrictMock pedalMap; + EXPECT_CALL(pedalMap, getValue(_, _)) + .WillRepeatedly([](float, float) { + return 80; + }); + etb.init(ETB_Throttle1, nullptr, nullptr, &pedalMap, true); + + // Below threshold, should return unadjusted throttle + ENGINE(rpmCalculator.mockRpm) = 1000; + EXPECT_EQ(80, etb.getSetpoint().value_or(-1)); + + // At threshold, should return unadjusted throttle + ENGINE(rpmCalculator.mockRpm) = 5000; + EXPECT_EQ(80, etb.getSetpoint().value_or(-1)); + + // Middle of range, should return half of unadjusted + ENGINE(rpmCalculator.mockRpm) = 5375; + EXPECT_EQ(40, etb.getSetpoint().value_or(-1)); + + // At limit+range, should return 0 + ENGINE(rpmCalculator.mockRpm) = 5750; + EXPECT_EQ(0, etb.getSetpoint().value_or(-1)); + + // Above limit+range, should return 0 + ENGINE(rpmCalculator.mockRpm) = 6000; + EXPECT_EQ(0, etb.getSetpoint().value_or(-1)); +} + TEST(etb, setpointNoPedalMap) { EtbController etb;