mirror of https://github.com/FOME-Tech/fome-fw.git
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:
parent
288ce40756
commit
f8070a922a
|
@ -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);
|
||||
}
|
||||
}
|
||||
event->update();
|
||||
|
||||
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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -103,7 +103,7 @@ bool RegisteredOutputPin::isPinConfigurationChanged() {
|
|||
pin_output_mode_e newMode = *(pin_output_mode_e *) ((void *) (&((char*) engineConfiguration)[m_pinModeOffset]));
|
||||
return pinChanged || curMode != newMode;
|
||||
#else
|
||||
return true;
|
||||
return true;
|
||||
#endif // EFI_PROD_CODE
|
||||
}
|
||||
|
||||
|
@ -117,9 +117,9 @@ void RegisteredOutputPin::init() {
|
|||
newMode = OM_DEFAULT;
|
||||
}
|
||||
|
||||
if (isPinConfigurationChanged()) {
|
||||
if (isPinConfigurationChanged()) {
|
||||
initPin(m_registrationName, newPin, newMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RegisteredOutputPin::unregister() {
|
||||
|
@ -374,7 +374,7 @@ void NamedOutputPin::setHigh() {
|
|||
setValue(true);
|
||||
|
||||
#if EFI_ENGINE_SNIFFER
|
||||
addEngineSnifferOutputPinEvent(this, true);
|
||||
addEngineSnifferOutputPinEvent(this, true);
|
||||
#endif /* EFI_ENGINE_SNIFFER */
|
||||
}
|
||||
|
||||
|
@ -707,7 +707,7 @@ static void initErrorLed(Gpio led) {
|
|||
|
||||
void initPrimaryPins() {
|
||||
#if EFI_PROD_CODE
|
||||
initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
|
||||
initErrorLed(LED_CRITICAL_ERROR_BRAIN_PIN);
|
||||
|
||||
addConsoleAction("gpio_pins", EnginePins::debug);
|
||||
#endif /* EFI_PROD_CODE */
|
||||
|
|
|
@ -84,8 +84,8 @@ void InjectorOutputPin::close(efitick_t nowNt) {
|
|||
}
|
||||
|
||||
void InjectorOutputPin::setHigh() {
|
||||
NamedOutputPin::setHigh();
|
||||
TunerStudioOutputChannels *state = getTunerStudioOutputChannels();
|
||||
NamedOutputPin::setHigh();
|
||||
TunerStudioOutputChannels *state = getTunerStudioOutputChannels();
|
||||
// this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
|
||||
switch (injectorIndex) {
|
||||
case 0:
|
||||
|
@ -104,8 +104,8 @@ void InjectorOutputPin::setHigh() {
|
|||
}
|
||||
|
||||
void InjectorOutputPin::setLow() {
|
||||
NamedOutputPin::setLow();
|
||||
TunerStudioOutputChannels *state = getTunerStudioOutputChannels();
|
||||
NamedOutputPin::setLow();
|
||||
TunerStudioOutputChannels *state = getTunerStudioOutputChannels();
|
||||
// this is NASTY but what's the better option? bytes? At cost of 22 extra bytes in output status packet?
|
||||
switch (injectorIndex) {
|
||||
case 0:
|
||||
|
|
|
@ -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))));
|
||||
}
|
||||
|
@ -209,10 +209,46 @@ TEST(injectionScheduling, InjectionNotScheduled) {
|
|||
// Expect no scheduler calls!
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Event scheduled at 125 degrees
|
||||
event.injectionStartAngle = 125;
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue