injector nonlinearity (#2916)

* nonlinear polynomial

* make existing tests work again

* test that nonlinearity is called

* s

* fix enum

* ui

* fix and test

* comment

* changeloggy
This commit is contained in:
Matthew Kennedy 2021-07-07 20:46:44 -07:00 committed by GitHub
parent 7917ff86f0
commit d10ba3ddfa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 10 deletions

View File

@ -30,6 +30,9 @@ All notable user-facing or behavior-altering changes will be documented in this
### Breaking Changes
- vvtOffset field migrated to four vvtOffsets fields. Anyone using VVT would need to manually adjust their configuration.
### Added
- Injector nonlinearity (small pulse) correction - so far just polynomial, but table modes coming soon.
## June 2021 Release "National Logistics Day"
### Fixed

View File

@ -104,14 +104,47 @@ float InjectorModelBase::getInjectionDuration(float fuelMassGram) const {
floatms_t baseDuration = fuelMassGram / m_massFlowRate * 1000;
if (baseDuration <= 0) {
// If 0 duration, don't add deadtime, just skip the injection.
// If 0 duration, don't correct or add deadtime, just skip the injection.
return 0.0f;
} else {
return baseDuration + m_deadtime;
}
// Correct short pulses (if enabled)
baseDuration = correctShortPulse(baseDuration);
return baseDuration + m_deadtime;
}
float InjectorModelBase::getFuelMassForDuration(floatms_t duration) const {
// Convert from ms -> grams
return duration * m_massFlowRate * 0.001f;
}
float InjectorModel::correctShortPulse(float baseDuration) const {
switch (CONFIG(injectorNonlinearMode)) {
case INJ_PolynomialAdder:
return correctInjectionPolynomial(baseDuration);
case INJ_None:
default:
return baseDuration;
}
}
float InjectorModel::correctInjectionPolynomial(float baseDuration) const {
if (baseDuration > CONFIG(applyNonlinearBelowPulse)) {
// Large pulse, skip correction.
return baseDuration;
}
auto& is = CONFIG(injectorCorrectionPolynomial);
float xi = 1;
float adder = 0;
// Add polynomial terms, starting with x^0
for (size_t i = 0; i < efi::size(is); i++) {
adder += is[i] * xi;
xi *= baseDuration;
}
return baseDuration + adder;
}

View File

@ -19,6 +19,7 @@ public:
virtual float getInjectorMassFlowRate() const = 0;
virtual float getInjectorFlowRatio() const = 0;
virtual expected<float> getAbsoluteRailPressure() const = 0;
virtual float correctShortPulse(float baseDuration) const = 0;
virtual void postState(float deadTime) const { (void)deadTime; };
@ -36,4 +37,8 @@ public:
float getInjectorMassFlowRate() const override;
float getInjectorFlowRatio() const override;
expected<float> getAbsoluteRailPressure() const override;
// Small pulse correction logic
float correctShortPulse(float baseDuration) const override;
virtual float correctInjectionPolynomial(float baseDuration) const;
};

View File

@ -1059,3 +1059,8 @@ typedef enum __attribute__ ((__packed__)) {
ICM_FixedRailPressure = 1,
ICM_SensedRailPressure = 2,
} injector_compensation_mode_e;
typedef enum __attribute__ ((__packed__)) {
INJ_None = 0,
INJ_PolynomialAdder = 1,
} InjectorNonlinearMode;

View File

@ -1357,9 +1357,12 @@ tle8888_mode_e tle8888mode;
float unused2432;;"units", 1, 0, -20, 100, 0
float postCrankingFactor;+Fuel multiplier (enrichment) immediately after engine start;"mult", 1, 0, 1, 3, 2
float postCrankingDurationSec;+Time over which to taper out after start enrichment;"seconds", 1, 0, 0, 30, 0
ThermistorConf auxTempSensor1;todo: finish implementation #332
ThermistorConf auxTempSensor2;todo: finish implementation #332
uint8_t[6] unused2508;;"units", 1, 0, -20, 100, 0
ThermistorConf auxTempSensor1
ThermistorConf auxTempSensor2
uint16_t applyNonlinearBelowPulse;+Apply nonlinearity correction below a pulse of this duration. Pulses longer than this duration will receive no adjustment.;"ms", {1/1000}, 0, 0, 30, 3
custom InjectorNonlinearMode 1 bits, U08, @OFFSET@, [0:0], "None", "Polynomial"
InjectorNonlinearMode injectorNonlinearMode
uint8_t[3] unused2508;;"units", 1, 0, -20, 100, 0
int16_t etbFreq;;"Hz", 1, 0, 0, @@ETB_HW_MAX_FREQUENCY@@, 0
pid_s etbWastegatePid;
uint8_t[4] unused2536;;"units", 1, 0, -20, 100, 0
@ -1395,7 +1398,8 @@ tle8888_mode_e tle8888mode;
pid_s[CAMS_PER_BANK iterate] auxPid;
uint8_t[40] unused1366;;"units", 1, 0, -20, 100, 0
float[8 iterate] injectorCorrectionPolynomial;;"", 1, 0, -1000, 1000, 4
uint8_t[8] unused1366;;"units", 1, 0, -20, 100, 0
linear_sensor_s oilPressure;

View File

@ -1403,7 +1403,8 @@ menuDialog = main
# basic
subMenu = injectorConfig, "Injection configuration"
subMenu = injectionSettings, "Injection hardware", 0, {isInjectionEnabled == 1}
subMenu = cylinderBankSelect, "Cylinder bank selection"
subMenu = cylinderBankSelect, "Cylinder bank selection", 0, {isInjectionEnabled == 1}
subMenu = injectorNonlinear, "Injector small-pulse correction", 0, {isInjectionEnabled == 1}
subMenu = std_separator
# Air mass model
@ -1940,6 +1941,21 @@ cmd_set_engine_type_default = "@@TS_IO_TEST_COMMAND_char@@\x00\x31\x00\x00"
field = "Cylinder 11 ", cylinderBankSelect11, {isInjectionEnabled == 1 && cylindersCount > 10}
field = "Cylinder 12 ", cylinderBankSelect12, {isInjectionEnabled == 1 && cylindersCount > 11}
dialog = injectorNonlinearPolynomial, "Polynomial Adder", yAxis
field = "Add nonlinearity below pulse", applyNonlinearBelowPulse
field = "constant", injectorCorrectionPolynomial1
field = "x^1", injectorCorrectionPolynomial2
field = "x^2", injectorCorrectionPolynomial3
field = "x^3", injectorCorrectionPolynomial4
field = "x^4", injectorCorrectionPolynomial5
field = "x^5", injectorCorrectionPolynomial6
field = "x^6", injectorCorrectionPolynomial7
field = "x^7", injectorCorrectionPolynomial8
dialog = injectorNonlinear
field = "Small pulse correction mode", injectorNonlinearMode
panel = injectorNonlinearPolynomial, {1}, { injectorNonlinearMode != 0 }
dialog = testFsio, "FSIO Test"
commandButton = "FSIO#1", cmd_test_fsio1
commandButton = "FSIO#2", cmd_test_fsio2

View File

@ -5,6 +5,7 @@
#include "gtest/gtest.h"
using ::testing::_;
using ::testing::StrictMock;
class MockInjectorModel : public InjectorModelBase {
@ -13,6 +14,7 @@ public:
MOCK_METHOD(float, getInjectorMassFlowRate, (), (const, override));
MOCK_METHOD(float, getInjectorFlowRatio, (), (const, override));
MOCK_METHOD(expected<float>, getAbsoluteRailPressure, (), (const, override));
MOCK_METHOD(float, correctShortPulse, (float baseDuration), (const, override));
};
TEST(InjectorModel, Prepare) {
@ -28,9 +30,12 @@ TEST(InjectorModel, getInjectionDuration) {
StrictMock<MockInjectorModel> dut;
EXPECT_CALL(dut, getDeadtime())
.WillRepeatedly(Return(2.0f));
.WillOnce(Return(2.0f));
EXPECT_CALL(dut, getInjectorMassFlowRate())
.WillRepeatedly(Return(4.8f)); // 400cc/min
.WillOnce(Return(4.8f)); // 400cc/min
EXPECT_CALL(dut, correctShortPulse(_))
.Times(2)
.WillRepeatedly([](float b) { return b; });
dut.prepare();
@ -38,6 +43,46 @@ TEST(InjectorModel, getInjectionDuration) {
EXPECT_NEAR(dut.getInjectionDuration(0.02f), 20 / 4.8f + 2.0f, EPS4D);
}
TEST(InjectorModel, getInjectionDurationNonlinear) {
StrictMock<MockInjectorModel> dut;
EXPECT_CALL(dut, getDeadtime())
.WillOnce(Return(2.0f));
EXPECT_CALL(dut, getInjectorMassFlowRate())
.WillOnce(Return(4.8f)); // 400cc/min
// Dummy nonlinearity correction: just doubles the pulse
EXPECT_CALL(dut, correctShortPulse(_))
.Times(2)
.WillRepeatedly([](float b) { return 2 * b; });
dut.prepare();
EXPECT_NEAR(dut.getInjectionDuration(0.01f), 2 * 10 / 4.8f + 2.0f, EPS4D);
EXPECT_NEAR(dut.getInjectionDuration(0.02f), 2 * 20 / 4.8f + 2.0f, EPS4D);
}
TEST(InjectorModel, nonlinearPolynomial) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);
InjectorModel dut;
INJECT_ENGINE_REFERENCE(&dut);
CONFIG(applyNonlinearBelowPulse) = 10;
for (int i = 0; i < 8; i++) {
CONFIG(injectorCorrectionPolynomial)[i] = i + 1;
}
// expect return of the original value, plus polynomial f(x)
EXPECT_NEAR(dut.correctInjectionPolynomial(-3), -3 + -13532, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(-2), -2 + -711, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(-1), -1 + -4, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(0), 0 + 1, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(1), 1 + 36, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(2), 2 + 1793, EPS4D);
EXPECT_NEAR(dut.correctInjectionPolynomial(3), 3 + 24604, EPS4D);
}
TEST(InjectorModel, Deadtime) {
WITH_ENGINE_TEST_HELPER(TEST_ENGINE);