hook up fuel trims (#3715)

* hook up fuel trims

* pass all the params

* store per-cylinder fuel mass directly

* main trigger callback only touches per-cylinder, no banks!

* test test test test

* move UI to happy land

* changelog
This commit is contained in:
Matthew Kennedy 2022-01-01 01:19:59 -06:00 committed by GitHub
parent 049dd5b22a
commit ab3e3ac83f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 74 additions and 31 deletions

View File

@ -27,9 +27,12 @@ All notable user-facing or behavior-altering changes will be documented in this
## Month 202x Release - "Release Name" ## Month 202x Release - "Release Name"
### Added
- Per-cylinder fuel trim tables
## December 2021 Release - "Cookie Exchange" ## December 2021 Release - "Cookie Exchange"
## Added ### Added
- Improved vehicle speed sensor configuration: now uses real physical constants about tires, gear ratio, sensor, etc. - Improved vehicle speed sensor configuration: now uses real physical constants about tires, gear ratio, sensor, etc.
- Improved priming logic. Now includes a table of priming fuel mass vs. engine temperature, in addition to a delay before priming to allow fuel pressure to build. #3674 - Improved priming logic. Now includes a table of priming fuel mass vs. engine temperature, in addition to a delay before priming to allow fuel pressure to build. #3674
- ISO-TP connector in firmware & ISO-TP to TCP/IP bridge in rusEFI console #3667 - ISO-TP connector in firmware & ISO-TP to TCP/IP bridge in rusEFI console #3667

View File

@ -332,14 +332,12 @@ public:
floatms_t injectionDuration = 0; floatms_t injectionDuration = 0;
// Per-injection fuel mass, including TPS accel enrich // Per-injection fuel mass, including TPS accel enrich
float injectionMass[STFT_BANK_COUNT] = {0}; float injectionMass[MAX_CYLINDER_COUNT] = {0};
float stftCorrection[STFT_BANK_COUNT] = {0}; float stftCorrection[STFT_BANK_COUNT] = {0};
/** // Stores the actual pulse duration of the last injection for every cylinder
* This one with wall wetting accounted for, used for logging. floatms_t actualLastInjection[MAX_CYLINDER_COUNT] = {0};
*/
floatms_t actualLastInjection[STFT_BANK_COUNT] = {0};
// Standard cylinder air charge - 100% VE at standard temperature, grams per cylinder // Standard cylinder air charge - 100% VE at standard temperature, grams per cylinder
float standardAirCharge = 0; float standardAirCharge = 0;

View File

@ -151,13 +151,6 @@ void EngineState::periodicFastCallback() {
float injectionMass = getInjectionMass(rpm); float injectionMass = getInjectionMass(rpm);
auto clResult = fuelClosedLoopCorrection(); auto clResult = fuelClosedLoopCorrection();
// compute per-bank fueling
for (size_t i = 0; i < STFT_BANK_COUNT; i++) {
float corr = clResult.banks[i];
engine->injectionMass[i] = injectionMass * corr;
engine->stftCorrection[i] = corr;
}
// Store the pre-wall wetting injection duration for scheduling purposes only, not the actual injection duration // Store the pre-wall wetting injection duration for scheduling purposes only, not the actual injection duration
engine->injectionDuration = engine->module<InjectorModel>()->getInjectionDuration(injectionMass); engine->injectionDuration = engine->module<InjectorModel>()->getInjectionDuration(injectionMass);
@ -167,7 +160,21 @@ void EngineState::periodicFastCallback() {
float ignitionLoad = getIgnitionLoad(); float ignitionLoad = getIgnitionLoad();
float advance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd; float advance = getAdvance(rpm, ignitionLoad) * luaAdjustments.ignitionTimingMult + luaAdjustments.ignitionTimingAdd;
// compute per-bank fueling
for (size_t i = 0; i < STFT_BANK_COUNT; i++) {
float corr = clResult.banks[i];
engine->stftCorrection[i] = corr;
}
// Now apply that to per-cylinder fueling and timing
for (size_t i = 0; i < engineConfiguration->specs.cylindersCount; i++) { for (size_t i = 0; i < engineConfiguration->specs.cylindersCount; i++) {
uint8_t bankIndex = engineConfiguration->cylinderBankSelect[i];
auto bankTrim =engine->stftCorrection[bankIndex];
auto cylinderTrim = getCylinderFuelTrim(i, rpm, fuelLoad);
// Apply both per-bank and per-cylinder trims
engine->injectionMass[i] = injectionMass * bankTrim * cylinderTrim;
timingAdvance[i] = advance; timingAdvance[i] = advance;
} }

View File

@ -431,5 +431,17 @@ float getStandardAirCharge() {
return idealGasLaw(cylDisplacement, 101.325f, 273.15f + 20.0f); return idealGasLaw(cylDisplacement, 101.325f, 273.15f + 20.0f);
} }
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad) {
auto trimPercent = interpolate3d(
config->fuelTrims[cylinderNumber].table,
config->fuelTrimLoadBins, fuelLoad,
config->fuelTrimRpmBins, rpm
);
// Convert from percent +- to multiplier
// 5% -> 1.05
return (100 + trimPercent) / 100;
}
#endif #endif
#endif #endif

View File

@ -30,6 +30,7 @@ float getInjectionMass(int rpm);
percent_t getInjectorDutyCycle(int rpm); percent_t getInjectorDutyCycle(int rpm);
float getStandardAirCharge(); float getStandardAirCharge();
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad);
struct AirmassModelBase; struct AirmassModelBase;
AirmassModelBase* getAirmassModel(engine_load_mode_e mode); AirmassModelBase* getAirmassModel(engine_load_mode_e mode);

View File

@ -165,9 +165,8 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now
return; return;
} }
// Select fuel mass from the correct bank // Select fuel mass from the correct cylinder
uint8_t bankIndex = engineConfiguration->cylinderBankSelect[this->cylinderNumber]; auto injectionMassGrams = engine->injectionMass[this->cylinderNumber];
float injectionMassGrams = engine->injectionMass[bankIndex];
// Perform wall wetting adjustment on fuel mass, not duration, so that // Perform wall wetting adjustment on fuel mass, not duration, so that
// it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients // it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
@ -195,7 +194,7 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now
engine->engineState.fuelConsumption.consumeFuel(injectionMassGrams * numberOfInjections, nowNt); engine->engineState.fuelConsumption.consumeFuel(injectionMassGrams * numberOfInjections, nowNt);
engine->actualLastInjection[bankIndex] = injectionDuration; engine->actualLastInjection[this->cylinderNumber] = injectionDuration;
if (cisnan(injectionDuration)) { if (cisnan(injectionDuration)) {
warning(CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse"); warning(CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");

View File

@ -1286,6 +1286,21 @@ menuDialog = main
subMenu = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1} subMenu = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1}
subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1} subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1}
; subMenu = fuelTrimSettings, "Fuel Trim", 0, {isInjectionEnabled == 1} ; subMenu = fuelTrimSettings, "Fuel Trim", 0, {isInjectionEnabled == 1}
groupMenu = "Cylinder fuel trims"
groupChildMenu = fuelTrimTbl1, "Fuel trim cyl 1"
groupChildMenu = fuelTrimTbl2, "Fuel trim cyl 2"
groupChildMenu = fuelTrimTbl3, "Fuel trim cyl 3"
groupChildMenu = fuelTrimTbl4, "Fuel trim cyl 4"
groupChildMenu = fuelTrimTbl5, "Fuel trim cyl 5"
groupChildMenu = fuelTrimTbl6, "Fuel trim cyl 6"
groupChildMenu = fuelTrimTbl7, "Fuel trim cyl 7"
groupChildMenu = fuelTrimTbl8, "Fuel trim cyl 8"
groupChildMenu = fuelTrimTbl9, "Fuel trim cyl 9"
groupChildMenu = fuelTrimTbl10, "Fuel trim cyl 10"
groupChildMenu = fuelTrimTbl11, "Fuel trim cyl 11"
groupChildMenu = fuelTrimTbl12, "Fuel trim cyl 12"
subMenu = std_separator subMenu = std_separator
# Air mass model # Air mass model
@ -1500,20 +1515,6 @@ menuDialog = main
# subMenu = antiLag, "Antilag Setup" # subMenu = antiLag, "Antilag Setup"
# subMenu = std_separator # subMenu = std_separator
groupMenu = "Cylinder fuel trims"
groupChildMenu = fuelTrimTbl1, "Fuel trim cyl 1"
groupChildMenu = fuelTrimTbl2, "Fuel trim cyl 2"
groupChildMenu = fuelTrimTbl3, "Fuel trim cyl 3"
groupChildMenu = fuelTrimTbl4, "Fuel trim cyl 4"
groupChildMenu = fuelTrimTbl5, "Fuel trim cyl 5"
groupChildMenu = fuelTrimTbl6, "Fuel trim cyl 6"
groupChildMenu = fuelTrimTbl7, "Fuel trim cyl 7"
groupChildMenu = fuelTrimTbl8, "Fuel trim cyl 8"
groupChildMenu = fuelTrimTbl9, "Fuel trim cyl 9"
groupChildMenu = fuelTrimTbl10, "Fuel trim cyl 10"
groupChildMenu = fuelTrimTbl11, "Fuel trim cyl 11"
groupChildMenu = fuelTrimTbl12, "Fuel trim cyl 12"
groupMenu = "Cylinder ign trims" groupMenu = "Cylinder ign trims"
groupChildMenu = ignTrimTbl1, "Ignition trim cyl 1" groupChildMenu = ignTrimTbl1, "Ignition trim cyl 1"
groupChildMenu = ignTrimTbl2, "Ignition trim cyl 2" groupChildMenu = ignTrimTbl2, "Ignition trim cyl 2"

View File

@ -178,3 +178,25 @@ TEST(FuelMath, deadtime) {
engine->periodicFastCallback(); engine->periodicFastCallback();
EXPECT_FLOAT_EQ( 20 + 2, engine->injectionDuration); EXPECT_FLOAT_EQ( 20 + 2, engine->injectionDuration);
} }
TEST(FuelMath, CylinderFuelTrim) {
EngineTestHelper eth(TEST_ENGINE);
EXPECT_CALL(eth.mockAirmass, getAirmass(_))
.WillRepeatedly(Return(AirmassResult{1, 50.0f}));
setTable(config->fuelTrims[0].table, -4);
setTable(config->fuelTrims[1].table, -2);
setTable(config->fuelTrims[2].table, 2);
setTable(config->fuelTrims[3].table, 4);
// run the fuel math
engine->periodicFastCallback();
// Check that each cylinder gets the expected amount of fuel
float unadjusted = 0.072142f;
EXPECT_NEAR(engine->injectionMass[0], unadjusted * 0.96, EPS4D);
EXPECT_NEAR(engine->injectionMass[1], unadjusted * 0.98, EPS4D);
EXPECT_NEAR(engine->injectionMass[2], unadjusted * 1.02, EPS4D);
EXPECT_NEAR(engine->injectionMass[3], unadjusted * 1.04, EPS4D);
}