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:
parent
b87d82735d
commit
5067b81fce
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue