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"
### Added
- Per-cylinder fuel trim tables
## December 2021 Release - "Cookie Exchange"
## Added
### Added
- 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
- 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;
// 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};
/**
* This one with wall wetting accounted for, used for logging.
*/
floatms_t actualLastInjection[STFT_BANK_COUNT] = {0};
// Stores the actual pulse duration of the last injection for every cylinder
floatms_t actualLastInjection[MAX_CYLINDER_COUNT] = {0};
// Standard cylinder air charge - 100% VE at standard temperature, grams per cylinder
float standardAirCharge = 0;

View File

@ -151,13 +151,6 @@ void EngineState::periodicFastCallback() {
float injectionMass = getInjectionMass(rpm);
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
engine->injectionDuration = engine->module<InjectorModel>()->getInjectionDuration(injectionMass);
@ -167,7 +160,21 @@ void EngineState::periodicFastCallback() {
float ignitionLoad = getIgnitionLoad();
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++) {
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;
}

View File

@ -431,5 +431,17 @@ float getStandardAirCharge() {
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

View File

@ -30,6 +30,7 @@ float getInjectionMass(int rpm);
percent_t getInjectorDutyCycle(int rpm);
float getStandardAirCharge();
float getCylinderFuelTrim(size_t cylinderNumber, int rpm, float fuelLoad);
struct AirmassModelBase;
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;
}
// Select fuel mass from the correct bank
uint8_t bankIndex = engineConfiguration->cylinderBankSelect[this->cylinderNumber];
float injectionMassGrams = engine->injectionMass[bankIndex];
// Select fuel mass from the correct cylinder
auto injectionMassGrams = engine->injectionMass[this->cylinderNumber];
// Perform wall wetting adjustment on fuel mass, not duration, so that
// 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->actualLastInjection[bankIndex] = injectionDuration;
engine->actualLastInjection[this->cylinderNumber] = injectionDuration;
if (cisnan(injectionDuration)) {
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 = injectorNonlinear, "Injector small-pulse correction", 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
# Air mass model
@ -1500,20 +1515,6 @@ menuDialog = main
# subMenu = antiLag, "Antilag Setup"
# 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"
groupChildMenu = ignTrimTbl1, "Ignition trim cyl 1"
groupChildMenu = ignTrimTbl2, "Ignition trim cyl 2"

View File

@ -178,3 +178,25 @@ TEST(FuelMath, deadtime) {
engine->periodicFastCallback();
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);
}