rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
injector_model.cpp
Go to the documentation of this file.
1#include "pch.h"
2
3#include "injector_model.h"
4#include "fuel_computer.h"
5
7 float flowRatio = getInjectorFlowRatio();
8
9 // "large pulse" flow rate
10 m_massFlowRate = flowRatio * getBaseFlowRate();
12
13 if (getNonlinearMode() == INJ_FordModel) {
16
17 // amount added to small pulses to correct for the "kink" from low flow region
19 }
20}
21
22constexpr float convertToGramsPerSecond(float ccPerMinute) {
23 return ccPerMinute * (fuelDensity / 60.f);
24}
25
26// returns: grams per second flow
34
38
40 // convert milligrams -> grams
41 // todo: make UI deal with scaling?!
43}
44
48
52
56
58 // not supported on second bank
59 return 0;
60}
61
63 // not supported on second bank
64 return 0;
65}
66
70
74
76 // nonlinear not supported on second bank
77 return InjectorNonlinearMode::INJ_None;
78}
79
81 // TODO: remove at the end of 2025
82 // hack not to break tunes before 2779925f54f5c2d23499cbb2797f71508c652f54 "injector lag lookup should be done based on differential pressure"
85 } else {
87 }
88}
89
91 return getFuelDifferentialPressure().Value + Sensor::get(SensorType::Map).value_or(STD_ATMOSPHERE);
92}
93
95 auto map = Sensor::get(SensorType::Map);
97
98 float baroKpa = baro.Value;
99 // todo: extract baro sensor validation logic
100 if (!baro || baro.Value > 120 || baro.Value < 50) {
101 baroKpa = STD_ATMOSPHERE;
102 }
103
104 switch (getInjectorCompensationMode()) {
105 case ICM_FixedRailPressure:
106 // Add barometric pressure, as "fixed" really means "fixed pressure above atmosphere"
108 + baroKpa
109 - map.value_or(STD_ATMOSPHERE);
110 case ICM_SensedRailPressure: {
112 warning(ObdCode::OBD_Fuel_Pressure_Sensor_Missing, "Fuel pressure compensation is set to use a pressure sensor, but none is configured.");
113 return unexpected;
114 }
115
117
118 // TODO: what happens when the sensor fails?
119 if (!fps) {
120 return unexpected;
121 }
122
124 case FPM_Differential:
125 // This sensor directly measures delta-P, no math needed!
126 return fps.Value;
127 case FPM_Gauge:
128 if (!map) {
129 return unexpected;
130 }
131
132 return fps.Value + baroKpa - map.Value;
133 case FPM_Absolute:
134 default:
135 if (!map) {
136 return unexpected;
137 }
138
139 return fps.Value - map.Value;
140 }
141 } default: return unexpected;
142 }
143}
144
146 // Compensation disabled, use reference flow.
147 auto compensationMode = getInjectorCompensationMode();
148 if (compensationMode == ICM_None || compensationMode == ICM_HPFP_Manual_Compensation) {
149 return 1.0f;
150 }
151
152 const float referencePressure = getFuelReferencePressure();
153
154 if (referencePressure < 50) {
155 // impossibly low fuel ref pressure
156 criticalError("Impossible fuel reference pressure: %f", referencePressure);
157
158 return 1.0f;
159 }
160
161 expected<float> diffPressure = getFuelDifferentialPressure();
162
163 // If sensor failed, best we can do is disable correction
164 if (!diffPressure) {
165 return 1.0f;
166 }
167
168 pressureDelta = diffPressure.Value;
169
170 // Somehow pressure delta is less than 0, assume failed sensor and return default flow
171 if (pressureDelta <= 0) {
172 return 1.0f;
173 }
174
175 pressureRatio = pressureDelta / referencePressure;
176 // todo: live data model?
177 float flowRatio = sqrtf(pressureRatio);
178
179 // TODO: should the flow ratio be clamped?
180 return flowRatio;
181}
182
190
191//TODO: only used in the tests, refactor pending to InjectorModelWithConfig
193 if (fuelMassGram <= 0) {
194 // If 0 mass, don't do any math, just skip the injection.
195 return 0.0f;
196 }
197
198 // Get the no-offset duration
199 floatms_t baseDuration = getBaseDurationImpl(fuelMassGram);
200
201 return baseDuration + m_deadtime;
202}
203
205 if (fuelMassGram <= 0) {
206 // If 0 mass, don't do any math, just skip the injection.
207 return 0.0f;
208 }
209
210 // hopefully one day we pick between useInjectorFlowLinearizationTable and ICM_HPFP_Manual_Compensation approaches
211 // and not more than one of these would stay
214 // todo: KPA vs BAR mess?!
215 return interpolate3d(config->injectorFlowLinearization,
216 config->injectorFlowLinearizationPressureBins, KPA2BAR(fps.Value),// array values are on bar
217 config->injectorFlowLinearizationFuelMassBins, fuelMassGram * 1000); // array values are on mg
218 }
219
220 // Get the no-offset duration
221 floatms_t baseDuration = getBaseDurationImpl(fuelMassGram);
222
223 // default non GDI case
224 if (getInjectorCompensationMode() != ICM_HPFP_Manual_Compensation) {
225 // Add deadtime offset
226 return baseDuration + m_deadtime;
227 }
228
230 return baseDuration + m_deadtime;
231 }
232
234 // todo: KPA vs BAR mess in code and UI?!
235 float fuelMassCompensation = interpolate3d(config->hpfpFuelMassCompensation,
236 config->hpfpFuelMassCompensationFuelPressure, KPA2BAR(fps.Value),// array values are on bar
237 config->hpfpFuelMassCompensationFuelMass, fuelMassGram * 1000); // array values are on mg
238
239 // recalculate base duration with fuel mass compensation
240 baseDuration = getBaseDurationImpl(fuelMassGram * fuelMassCompensation);
241 return baseDuration + m_deadtime;
242}
243
245 // Convert from ms -> grams
246 return duration * m_massFlowRate * 0.001f;
247}
248
249// todo: all that *1000 and *0.001f is pretty annoying, we need a cleaner approach for units!
251 floatms_t baseDuration = fuelMassGram / m_massFlowRate * 1000;
252
253 switch (getNonlinearMode()) {
254 case INJ_FordModel:
255 if (fuelMassGram < m_smallPulseBreakPoint) {
256 // Small pulse uses a different slope, and adds the "zero fuel pulse" offset
257 return (fuelMassGram / m_smallPulseFlowRate * 1000) + m_smallPulseOffset;
258 } else {
259 // Large pulse
260 return baseDuration;
261 }
262 case INJ_PolynomialAdder:
263 return correctInjectionPolynomial(baseDuration);
264 case INJ_None:
265 default:
266 return baseDuration;
267 }
268}
269
271 if (baseDuration > engineConfiguration->applyNonlinearBelowPulse) {
272 // Large pulse, skip correction.
273 return baseDuration;
274 }
275
277 float xi = 1;
278
279 float adder = 0;
280
281 // Add polynomial terms, starting with x^0
282 for (size_t i = 0; i < efi::size(is); i++) {
283 adder += is[i] * xi;
284 xi *= baseDuration;
285 }
286
287 return baseDuration + adder;
288}
289
291 : m_cfg(cfg)
292{
293}
294
299
300// TODO: actual separate config for second bank!
floatms_t getBaseDurationImpl(float fuelMassGram) const
float getFuelMassForDuration(floatms_t duration) const override
virtual float getInjectorFlowRatio()=0
virtual float getBaseFlowRate() const =0
virtual float getSmallPulseFlowRate() const =0
virtual floatms_t correctInjectionPolynomial(float floatms_t) const
virtual float getSmallPulseBreakPoint() const =0
void prepare() override
virtual InjectorNonlinearMode getNonlinearMode() const =0
floatms_t getInjectionDuration(float fuelMassGram) const override
expected< float > getFuelPressure() const override
InjectorModelWithConfig(const injector_s *const cfg)
void updateState() override
const injector_s *const m_cfg
virtual float getFuelReferencePressure() const =0
virtual injector_compensation_mode_e getInjectorCompensationMode() const =0
float getBaseFlowRate() const override
float getInjectorFlowRatio() override
floatms_t getInjectionDuration(float fuelMassGram) const override
expected< float > getFuelDifferentialPressure() const override
floatms_t getDeadtime() const override
virtual bool hasSensor() const
Definition sensor.h:141
virtual SensorResult get() const =0
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
bool warning(ObdCode code, const char *fmt,...)
constexpr float fuelDensity
constexpr float convertToGramsPerSecond(float ccPerMinute)
@ OBD_Fuel_Pressure_Sensor_Missing
InjectorNonlinearMode
injector_compensation_mode_e
@ FuelPressureInjector
@ BarometricPressure
virtual floatms_t getDeadtime() const =0
float getSmallPulseBreakPoint() const override
float getSmallPulseFlowRate() const override
float getFuelReferencePressure() const final
InjectorNonlinearMode getNonlinearMode() const override
injector_compensation_mode_e getInjectorCompensationMode() const final
InjectorNonlinearMode getNonlinearMode() const override
float getFuelReferencePressure() const final
float getSmallPulseFlowRate() const override
float getSmallPulseBreakPoint() const override
injector_compensation_mode_e getInjectorCompensationMode() const final
scaled_channel< uint16_t, 1000, 1 > fordInjectorSmallPulseBreakPoint
scaled_channel< int16_t, 100, 1 > battLagCorrBattBins[VBAT_INJECTOR_CURVE_SIZE]
scaled_channel< uint32_t, 10, 1 > battLagCorrPressBins[VBAT_INJECTOR_CURVE_PRESSURE_SIZE]
scaled_channel< int16_t, 100, 1 > battLagCorrTable[VBAT_INJECTOR_CURVE_PRESSURE_SIZE][VBAT_INJECTOR_CURVE_SIZE]
scaled_channel< uint16_t, 10, 1 > injectorFlowLinearizationPressureBins[FLOW_LINEARIZATION_PRESSURE_SIZE]
scaled_channel< uint16_t, 10, 1 > hpfpFuelMassCompensationFuelPressure[HPFP_FUEL_MASS_COMPENSATION_SIZE]
scaled_channel< uint16_t, 100, 1 > injectorFlowLinearization[FLOW_LINEARIZATION_PRESSURE_SIZE][FLOW_LINEARIZATION_MASS_SIZE]
scaled_channel< uint16_t, 100, 1 > injectorFlowLinearizationFuelMassBins[FLOW_LINEARIZATION_MASS_SIZE]
scaled_channel< uint16_t, 100, 1 > hpfpFuelMassCompensationFuelMass[HPFP_FUEL_MASS_COMPENSATION_SIZE]
scaled_channel< uint16_t, 100, 1 > hpfpFuelMassCompensation[HPFP_FUEL_MASS_COMPENSATION_SIZE][HPFP_FUEL_MASS_COMPENSATION_SIZE]