rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
Public Member Functions | Data Fields | Private Member Functions | Private Attributes
InjectionEvent Class Reference

#include <fuel_schedule.h>

Collaboration diagram for InjectionEvent:
Collaboration graph
[legend]

Public Member Functions

 InjectionEvent ()=default
 
bool update ()
 
void onTriggerTooth (efitick_t nowNt, float currentPhase, float nextPhase)
 
WallFuelgetWallFuel ()
 
void setIndex (uint8_t index)
 

Data Fields

InjectorOutputPinoutputs [MAX_WIRES_COUNT] {}
 
InjectorOutputPinoutputsStage2 [MAX_WIRES_COUNT] {}
 
float injectionStartAngle = 0
 

Private Member Functions

bool updateInjectionAngle ()
 
expected< floatcomputeInjectionAngle () const
 

Private Attributes

bool isSimultaneous = false
 
uint8_t ownIndex = 0
 
uint8_t cylinderNumber = 0
 
WallFuel wallFuel {}
 

Detailed Description

Definition at line 16 of file fuel_schedule.h.

Constructor & Destructor Documentation

◆ InjectionEvent()

InjectionEvent::InjectionEvent ( )
default

Member Function Documentation

◆ computeInjectionAngle()

expected< float > InjectionEvent::computeInjectionAngle ( ) const
private

Definition at line 78 of file fuel_schedule.cpp.

78 {
79 floatus_t oneDegreeUs = getEngineRotationState()->getOneDegreeUs(); // local copy
80 if (std::isnan(oneDegreeUs)) {
81 // in order to have fuel schedule we need to have current RPM
82 // wonder if this line slows engine startup?
83 return unexpected;
84 }
85
86 float fuelMs = getEngineState()->injectionDuration;
87 if (std::isnan(fuelMs)) {
88 return unexpected;
89 }
90
91 // injection phase may be scheduled by injection end, so we need to step the angle back
92 // for the duration of the injection
93 angle_t injectionDurationAngle = getInjectionAngleCorrection(fuelMs, oneDegreeUs);
94
95 // User configured offset - degrees after TDC combustion
97 if (std::isnan(injectionOffset)) {
98 // injection offset map not ready - we are not ready to schedule fuel events
99 return unexpected;
100 }
101
102 angle_t openingAngle = injectionOffset - injectionDurationAngle;
103 assertAngleRange(openingAngle, "openingAngle_r", ObdCode::CUSTOM_ERR_6554);
104 wrapAngle(openingAngle, "addFuel#1", ObdCode::CUSTOM_ERR_6555);
105 // TODO: should we log per-cylinder injection timing? #76
107
108 // Convert from cylinder-relative to cylinder-1-relative
110
111 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(openingAngle), "findAngle#3", false);
112 assertAngleRange(openingAngle, "findAngle#a33", ObdCode::CUSTOM_ERR_6544);
113
114 wrapAngle(openingAngle, "addFuel#2", ObdCode::CUSTOM_ERR_6555);
115
116#if EFI_UNIT_TEST
117// printf("registerInjectionEvent openingAngle=%.2f inj %d\r\n", openingAngle, cylinderNumber);
118#endif
119
120 return openingAngle;
121}
virtual floatus_t getOneDegreeUs()=0
angle_t injectionOffset
floatms_t injectionDuration
uint8_t cylinderNumber
EngineRotationState * getEngineRotationState()
Definition engine.cpp:573
EngineState * getEngineState()
Definition engine.cpp:577
angle_t getPerCylinderFiringOrderOffset(uint8_t cylinderIndex, uint8_t cylinderNumber)
static float getInjectionAngleCorrection(float fuelMs, float oneDegreeUs)
@ CUSTOM_ERR_6544
@ CUSTOM_ERR_ASSERT
@ CUSTOM_ERR_6555
@ CUSTOM_ERR_6554
float angle_t
injectionOffset("Fuel: Injection timing SOI", SensorCategory.SENSOR_INPUTS, FieldType.INT16, 56, 1.0, 0.0, 0.0, "deg")
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)
TunerStudioOutputChannels * getTunerStudioOutputChannels()
Definition engine.cpp:581

Referenced by updateInjectionAngle().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getWallFuel()

WallFuel & InjectionEvent::getWallFuel ( )

Definition at line 39 of file fuel_schedule.cpp.

39 {
40 return wallFuel;
41}
WallFuel wallFuel

Referenced by getLiveData(), resetAccel(), and updateFuelInfo().

Here is the caller graph for this function:

◆ onTriggerTooth()

void InjectionEvent::onTriggerTooth ( efitick_t  nowNt,
float  currentPhase,
float  nextPhase 
)

Definition at line 78 of file main_trigger_callback.cpp.

78 {
79 auto eventAngle = injectionStartAngle;
80
81 // Determine whether our angle is going to happen before (or near) the next tooth
82 if (!isPhaseInRange(eventAngle, currentPhase, nextPhase)) {
83 return;
84 }
85
86 // Select fuel mass from the correct cylinder
87 auto injectionMassGrams = getEngineState()->injectionMass[this->cylinderNumber];
88
89 // Perform wall wetting adjustment on fuel mass, not duration, so that
90 // it's correct during fuel pressure (injector flow) or battery voltage (deadtime) transients
91 // TODO: is it correct to wall wet on both pulses?
92 injectionMassGrams = wallFuel.adjust(injectionMassGrams);
93
94 // Disable staging in simultaneous mode
95 float stage2Fraction = isSimultaneous ? 0 : getEngineState()->injectionStage2Fraction;
96
97 // Compute fraction of fuel on stage 2, remainder goes on stage 1
98 const float injectionMassStage2 = stage2Fraction * injectionMassGrams;
99 float injectionMassStage1 = injectionMassGrams - injectionMassStage2;
100
101#if EFI_VEHICLE_SPEED
102 {
103 // Log this fuel as consumed
104
105 bool isCranking = getEngineRotationState()->isCranking();
106 int numberOfInjections = isCranking ? getNumberOfInjections(engineConfiguration->crankingInjectionMode) : getNumberOfInjections(engineConfiguration->injectionMode);
107
108 float actualInjectedMass = numberOfInjections * (injectionMassStage1 + injectionMassStage2);
109
110#ifdef MODULE_ODOMETER
111 engine->module<TripOdometer>()->consumeFuel(actualInjectedMass, nowNt);
112#endif // MODULE_ODOMETER
113
114 }
115#endif // EFI_VEHICLE_SPEED
116
117 const floatms_t injectionDurationStage1 = engine->module<InjectorModelPrimary>()->getInjectionDuration(injectionMassStage1);
118 const floatms_t injectionDurationStage2 = injectionMassStage2 > 0 ? engine->module<InjectorModelSecondary>()->getInjectionDuration(injectionMassStage2) : 0;
119
120#if EFI_PRINTF_FUEL_DETAILS
121 if (printFuelDebug) {
122 printf("fuel injectionDuration=%.2fms adjusted=%.2fms\n",
123 getEngineState()->injectionDuration,
124 injectionDurationStage1);
125 }
126#endif /*EFI_PRINTF_FUEL_DETAILS */
127
128 if (this->cylinderNumber == 0) {
131 } else {
133 }
136 } else {
138 }
139 engine->outputChannels.actualLastInjection = injectionDurationStage1;
140 engine->outputChannels.actualLastInjectionStage2 = injectionDurationStage2;
141 }
142
143 if (std::isnan(injectionDurationStage1) || std::isnan(injectionDurationStage2)) {
144 warning(ObdCode::CUSTOM_OBD_NAN_INJECTION, "NaN injection pulse");
145 return;
146 }
147 if (injectionDurationStage1 < 0) {
148 warning(ObdCode::CUSTOM_OBD_NEG_INJECTION, "Negative injection pulse %.2f", injectionDurationStage1);
149 return;
150 }
151
152 // If somebody commanded an impossibly short injection, do nothing.
153 // Durations under 50us-ish aren't safe for the scheduler
154 // as their order may be swapped, resulting in a stuck open injector
155 // see https://github.com/rusefi/rusefi/pull/596 for more details
156 if (injectionDurationStage1 < 0.050f)
157 {
158 return;
159 }
160
161 floatus_t durationUsStage1 = MS2US(injectionDurationStage1);
162 floatus_t durationUsStage2 = MS2US(injectionDurationStage2);
163
164 // Only bother with the second stage if it's long enough to be relevant
165 bool hasStage2Injection = durationUsStage2 > 50;
166
167#if EFI_PRINTF_FUEL_DETAILS
168 if (printFuelDebug) {
169 InjectorOutputPin *output = outputs[0];
170 printf("handleFuelInjectionEvent fuelout %s injection_duration %dus engineCycleDuration=%.1fms\t\n", output->getName(), (int)durationUsStage1,
172 }
173#endif /*EFI_PRINTF_FUEL_DETAILS */
174
175 action_s startAction, endActionStage1, endActionStage2;
176 // We use different callbacks based on whether we're running sequential mode or not - everything else is the same
177 if (isSimultaneous) {
178 startAction = action_s::make<startSimultaneousInjection>();
179 endActionStage1 = action_s::make<endSimultaneousInjection>(this);
180 } else {
181 auto const taggedPointer{TaggedPointer<decltype(this)>::make(this, hasStage2Injection)};
182
183 // sequential or batch
184 startAction = action_s::make<turnInjectionPinHigh>( taggedPointer.getRaw() );
185 endActionStage1 = action_s::make<turnInjectionPinLow>( this );
186 endActionStage2 = action_s::make<turnInjectionPinLowStage2>( this );
187 }
188
189 // Correctly wrap injection start angle
190 float angleFromNow = eventAngle - currentPhase;
191 if (angleFromNow < 0) {
192 angleFromNow += getEngineState()->engineCycle;
193 }
194
195 // Schedule opening (stage 1 + stage 2 open together)
196 efitick_t startTime = scheduleByAngle(nullptr, nowNt, angleFromNow, startAction);
197
198 // Schedule closing stage 1
199 efitick_t turnOffTimeStage1 = startTime + US2NT((int)durationUsStage1);
200 getScheduler()->schedule("inj", nullptr, turnOffTimeStage1, endActionStage1);
201
202 // Schedule closing stage 2 (if applicable)
203 if (hasStage2Injection && endActionStage2) {
204 efitick_t turnOffTimeStage2 = startTime + US2NT((int)durationUsStage2);
205 getScheduler()->schedule("inj stage 2", nullptr, turnOffTimeStage2, endActionStage2);
206 }
207
208#if EFI_DETAILED_LOGGING
209 printf("scheduling injection angle=%.2f/delay=%d injectionDuration=%d %d\r\n", angleFromNow, (int)NT2US(startTime - nowNt), (int)durationUsStage1, (int)durationUsStage2);
210#endif
211#if EFI_DETAILED_LOGGING
212 efiPrintf("handleFuel pin=%s eventIndex %d duration=%.2fms %d", outputs[0]->name,
213 injEventIndex,
214 injectionDurationStage1,
215 getRevolutionCounter());
216 efiPrintf("handleFuel pin=%s delay=%.2f %d", outputs[0]->name, NT2US(startTime - nowNt),
217 getRevolutionCounter());
218#endif /* EFI_DETAILED_LOGGING */
219}
TunerStudioOutputChannels outputChannels
Definition engine.h:109
constexpr auto & module()
Definition engine.h:200
virtual bool isCranking() const =0
angle_t engineCycle
float injectionStage2Fraction
InjectorOutputPin * outputs[MAX_WIRES_COUNT]
float injectionStartAngle
const char * getName() const
Definition efi_gpio.cpp:422
static float getOrZero(SensorType type)
Definition sensor.h:83
float adjust(float desiredMassGrams)
Definition wall_fuel.cpp:14
bool isPhaseInRange(float test, float current, float next)
Definition efilib.cpp:176
Scheduler * getScheduler()
Definition engine.cpp:585
static EngineAccessor engine
Definition engine.h:413
static constexpr engine_configuration_s * engineConfiguration
floatms_t getCrankshaftRevolutionTimeMs(float rpm)
bool warning(ObdCode code, const char *fmt,...)
int getNumberOfInjections(injection_mode_e mode)
bool printFuelDebug
@ CUSTOM_OBD_NEG_INJECTION
@ CUSTOM_OBD_NAN_INJECTION
efitick_t scheduleByAngle(scheduling_s *timer, efitick_t nowNt, angle_t angle, action_s const &action)
float floatms_t
virtual void schedule(const char *msg, scheduling_s *scheduling, efitick_t targetTime, action_s const &action)=0
Schedule an action to be executed in the future.
float injectionMass[MAX_CYLINDER_COUNT]
scaled_channel< uint16_t, 300, 1 > actualLastInjectionStage2
scaled_channel< uint16_t, 300, 1 > actualLastInjection
printf("\n")

Referenced by FuelSchedule::onTriggerTooth().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ setIndex()

void InjectionEvent::setIndex ( uint8_t  index)
inline

Definition at line 27 of file fuel_schedule.h.

27 {
28 ownIndex = index;
29 }

Referenced by FuelSchedule::FuelSchedule().

Here is the caller graph for this function:

◆ update()

bool InjectionEvent::update ( )
Returns
false in case of error, true if success

also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires

Definition at line 142 of file fuel_schedule.cpp.

142 {
143 bool updatedAngle = updateInjectionAngle();
144
145 if (!updatedAngle) {
146 return false;
147 }
148
150 engine->outputChannels.currentInjectionMode = static_cast<uint8_t>(mode);
151
152 int injectorIndex;
153 if (mode == IM_SIMULTANEOUS || mode == IM_SINGLE_POINT) {
154 // These modes only have one injector
155 injectorIndex = 0;
156 } else if (mode == IM_SEQUENTIAL || mode == IM_BATCH) {
157 // Map order index -> cylinder index (firing order)
158 injectorIndex = getCylinderNumberAtIndex(ownIndex);
159 } else {
160 firmwareError(ObdCode::CUSTOM_OBD_UNEXPECTED_INJECTION_MODE, "Unexpected injection mode %d", mode);
161 injectorIndex = 0;
162 }
163
164 InjectorOutputPin *secondOutput;
165 InjectorOutputPin* secondOutputStage2;
166
167 if (mode == IM_BATCH) {
168 /**
169 * also fire the 2nd half of the injectors so that we can implement a batch mode on individual wires
170 */
171 // Compute the position of this cylinder's twin in the firing order
172 // Each injector gets fired as a primary (the same as sequential), but also
173 // fires the injector 360 degrees later in the firing order.
175 int secondIndex = getCylinderNumberAtIndex(secondOrder);
176 secondOutput = &enginePins.injectors[secondIndex];
177 secondOutputStage2 = &enginePins.injectorsStage2[secondIndex];
178 } else {
179 secondOutput = nullptr;
180 secondOutputStage2 = nullptr;
181 }
182
183 InjectorOutputPin *output = &enginePins.injectors[injectorIndex];
184
185 outputs[0] = output;
186 outputs[1] = secondOutput;
187 isSimultaneous = mode == IM_SIMULTANEOUS;
188 // Stash the cylinder number so we can select the correct fueling bank later
189 cylinderNumber = injectorIndex;
190
191 outputsStage2[0] = &enginePins.injectorsStage2[injectorIndex];
192 outputsStage2[1] = secondOutputStage2;
193
194 if (!isSimultaneous && !output->isInitialized()) {
195 // todo: extract method for this index math
197 }
198
199 return true;
200}
InjectorOutputPin injectorsStage2[MAX_CYLINDER_COUNT]
Definition efi_gpio.h:128
InjectorOutputPin injectors[MAX_CYLINDER_COUNT]
Definition efi_gpio.h:127
InjectorOutputPin * outputsStage2[MAX_WIRES_COUNT]
bool updateInjectionAngle()
bool isInitialized() const
Definition efi_gpio.cpp:559
EnginePins enginePins
Definition efi_gpio.cpp:24
injection_mode_e getCurrentInjectionMode()
Definition engine.cpp:548
void firmwareError(ObdCode code, const char *fmt,...)
size_t getCylinderNumberAtIndex(size_t cylinderIndex)
@ CUSTOM_OBD_INJECTION_NO_PIN_ASSIGNED
@ CUSTOM_OBD_UNEXPECTED_INJECTION_MODE
injection_mode_e

Referenced by FuelSchedule::addFuelEvents().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ updateInjectionAngle()

bool InjectionEvent::updateInjectionAngle ( )
private

Definition at line 123 of file fuel_schedule.cpp.

123 {
124 auto result = computeInjectionAngle();
125
126 if (result) {
127 // If injector duty cycle is high, lock injection SOI so that we
128 // don't miss injections at or above 100% duty
129 if (getEngineState()->shouldUpdateInjectionTiming) {
130 injectionStartAngle = result.Value;
131 }
132
133 return true;
134 } else {
135 return false;
136 }
137}
expected< float > computeInjectionAngle() const

Referenced by update().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ cylinderNumber

uint8_t InjectionEvent::cylinderNumber = 0
private

Definition at line 44 of file fuel_schedule.h.

Referenced by computeInjectionAngle(), onTriggerTooth(), and update().

◆ injectionStartAngle

float InjectionEvent::injectionStartAngle = 0

Definition at line 52 of file fuel_schedule.h.

Referenced by onTriggerTooth(), and updateInjectionAngle().

◆ isSimultaneous

bool InjectionEvent::isSimultaneous = false
private

This is a performance optimization for IM_SIMULTANEOUS fuel strategy. It's more efficient to handle all injectors together if that's the case

Definition at line 42 of file fuel_schedule.h.

Referenced by onTriggerTooth(), and update().

◆ outputs

InjectorOutputPin* InjectionEvent::outputs[MAX_WIRES_COUNT] {}

Definition at line 50 of file fuel_schedule.h.

50{};

Referenced by onTriggerTooth(), turnInjectionPinLow(), and update().

◆ outputsStage2

InjectorOutputPin* InjectionEvent::outputsStage2[MAX_WIRES_COUNT] {}

Definition at line 51 of file fuel_schedule.h.

51{};

Referenced by turnInjectionPinLowStage2(), and update().

◆ ownIndex

uint8_t InjectionEvent::ownIndex = 0
private

Definition at line 43 of file fuel_schedule.h.

Referenced by computeInjectionAngle(), setIndex(), and update().

◆ wallFuel

WallFuel InjectionEvent::wallFuel {}
private

Definition at line 46 of file fuel_schedule.h.

46{};

Referenced by getWallFuel(), and onTriggerTooth().


The documentation for this class was generated from the following files: