implement & test backend for injection splitting (#367)

* framing dual injection

* fix test

* uncomment logic

* format

* start test

* good test

* add todo
This commit is contained in:
Matthew Kennedy 2024-02-21 10:54:27 -08:00 committed by GitHub
parent 288ce40756
commit f8070a922a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 92 additions and 20 deletions

View File

@ -16,7 +16,13 @@ void endSimultaneousInjection(InjectionEvent *event) {
event->update();
}
void turnInjectionPinLow(InjectionEvent *event) {
static InjectionEvent* argToEvent(uintptr_t arg) {
return reinterpret_cast<InjectionEvent*>(arg & ~(1UL));
}
void turnInjectionPinLow(uintptr_t arg) {
auto event = argToEvent(arg);
efitick_t nowNt = getTimeNowNt();
for (size_t i = 0; i < efi::size(event->outputs); i++) {
@ -25,7 +31,19 @@ void turnInjectionPinLow(InjectionEvent *event) {
output->close(nowNt);
}
}
efitick_t nextSplitDuration = event->splitInjectionDuration;
if (nextSplitDuration > 0) {
event->splitInjectionDuration = 0;
efitick_t openTime = getTimeNowNt() + MS2NT(2);
efitick_t closeTime = openTime + nextSplitDuration;
getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, openTime, { &turnInjectionPinHigh, arg });
getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, closeTime, { turnInjectionPinLow, arg });
} else {
event->update();
}
}
static void turnInjectionPinLowStage2(InjectionEvent* event) {
@ -43,7 +61,7 @@ void turnInjectionPinHigh(uintptr_t arg) {
efitick_t nowNt = getTimeNowNt();
// clear last bit to recover the pointer
InjectionEvent *event = reinterpret_cast<InjectionEvent*>(arg & ~(1UL));
InjectionEvent* event = argToEvent(arg);
// extract last bit
bool stage2Active = arg & 1;
@ -75,6 +93,10 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
return;
}
// don't allow split inj in simultaneous mode
// TODO: #364 implement logic to actually enable split injections
bool doSplitInjection = false && !isSimultaneous;
// Select fuel mass from the correct cylinder
auto injectionMassGrams = getEngineState()->injectionMass[this->cylinderNumber];
@ -83,8 +105,8 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
// TODO: is it correct to wall wet on both pulses?
injectionMassGrams = wallFuel.adjust(injectionMassGrams);
// Disable staging in simultaneous mode
float stage2Fraction = isSimultaneous ? 0 : getEngineState()->injectionStage2Fraction;
// Disable staging in simultaneous mode or split injection mode
float stage2Fraction = (isSimultaneous || doSplitInjection) ? 0 : getEngineState()->injectionStage2Fraction;
// Compute fraction of fuel on stage 2, remainder goes on stage 1
const float injectionMassStage2 = stage2Fraction * injectionMassGrams;
@ -101,6 +123,11 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
engine->module<TripOdometer>()->consumeFuel(actualInjectedMass, nowNt);
}
if (doSplitInjection) {
// If in split mode, do the injection in two halves
injectionMassStage1 = injectionMassStage1 / 2;
}
const floatms_t injectionDurationStage1 = engine->module<InjectorModelPrimary>()->getInjectionDuration(injectionMassStage1);
const floatms_t injectionDurationStage2 = injectionMassStage2 > 0 ? engine->module<InjectorModelSecondary>()->getInjectionDuration(injectionMassStage2) : 0;
@ -164,7 +191,7 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
// sequential or batch
startAction = { &turnInjectionPinHigh, startActionPtr };
endActionStage1 = { &turnInjectionPinLow, this };
endActionStage1 = { &turnInjectionPinLow, startActionPtr };
endActionStage2 = { &turnInjectionPinLowStage2, this };
}
@ -178,7 +205,15 @@ void InjectionEvent::onTriggerTooth(efitick_t nowNt, float currentPhase, float n
efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
// Schedule closing stage 1
efitick_t turnOffTimeStage1 = startTime + US2NT((int)durationUsStage1);
efitick_t durationStage1Nt = US2NT((int)durationUsStage1);
efitick_t turnOffTimeStage1 = startTime + durationStage1Nt;
if (doSplitInjection) {
this->splitInjectionDuration = durationStage1Nt;
} else {
this->splitInjectionDuration = 0;
}
getExecutorInterface()->scheduleByTimestampNt("inj", nullptr, turnOffTimeStage1, endActionStage1);
// Schedule closing stage 2 (if applicable)

View File

@ -50,6 +50,7 @@ public:
InjectorOutputPin *outputs[MAX_WIRES_COUNT];
InjectorOutputPin *outputsStage2[MAX_WIRES_COUNT];
float injectionStartAngle = 0;
efitick_t splitInjectionDuration = 0;
};
void turnInjectionPinHigh(uintptr_t arg);

View File

@ -14,4 +14,4 @@
void mainTriggerCallback(uint32_t trgEventIndex, efitick_t edgeTimestamp, angle_t currentPhase, angle_t nextPhase);
void endSimultaneousInjection(InjectionEvent *event);
void turnInjectionPinLow(InjectionEvent *event);
void turnInjectionPinLow(uintptr_t arg);

View File

@ -93,7 +93,7 @@ TEST(injectionScheduling, InjectionIsScheduledDualStage) {
efitick_t startTime = nowNt + nt5deg;
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Truly(ActionArgumentHasLowBitSet)));
// falling edge (primary) 20ms later
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Property(&action_s::getArgument, Eq(&event))));
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(20), Truly(ActionArgumentHasLowBitSet)));
// falling edge (secondary) 10ms later
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime + MS2NT(10), Property(&action_s::getArgument, Eq(&event))));
}
@ -216,3 +216,39 @@ TEST(injectionScheduling, InjectionNotScheduled) {
// We are at 130 degrees now, next tooth 140
event.onTriggerTooth(nowNt, 130, 140);
}
TEST(injectionScheduling, SplitInjectionScheduled) {
StrictMock<MockExecutor> mockExec;
EngineTestHelper eth(engine_type_e::TEST_ENGINE);
engine->executor.setMockExecutor(&mockExec);
InjectionEvent event;
uintptr_t arg = reinterpret_cast<uintptr_t>(&event);
InjectorOutputPin pin;
pin.shortName = "test";
pin.injectorIndex = 0;
event.outputs[0] = &pin;
{
InSequence is;
// Should schedule second half of split injection:
// - starts 2ms from now
// - duration 10ms (ends 12ms from now)
efitick_t nowNt = getTimeNowNt();
efitick_t startTime = nowNt + MS2NT(2);
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, startTime, Property(&action_s::getArgument, Eq(&event))));
efitick_t endTime = startTime + MS2NT(10);
EXPECT_CALL(mockExec, scheduleByTimestampNt(testing::NotNull(), _, endTime, Property(&action_s::getArgument, Eq(&event))));
}
// Split injection duration of 10ms
event.splitInjectionDuration = MS2NT(10);
// Close injector, should cause second half of split injection to be scheduled!
turnInjectionPinLow(arg);
// Expect it to get zeroed so we don't repeat ad infinitum
EXPECT_EQ(event.splitInjectionDuration, 0);
}