Compute injection duration later in the pipeline (#2175)

* injector model, test

* new math

* inject fuel based on new math

* tests

* fix

* it should work like this

* format

* update TPS AE even when we're cutting fuel

* comment

* conversion factor

Co-authored-by: Matthew Kennedy <makenne@microsoft.com>
This commit is contained in:
Matthew Kennedy 2021-03-03 04:30:56 -08:00 committed by GitHub
parent b87d82735d
commit 5067b81fce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 72 additions and 21 deletions

View File

@ -237,6 +237,9 @@ public:
*/ */
floatms_t injectionDuration = 0; floatms_t injectionDuration = 0;
// Per-injection fuel mass, including TPS accel enrich
float injectionMass = 0;
/** /**
* This one with wall wetting accounted for, used for logging. * This one with wall wetting accounted for, used for logging.
*/ */

View File

@ -19,6 +19,7 @@
#include "closed_loop_fuel.h" #include "closed_loop_fuel.h"
#include "sensor.h" #include "sensor.h"
#include "launch_control.h" #include "launch_control.h"
#include "injector_model.h"
#if EFI_PROD_CODE #if EFI_PROD_CODE
@ -170,7 +171,11 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
auto tps = Sensor::get(SensorType::Tps1); auto tps = Sensor::get(SensorType::Tps1);
updateTChargeK(rpm, tps.value_or(0) PASS_ENGINE_PARAMETER_SUFFIX); updateTChargeK(rpm, tps.value_or(0) PASS_ENGINE_PARAMETER_SUFFIX);
ENGINE(injectionDuration) = getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX);
float injectionMass = getInjectionMass(rpm PASS_ENGINE_PARAMETER_SUFFIX);
ENGINE(injectionMass) = injectionMass;
// Store the pre-wall wetting injection duration for scheduling purposes only, not the actual injection duration
ENGINE(injectionDuration) = ENGINE(injectorModel)->getInjectionDuration(injectionMass);
float fuelLoad = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE); float fuelLoad = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
injectionOffset = getInjectionOffset(rpm, fuelLoad PASS_ENGINE_PARAMETER_SUFFIX); injectionOffset = getInjectionOffset(rpm, fuelLoad PASS_ENGINE_PARAMETER_SUFFIX);

View File

@ -100,3 +100,8 @@ float InjectorModelBase::getInjectionDuration(float fuelMassGram) const {
return baseDuration + m_deadtime; return baseDuration + m_deadtime;
} }
} }
float InjectorModelBase::getFuelMassForDuration(floatms_t duration) const {
// Convert from ms -> grams
return duration * m_massFlowRate * 0.001f;
}

View File

@ -6,12 +6,14 @@
struct IInjectorModel { struct IInjectorModel {
virtual void prepare() = 0; virtual void prepare() = 0;
virtual floatms_t getInjectionDuration(float fuelMassGram) const = 0; virtual floatms_t getInjectionDuration(float fuelMassGram) const = 0;
virtual float getFuelMassForDuration(floatms_t duration) const = 0;
}; };
class InjectorModelBase : public IInjectorModel { class InjectorModelBase : public IInjectorModel {
public: public:
void prepare() override; void prepare() override;
floatms_t getInjectionDuration(float fuelMassGram) const override; floatms_t getInjectionDuration(float fuelMassGram) const override;
float getFuelMassForDuration(floatms_t duration) const override;
virtual floatms_t getDeadtime() const = 0; virtual floatms_t getDeadtime() const = 0;
virtual float getInjectorMassFlowRate() const = 0; virtual float getInjectorMassFlowRate() const = 0;

View File

@ -300,7 +300,7 @@ static float getCycleFuelMass(bool isCranking, float baseFuelMass DECLARE_ENGINE
* @returns Length of each individual fuel injection, in milliseconds * @returns Length of each individual fuel injection, in milliseconds
* in case of single point injection mode the amount of fuel into all cylinders, otherwise the amount for one cylinder * in case of single point injection mode the amount of fuel into all cylinders, otherwise the amount for one cylinder
*/ */
floatms_t getInjectionDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) { floatms_t getInjectionMass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
ScopePerf perf(PE::GetInjectionDuration); ScopePerf perf(PE::GetInjectionDuration);
#if EFI_SHAFT_POSITION_INPUT #if EFI_SHAFT_POSITION_INPUT
@ -317,16 +317,19 @@ floatms_t getInjectionDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX) {
float durationMultiplier = getInjectionModeDurationMultiplier(PASS_ENGINE_PARAMETER_SIGNATURE); float durationMultiplier = getInjectionModeDurationMultiplier(PASS_ENGINE_PARAMETER_SIGNATURE);
float injectionFuelMass = cycleFuelMass * durationMultiplier; float injectionFuelMass = cycleFuelMass * durationMultiplier;
// Prepare injector flow rate & deadtime
ENGINE(injectorModel)->prepare(); ENGINE(injectorModel)->prepare();
// TODO: move everything below here to injector scheduling, so that wall wetting works properly
floatms_t injectionDuration = ENGINE(injectorModel)->getInjectionDuration(injectionFuelMass);
floatms_t tpsAccelEnrich = ENGINE(tpsAccelEnrichment.getTpsEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE)); floatms_t tpsAccelEnrich = ENGINE(tpsAccelEnrichment.getTpsEnrichment(PASS_ENGINE_PARAMETER_SIGNATURE));
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0); efiAssert(CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
ENGINE(engineState.tpsAccelEnrich) = tpsAccelEnrich; ENGINE(engineState.tpsAccelEnrich) = tpsAccelEnrich;
return injectionDuration + (durationMultiplier * tpsAccelEnrich); // For legacy reasons, the TPS accel table is in units of milliseconds, so we have to convert BACK to mass
float tpsAccelPerInjection = durationMultiplier * tpsAccelEnrich;
float tpsFuelMass = ENGINE(injectorModel)->getFuelMassForDuration(tpsAccelPerInjection);
return injectionFuelMass + tpsFuelMass;
#else #else
return 0; return 0;
#endif #endif

View File

@ -27,7 +27,7 @@ float getFuelCutOffCorrection(efitick_t nowNt, int rpm DECLARE_ENGINE_PARAMETER_
angle_t getCltTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE); angle_t getCltTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
float getCrankingFuel(float baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX); float getCrankingFuel(float baseFuel DECLARE_ENGINE_PARAMETER_SUFFIX);
float getCrankingFuel3(float baseFuel, uint32_t revolutionCounterSinceStart DECLARE_ENGINE_PARAMETER_SUFFIX); float getCrankingFuel3(float baseFuel, uint32_t revolutionCounterSinceStart DECLARE_ENGINE_PARAMETER_SUFFIX);
floatms_t getInjectionDuration(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); floatms_t getInjectionMass(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
percent_t getInjectorDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX); percent_t getInjectorDutyCycle(int rpm DECLARE_ENGINE_PARAMETER_SUFFIX);
float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE); float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE);

View File

@ -52,6 +52,7 @@
#include "engine.h" #include "engine.h"
#include "perf_trace.h" #include "perf_trace.h"
#include "sensor.h" #include "sensor.h"
#include "injector_model.h"
#if EFI_LAUNCH_CONTROL #if EFI_LAUNCH_CONTROL
#include "launch_control.h" #include "launch_control.h"
#endif #endif
@ -203,13 +204,11 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now
return; return;
} }
/** // Perform wall wetting adjustment on fuel mass, not duration, so that
* todo: this is a bit tricky with batched injection. is it? Does the same // it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
* wetting coefficient works the same way for any injection mode, or is something const float injectionMass = wallFuel.adjust(ENGINE(injectionMass) PASS_ENGINE_PARAMETER_SUFFIX);
* x2 or /2? const floatms_t injectionDuration = ENGINE(injectorModel)->getInjectionDuration(injectionMass);
*/
const floatms_t injectionDuration = wallFuel.adjust(ENGINE(injectionDuration) PASS_ENGINE_PARAMETER_SUFFIX);
#if EFI_PRINTF_FUEL_DETAILS #if EFI_PRINTF_FUEL_DETAILS
if (printFuelDebug) { if (printFuelDebug) {
printf("fuel index=%d injectionDuration=%.2fms adjusted=%.2fms\n", printf("fuel index=%d injectionDuration=%.2fms adjusted=%.2fms\n",
@ -320,6 +319,12 @@ static void handleFuel(const bool limitedFuel, uint32_t trgEventIndex, int rpm,
efiAssertVoid(CUSTOM_STACK_6627, getCurrentRemainingStack() > 128, "lowstck#3"); efiAssertVoid(CUSTOM_STACK_6627, getCurrentRemainingStack() > 128, "lowstck#3");
efiAssertVoid(CUSTOM_ERR_6628, trgEventIndex < engine->engineCycleEventCount, "handleFuel/event index"); efiAssertVoid(CUSTOM_ERR_6628, trgEventIndex < engine->engineCycleEventCount, "handleFuel/event index");
ENGINE(tpsAccelEnrichment.onNewValue(Sensor::get(SensorType::Tps1).value_or(0) PASS_ENGINE_PARAMETER_SUFFIX));
if (trgEventIndex == 0) {
ENGINE(tpsAccelEnrichment.onEngineCycleTps(PASS_ENGINE_PARAMETER_SIGNATURE));
ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE));
}
if (limitedFuel) { if (limitedFuel) {
return; return;
} }
@ -348,12 +353,6 @@ static void handleFuel(const bool limitedFuel, uint32_t trgEventIndex, int rpm,
} }
#endif /* FUEL_MATH_EXTREME_LOGGING */ #endif /* FUEL_MATH_EXTREME_LOGGING */
ENGINE(tpsAccelEnrichment.onNewValue(Sensor::get(SensorType::Tps1).value_or(0) PASS_ENGINE_PARAMETER_SUFFIX));
if (trgEventIndex == 0) {
ENGINE(tpsAccelEnrichment.onEngineCycleTps(PASS_ENGINE_PARAMETER_SIGNATURE));
ENGINE(engineLoadAccelEnrichment.onEngineCycle(PASS_ENGINE_PARAMETER_SIGNATURE));
}
fs->onTriggerTooth(trgEventIndex, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX); fs->onTriggerTooth(trgEventIndex, rpm, nowNt PASS_ENGINE_PARAMETER_SUFFIX);
} }
@ -542,7 +541,7 @@ static void showMainInfo(Engine *engine) {
int rpm = GET_RPM(); int rpm = GET_RPM();
float el = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE); float el = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
scheduleMsg(logger, "rpm %d engine_load %.2f", rpm, el); scheduleMsg(logger, "rpm %d engine_load %.2f", rpm, el);
scheduleMsg(logger, "fuel %.2fms timing %.2f", getInjectionDuration(rpm PASS_ENGINE_PARAMETER_SUFFIX), engine->engineState.timingAdvance); scheduleMsg(logger, "fuel %.2fms timing %.2f", ENGINE(injectionDuration), engine->engineState.timingAdvance);
#endif /* EFI_PROD_CODE */ #endif /* EFI_PROD_CODE */
} }

View File

@ -5,6 +5,7 @@
#include "table_helper.h" #include "table_helper.h"
#include "pwm_generator_logic.h" #include "pwm_generator_logic.h"
#include "airmass.h" #include "airmass.h"
#include "injector_model.h"
#include "gmock/gmock.h" #include "gmock/gmock.h"
@ -66,3 +67,10 @@ public:
MOCK_METHOD(AirmassResult, getAirmass, (int rpm), (override)); MOCK_METHOD(AirmassResult, getAirmass, (int rpm), (override));
}; };
class MockInjectorModel2 : public IInjectorModel {
public:
MOCK_METHOD(void, prepare, (), (override));
MOCK_METHOD(floatms_t, getInjectionDuration, (float fuelMassGram), (const, override));
MOCK_METHOD(float, getFuelMassForDuration, (floatms_t duration), (const, override));
};

View File

@ -1,5 +1,6 @@
#include "engine_test_helper.h" #include "engine_test_helper.h"
#include "main_trigger_callback.h" #include "main_trigger_callback.h"
#include "injector_model.h"
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "mocks.h" #include "mocks.h"
@ -23,7 +24,9 @@ TEST(injectionScheduling, NormalDutyCycle) {
event.outputs[0] = &pin; event.outputs[0] = &pin;
// Injection duration of 20ms // Injection duration of 20ms
engine->injectionDuration = 20.0f; MockInjectorModel2 im;
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
engine->injectorModel = &im;
{ {
InSequence is; InSequence is;

View File

@ -704,7 +704,13 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
assertInjectors("#0_inj", 0, 0); assertInjectors("#0_inj", 0, 0);
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE); engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
engine->injectionDuration = 12.5f; engine->injectionDuration = 12.5f;
// Injection duration of 12.5ms
MockInjectorModel2 im;
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(12.5f));
engine->injectorModel = &im;
assertEqualsM("duty for maf=3", 62.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX)); assertEqualsM("duty for maf=3", 62.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX));
ASSERT_EQ( 4, engine->executor.size()) << "qs#1"; ASSERT_EQ( 4, engine->executor.size()) << "qs#1";
@ -860,6 +866,11 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
assertInjectionEvent("#3#", &t->elements[3], 1, 0, 45 + 90); assertInjectionEvent("#3#", &t->elements[3], 1, 0, 45 + 90);
engine->injectionDuration = 17.5; engine->injectionDuration = 17.5;
// Injection duration of 17.5ms
MockInjectorModel2 im2;
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
engine->injectorModel = &im2;
// duty cycle above 75% is a special use-case because 'special' fuel event overlappes the next normal event in batch mode // duty cycle above 75% is a special use-case because 'special' fuel event overlappes the next normal event in batch mode
assertEqualsM("duty for maf=3", 87.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX)); assertEqualsM("duty for maf=3", 87.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX));
@ -987,7 +998,13 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
ASSERT_EQ( 4, engine->executor.size()) << "Lqs#0"; ASSERT_EQ( 4, engine->executor.size()) << "Lqs#0";
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE); engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
engine->injectionDuration = 17.5f; engine->injectionDuration = 17.5f;
// Injection duration of 17.5ms
MockInjectorModel2 im;
EXPECT_CALL(im, getInjectionDuration(_)).WillRepeatedly(Return(17.5f));
engine->injectorModel = &im;
assertEqualsM("Lduty for maf=3", 87.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX)); assertEqualsM("Lduty for maf=3", 87.5, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX));
@ -1048,7 +1065,13 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
ASSERT_EQ( 0, engine->executor.size()) << "Lqs#04"; ASSERT_EQ( 0, engine->executor.size()) << "Lqs#04";
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE); engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Injection duration of 2ms
engine->injectionDuration = 2.0f; engine->injectionDuration = 2.0f;
MockInjectorModel2 im2;
EXPECT_CALL(im2, getInjectionDuration(_)).WillRepeatedly(Return(2.0f));
engine->injectorModel = &im2;
ASSERT_EQ( 10, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX)) << "Lduty for maf=3"; ASSERT_EQ( 10, getInjectorDutyCycle(GET_RPM() PASS_ENGINE_PARAMETER_SUFFIX)) << "Lduty for maf=3";