rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
ignition_state.cpp
Go to the documentation of this file.
1/**
2 * @file ignition_state.cpp
3 *
4 * @date Mar 27, 2013
5 * @author Andrey Belomutskiy, (c) 2012-2020
6 *
7 * This file is part of rusEfi - see http://rusefi.com
8 *
9 * rusEfi is free software; you can redistribute it and/or modify it under the terms of
10 * the GNU General Public License as published by the Free Software Foundation; either
11 * version 3 of the License, or (at your option) any later version.
12 *
13 * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
14 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program.
18 * If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "pch.h"
22
23#include "idle_thread.h"
24#include "launch_control.h"
25#include "gppwm_channel.h"
26
27#if EFI_ENGINE_CONTROL
28
29// TODO: wow move this into engineState at least for context not to leak from test to test!
30// todo: reset this between cranking attempts?! #2735
32
35
36#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
37
38/**
39 * @return ignition timing angle advance before TDC
40 */
41angle_t getRunningAdvance(float rpm, float engineLoad) {
42 if (std::isnan(engineLoad)) {
43 warning(ObdCode::CUSTOM_NAN_ENGINE_LOAD, "NaN engine load");
44 return NAN;
45 }
46
47 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(engineLoad), "invalid el", NAN);
48
49 // compute base ignition angle from main table
50 float advanceAngle = IgnitionState::getInterpolatedIgnitionAngle(rpm, engineLoad);
51
52 float vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed);
54 engine->ignitionState.tractionAdvanceDrop = tcTimingDropTable.getValue(wheelSlip, vehicleSpeed);
55 engine->engineState.tractionControlSparkSkip = tcSparkSkipTable.getValue(wheelSlip, vehicleSpeed);
57
59
60#if EFI_ANTILAG_SYSTEM
65 config->alsIgnRetardLoadBins, throttleIntent,
67 );
69 }
70#endif /* EFI_ANTILAG_SYSTEM */
71
72 // Add any adjustments if configured
73 for (size_t i = 0; i < efi::size(config->ignBlends); i++) {
74 auto result = calculateBlend(config->ignBlends[i], rpm, engineLoad);
75
76 engine->outputChannels.ignBlendParameter[i] = result.BlendParameter;
77 engine->outputChannels.ignBlendBias[i] = result.Bias;
78 engine->outputChannels.ignBlendOutput[i] = result.Value;
79 engine->outputChannels.ignBlendYAxis[i] = result.TableYAxis;
80
81 advanceAngle += result.Value;
82 }
83
84 // get advance from the separate table for Idle
85#if EFI_IDLE_CONTROL
88 float idleAdvance = interpolate2d(rpm, config->idleAdvanceBins, config->idleAdvance);
89
91 if (tps) {
92 // interpolate between idle table and normal (running) table using TPS threshold
93 // 0 TPS -> idle table
94 // 1/2 threshold -> idle table
95 // idle threshold -> normal table
97 advanceAngle = interpolateClamped(idleThreshold / 2, idleAdvance, idleThreshold, advanceAngle, tps.Value);
98 }
99 }
100#endif
101
102#if EFI_IDLE_CONTROL
103 // reset ignition table dot, see #8198
108 } else {
112 }
113#endif
114
115#if EFI_LAUNCH_CONTROL
117 const float launchAngle = engineConfiguration->launchTimingRetard;
119 const int launchRpm = engineConfiguration->launchRpm;
120 const int smoothRetardStartRpm = (launchRpm - engineConfiguration->launchRpmWindow);
121 const int smoothRetardEndRpm = (launchRpm - engineConfiguration->launchCorrectionsEndRpm);
122 if (smoothRetardStartRpm <= rpm) {
123 if (engineConfiguration->launchSmoothRetard && (rpm <= smoothRetardEndRpm)) {
124 // https://github.com/rusefi/rusefi/issues/5611#issuecomment-2130431696
125 return interpolateClamped(smoothRetardStartRpm, advanceAngle, smoothRetardEndRpm, launchAngle, rpm);
126 } else {
127 return launchAngle;
128 }
129 }
131 return launchAngle;
132 }
133 }
136 ) {
138 }
141 }
142#endif /* EFI_LAUNCH_CONTROL */
143
144#ifdef MODULE_VVL_CONTROLLER
145 advanceAngle += engine->module<VvlController>().unmock().getTimingModifier();
146#endif /* MODULE_VVL_CONTROLLER */
147
148 return advanceAngle;
149}
150
152 const auto clt = Sensor::get(SensorType::Clt);
153
154 if (!clt)
155 return 0; // this error should be already reported somewhere else, let's just handle it
156
157 return interpolate3d(
159 config->ignitionCltCorrLoadBins, engineLoad,
161 );
162}
163
167
170
171 if (!iat) {
173 } else {
176 config->ignitionIatCorrLoadBins, engineLoad,
178 );
179 }
180
181#if EFI_IDLE_CONTROL
183
185#endif // EFI_IDLE_CONTROL
186
188
189#if EFI_TUNER_STUDIO
191#endif /* EFI_TUNER_STUDIO */
192
197}
198
199/**
200 * @return ignition timing angle advance before TDC for Cranking
201 */
202angle_t getCrankingAdvance(float rpm, float engineLoad) {
203 // get advance from the separate table for Cranking
205 return interpolate2d(rpm, config->crankingAdvanceBins, config->crankingAdvance);
206 }
207
208 // Interpolate the cranking timing angle to the earlier running angle for faster engine start
209 angle_t crankingToRunningTransitionAngle = getRunningAdvance(engineConfiguration->cranking.rpm, engineLoad);
210 // interpolate not from zero, but starting from min. possible rpm detected
211 if (rpm < minCrankingRpm || minCrankingRpm == 0)
212 minCrankingRpm = rpm;
214}
215#endif // EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
216
217angle_t IgnitionState::getAdvance(float rpm, float engineLoad) {
218 if (std::isnan(engineLoad)) {
219 return 0; // any error should already be reported
220 }
221 if (engineConfiguration->timingMode == TM_FIXED) {
222 // fixed timing is the simple: cranking/running does not matter, no corrections!
224 }
225
226 angle_t angle;
227
228 bool isCranking = engine->rpmCalculator.isCranking();
229 if (isCranking) {
230 angle = getCrankingAdvance(rpm, engineLoad);
231 assertAngleRange(angle, "crAngle", ObdCode::CUSTOM_ERR_ANGLE_CR);
232 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "cr_AngleN", 0);
233 } else {
234 angle = getRunningAdvance(rpm, engineLoad);
235
236 if (std::isnan(angle)) {
237 warning(ObdCode::CUSTOM_ERR_6610, "NaN angle from table");
238 return 0;
239 }
240 }
241
242 // Allow if we're either not cranking OR allowed to correct in cranking
243 bool allowCorrections = !isCranking || engineConfiguration->useAdvanceCorrectionsForCranking;
244
245 if (allowCorrections) {
246 angle_t correction = getAdvanceCorrections(engineLoad);
247 if (!std::isnan(correction)) { // correction could be NaN during settings update
248 angle += correction;
249 }
250 }
251
252 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "_AngleN5", 0);
253 return angle;
254}
255
256angle_t IgnitionState::getWrappedAdvance(const float rpm, const float engineLoad) {
257 angle_t angle = getAdvance(rpm, engineLoad) * luaTimingMult + luaTimingAdd;
258 wrapAngle(angle, "getWrappedAdvance", ObdCode::CUSTOM_ERR_ADCANCE_CALC_ANGLE);
259 return angle;
260}
261
262PUBLIC_API_WEAK_SOMETHING_WEIRD
263angle_t getCylinderIgnitionTrim(size_t cylinderNumber, float rpm, float ignitionLoad) {
264 return IgnitionState::getInterpolatedIgnitionTrim(cylinderNumber, rpm, ignitionLoad);
265}
266
267size_t getMultiSparkCount(float rpm) {
268 // Compute multispark (if enabled)
270 && rpm <= engineConfiguration->multisparkMaxRpm
272 // For zero RPM, disable multispark. We don't yet know the engine speed, so multispark may not be safe.
273 if (rpm == 0) {
274 return 0;
275 }
276
278 floatus_t multiDwell = 1000.0f * engineConfiguration->multisparkDwell;
279
280 // dwell times are below 10 seconds here so we use 32 bit type for performance reasons
281 engine->engineState.multispark.delay = (uint32_t)USF2NT(multiDelay);
282 engine->engineState.multispark.dwell = (uint32_t)USF2NT(multiDwell);
283
284 constexpr float usPerDegreeAt1Rpm = 60e6 / 360;
285 floatus_t usPerDegree = usPerDegreeAt1Rpm / rpm;
286
287 // How long is there for sparks? The user configured an angle, convert to time.
288 floatus_t additionalSparksUs = usPerDegree * engineConfiguration->multisparkMaxSparkingAngle;
289 // How long does one spark take?
290 floatus_t oneSparkTime = multiDelay + multiDwell;
291
292 // How many sparks can we fit in the alloted time?
293 float sparksFitInTime = additionalSparksUs / oneSparkTime;
294
295 // Take the floor (convert to uint8_t) - we want to undershoot, not overshoot
296 uint32_t floored = sparksFitInTime;
297
298 // Allow no more than the maximum number of extra sparks
300 } else {
301 return 0;
302 }
303}
304
309
310/**
311 * @return Spark dwell time, in milliseconds. 0 if tables are not ready.
312 */
313floatms_t IgnitionState::getSparkDwell(float rpm, bool isCranking) {
314 float dwellMs;
315 if (isCranking) {
317 } else {
318 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(rpm), "invalid rpm", NAN);
319
321 dwellVoltageCorrection = interpolate2d(
325 );
326
327 // for compat (table full of zeroes)
328 if (dwellVoltageCorrection < 0.1f) {
330 }
331
333 }
334
335 if (std::isnan(dwellMs) || dwellMs <= 0) {
336 // this could happen during engine configuration reset
337 warning(ObdCode::CUSTOM_ERR_DWELL_DURATION, "invalid dwell: %.2f at rpm=%.0f", dwellMs, rpm);
338 return 0;
339 }
340 return dwellMs;
341}
342
343void IgnitionState::updateDwell(float rpm, bool isCranking) {
344 sparkDwell = getSparkDwell(rpm, isCranking);
345 dwellDurationAngle = std::isnan(rpm) ? NAN : getDwell() / getOneDegreeTimeMs(rpm);
346}
347
351
352 angle_t IgnitionState::getTrailingSparkAngle(const float rpm, const float engineLoad){
353 if (std::isnan(engineLoad)) {
354 // default value from: https://github.com/rusefi/rusefi/commit/86683afca22ed1a8af8fd5ac9231442e2124646e#diff-6e80cdd8c55add68105618ad9e8954170a47f59814201dadd2b888509d6b2e39R176
355 return 10;
356 }
357 return interpolate3d(
359 config->trailingSparkLoadBins, engineLoad,
361 );
362}
363
365 // time => degree
367
368 if (!std::isnan(correction)) {
369 return correction;
370 }
371 return 0;
372}
373
375 return interpolate3d(
379 );
380}
381
383 const size_t cylinderNumber,
384 const float rpm,
385 const float ignitionLoad
386) {
387 return interpolate3d(
388 config->ignTrims[cylinderNumber].table,
391 );
392}
393
394#endif // EFI_ENGINE_CONTROL
angle_t getAdvanceCorrections(float engineLoad)
angle_t getCrankingAdvance(float rpm, float engineLoad)
angle_t getRunningAdvance(float rpm, float engineLoad)
TriggerCentral triggerCentral
Definition engine.h:318
IgnitionState ignitionState
Definition engine.h:239
LaunchControlBase launchController
Definition engine.h:220
EngineState engineState
Definition engine.h:344
RpmCalculator rpmCalculator
Definition engine.h:306
AntilagSystemBase antilagController
Definition engine.h:228
TunerStudioOutputChannels outputChannels
Definition engine.h:109
constexpr auto & module()
Definition engine.h:200
ShiftTorqueReductionController shiftTorqueReductionController
Definition engine.h:221
void updateSparkSkip()
Definition engine2.cpp:137
multispark_state multispark
bool isIdlingOrTaper() const override
Definition idle_thread.h:88
bool isCoastingAdvance() const override
Definition idle_thread.h:92
void updateAdvanceCorrections(float engineLoad)
angle_t getTrailingSparkAngle(const float rpm, const float engineLoad)
angle_t getAdvance(float rpm, float engineLoad)
angle_t getWrappedAdvance(const float rpm, const float engineLoad)
static angle_t getInterpolatedIgnitionTrim(size_t cylinderNumber, float rpm, float ignitionLoad)
static angle_t getInterpolatedIgnitionAngle(float rpm, float ignitionLoad)
angle_t getSparkHardwareLatencyCorrection()
floatms_t getSparkDwell(float rpm, bool isCranking)
floatms_t getDwell() const
void updateDwell(float rpm, bool isCranking)
floatus_t oneDegreeUs
bool isCranking() const override
virtual SensorResult get() const =0
static float getOrZero(SensorType type)
Definition sensor.h:83
InstantRpmCalculator instantRpm
float getTimingModifier() const
float interpolateClamped(float x1, float y1, float x2, float y2, float x)
static EngineAccessor engine
Definition engine.h:413
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
BlendResult calculateBlend(blend_table_s &cfg, float rpm, float load)
bool warning(ObdCode code, const char *fmt,...)
angle_t getCltTimingCorrection()
Idle Valve Control thread.
angle_t getAdvanceCorrections(float engineLoad)
angle_t getCrankingAdvance(float rpm, float engineLoad)
void initIgnitionAdvanceControl()
PUBLIC_API_WEAK_SOMETHING_WEIRD angle_t getCylinderIgnitionTrim(size_t cylinderNumber, float rpm, float ignitionLoad)
angle_t getRunningAdvance(float rpm, float engineLoad)
size_t getMultiSparkCount(float rpm)
static Map3D< TRACTION_CONTROL_ETB_DROP_SLIP_SIZE, TRACTION_CONTROL_ETB_DROP_SPEED_SIZE, int8_t, uint16_t, uint8_t > tcTimingDropTable
static Map3D< TRACTION_CONTROL_ETB_DROP_SLIP_SIZE, TRACTION_CONTROL_ETB_DROP_SPEED_SIZE, int8_t, uint16_t, uint8_t > tcSparkSkipTable
float minCrankingRpm
static CCM_OPTIONAL FunctionalSensor iat(SensorType::Iat, MS2NT(10))
static CCM_OPTIONAL FunctionalSensor clt(SensorType::Clt, MS2NT(10))
@ CUSTOM_ERR_DWELL_DURATION
@ CUSTOM_ERR_ADCANCE_CALC_ANGLE
@ CUSTOM_ERR_6610
@ CUSTOM_NAN_ENGINE_LOAD
@ CUSTOM_ERR_ANGLE_CR
@ CUSTOM_ERR_ASSERT
float angle_t
@ DriverThrottleIntent
instantRpm("sync: instant RPM", SensorCategory.SENSOR_INPUTS, FieldType.INT16, 326, 1.0, 0.0, 0.0, "rpm")
ignitionLoad("Ignition: load", SensorCategory.SENSOR_INPUTS, FieldType.INT, 1412, 1.0, -1.0, -1.0, "")
scaled_channel< int16_t, 100, 1 > timingALSCorrection
uint8_t tractionControlSpeedBins[TRACTION_CONTROL_ETB_DROP_SPEED_SIZE]
int8_t tractionControlIgnitionSkip[TRACTION_CONTROL_ETB_DROP_SLIP_SIZE][TRACTION_CONTROL_ETB_DROP_SPEED_SIZE]
scaled_channel< uint16_t, 100, 1 > tractionControlSlipBins[TRACTION_CONTROL_ETB_DROP_SLIP_SIZE]
int8_t tractionControlTimingDrop[TRACTION_CONTROL_ETB_DROP_SLIP_SIZE][TRACTION_CONTROL_ETB_DROP_SPEED_SIZE]
scaled_channel< int8_t, 5, 1 > table[IGN_TRIM_SIZE][IGN_TRIM_SIZE]
scaled_channel< int16_t, 100, 1 > dfcoTimingRetard
scaled_channel< int16_t, 100, 1 > timingPidCorrection
scaled_channel< int16_t, 100, 1 > cltTimingCorrection
scaled_channel< int16_t, 50, 1 > tractionAdvanceDrop
scaled_channel< int16_t, 100, 1 > timingIatCorrection
scaled_channel< int16_t, 100, 1 > ignBlendOutput[IGN_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > ignBlendParameter[IGN_BLEND_COUNT]
scaled_channel< uint8_t, 2, 1 > ignBlendBias[IGN_BLEND_COUNT]
scaled_channel< int16_t, 10, 1 > ignBlendYAxis[IGN_BLEND_COUNT]
scaled_channel< uint8_t, 10, 1 > dwellVoltageCorrVoltBins[DWELL_CURVE_SIZE]
scaled_channel< int16_t, 100, 1 > crankingAdvance[CRANKING_ADVANCE_CURVE_SIZE]
scaled_channel< int8_t, 10, 1 > trailingSparkTable[TRAILING_SPARK_SIZE][TRAILING_SPARK_SIZE]
scaled_channel< uint8_t, 1, 5 > trailingSparkLoadBins[TRAILING_SPARK_SIZE]
scaled_channel< uint8_t, 50, 1 > dwellVoltageCorrValues[DWELL_CURVE_SIZE]
scaled_channel< int16_t, 10, 1 > ignitionCltCorrTable[CLT_TIMING_CURVE_SIZE][CLT_TIMING_CURVE_SIZE]
scaled_channel< int16_t, 1, 1 > ignitionCltCorrTempBins[CLT_TIMING_CURVE_SIZE]
scaled_channel< uint8_t, 1, 50 > trailingSparkRpmBins[TRAILING_SPARK_SIZE]
scaled_channel< uint8_t, 1, 50 > idleAdvanceBins[IDLE_ADVANCE_CURVE_SIZE]
scaled_channel< int16_t, 10, 1 > ignitionIatCorrTable[IAT_IGN_CORR_LOAD_COUNT][IAT_IGN_CORR_COUNT]
scaled_channel< int16_t, 10, 1 > ignitionTable[IGN_LOAD_COUNT][IGN_RPM_COUNT]
scaled_channel< uint8_t, 1, 5 > ignitionIatCorrLoadBins[IAT_IGN_CORR_LOAD_COUNT]
uint16_t crankingAdvanceBins[CRANKING_ADVANCE_CURVE_SIZE]
scaled_channel< uint16_t, 100, 1 > sparkDwellValues[DWELL_CURVE_SIZE]
scaled_channel< int16_t, 10, 1 > ALSTimingRetardTable[ALS_SIZE][ALS_SIZE]
scaled_channel< uint8_t, 1, 5 > ignitionCltCorrLoadBins[CLT_TIMING_CURVE_SIZE]
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)