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;
// Per-injection fuel mass, including TPS accel enrich
float injectionMass = 0;
/**
* This one with wall wetting accounted for, used for logging.
*/

View File

@ -19,6 +19,7 @@
#include "closed_loop_fuel.h"
#include "sensor.h"
#include "launch_control.h"
#include "injector_model.h"
#if EFI_PROD_CODE
@ -170,7 +171,11 @@ void EngineState::periodicFastCallback(DECLARE_ENGINE_PARAMETER_SIGNATURE) {
auto tps = Sensor::get(SensorType::Tps1);
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);
injectionOffset = getInjectionOffset(rpm, fuelLoad PASS_ENGINE_PARAMETER_SUFFIX);

View File

@ -100,3 +100,8 @@ float InjectorModelBase::getInjectionDuration(float fuelMassGram) const {
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 {
virtual void prepare() = 0;
virtual floatms_t getInjectionDuration(float fuelMassGram) const = 0;
virtual float getFuelMassForDuration(floatms_t duration) const = 0;
};
class InjectorModelBase : public IInjectorModel {
public:
void prepare() override;
floatms_t getInjectionDuration(float fuelMassGram) const override;
float getFuelMassForDuration(floatms_t duration) const override;
virtual floatms_t getDeadtime() 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
* 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);
#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 injectionFuelMass = cycleFuelMass * durationMultiplier;
// Prepare injector flow rate & deadtime
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));
efiAssert(CUSTOM_ERR_ASSERT, !cisnan(tpsAccelEnrich), "NaN tpsAccelEnrich", 0);
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
return 0;
#endif

View File

@ -27,7 +27,7 @@ float getFuelCutOffCorrection(efitick_t nowNt, int rpm DECLARE_ENGINE_PARAMETER_
angle_t getCltTimingCorrection(DECLARE_ENGINE_PARAMETER_SIGNATURE);
float getCrankingFuel(float baseFuel 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);
float getStandardAirCharge(DECLARE_ENGINE_PARAMETER_SIGNATURE);

View File

@ -52,6 +52,7 @@
#include "engine.h"
#include "perf_trace.h"
#include "sensor.h"
#include "injector_model.h"
#if EFI_LAUNCH_CONTROL
#include "launch_control.h"
#endif
@ -203,13 +204,11 @@ void InjectionEvent::onTriggerTooth(size_t trgEventIndex, int rpm, efitick_t now
return;
}
/**
* todo: this is a bit tricky with batched injection. is it? Does the same
* wetting coefficient works the same way for any injection mode, or is something
* x2 or /2?
*/
// Perform wall wetting adjustment on fuel mass, not duration, so that
// it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
const float injectionMass = wallFuel.adjust(ENGINE(injectionMass) PASS_ENGINE_PARAMETER_SUFFIX);
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 (printFuelDebug) {
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_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) {
return;
}
@ -348,12 +353,6 @@ static void handleFuel(const bool limitedFuel, uint32_t trgEventIndex, int rpm,
}
#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);
}
@ -542,7 +541,7 @@ static void showMainInfo(Engine *engine) {
int rpm = GET_RPM();
float el = getFuelingLoad(PASS_ENGINE_PARAMETER_SIGNATURE);
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 */
}

View File

@ -5,6 +5,7 @@
#include "table_helper.h"
#include "pwm_generator_logic.h"
#include "airmass.h"
#include "injector_model.h"
#include "gmock/gmock.h"
@ -66,3 +67,10 @@ public:
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 "main_trigger_callback.h"
#include "injector_model.h"
#include <gmock/gmock.h>
#include "mocks.h"
@ -23,7 +24,9 @@ TEST(injectionScheduling, NormalDutyCycle) {
event.outputs[0] = &pin;
// Injection duration of 20ms
engine->injectionDuration = 20.0f;
MockInjectorModel2 im;
EXPECT_CALL(im, getInjectionDuration(_)).WillOnce(Return(20.0f));
engine->injectorModel = &im;
{
InSequence is;

View File

@ -704,7 +704,13 @@ void doTestFuelSchedulerBug299smallAndMedium(int startUpDelayMs) {
assertInjectors("#0_inj", 0, 0);
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
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));
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);
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
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";
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
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));
@ -1048,7 +1065,13 @@ TEST(big, testFuelSchedulerBug299smallAndLarge) {
ASSERT_EQ( 0, engine->executor.size()) << "Lqs#04";
engine->periodicFastCallback(PASS_ENGINE_PARAMETER_SIGNATURE);
// Injection duration of 2ms
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";