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
31
32#if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
33
34/**
35 * @return ignition timing angle advance before TDC
36 */
37angle_t getRunningAdvance(float rpm, float engineLoad) {
38 if (std::isnan(engineLoad)) {
39 warning(ObdCode::CUSTOM_NAN_ENGINE_LOAD, "NaN engine load");
40 return NAN;
41 }
42
43 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(engineLoad), "invalid el", NAN);
44
45 // compute base ignition angle from main table
46 float advanceAngle = IgnitionState::getInterpolatedIgnitionAngle(rpm, engineLoad);
47
48 float vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed);
50 engine->ignitionState.tractionAdvanceDrop = tcTimingDropTable.getValue(wheelSlip, vehicleSpeed);
51 engine->engineState.tractionControlSparkSkip = tcSparkSkipTable.getValue(wheelSlip, vehicleSpeed);
53
55
56#if EFI_ANTILAG_SYSTEM
61 config->alsIgnRetardLoadBins, throttleIntent,
63 );
65 }
66#endif /* EFI_ANTILAG_SYSTEM */
67
68 // Add any adjustments if configured
69 for (size_t i = 0; i < efi::size(config->ignBlends); i++) {
70 auto result = calculateBlend(config->ignBlends[i], rpm, engineLoad);
71
72 engine->outputChannels.ignBlendParameter[i] = result.BlendParameter;
73 engine->outputChannels.ignBlendBias[i] = result.Bias;
74 engine->outputChannels.ignBlendOutput[i] = result.Value;
75 engine->outputChannels.ignBlendYAxis[i] = result.TableYAxis;
76
77 advanceAngle += result.Value;
78 }
79
80 // get advance from the separate table for Idle
81#if EFI_IDLE_CONTROL
84 float idleAdvance = interpolate2d(rpm, config->idleAdvanceBins, config->idleAdvance);
85
87 if (tps) {
88 // interpolate between idle table and normal (running) table using TPS threshold
89 // 0 TPS -> idle table
90 // 1/2 threshold -> idle table
91 // idle threshold -> normal table
93 advanceAngle = interpolateClamped(idleThreshold / 2, idleAdvance, idleThreshold, advanceAngle, tps.Value);
94 }
95 }
96#endif
97
98#if EFI_IDLE_CONTROL
99 // reset ignition table dot, see #8198
104 } else {
108 }
109#endif
110
111#if EFI_LAUNCH_CONTROL
113 const float launchAngle = engineConfiguration->launchTimingRetard;
115 const int launchRpm = engineConfiguration->launchRpm;
116 const int smoothRetardStartRpm = (launchRpm - engineConfiguration->launchRpmWindow);
117 const int smoothRetardEndRpm = (launchRpm - engineConfiguration->launchCorrectionsEndRpm);
118 if (smoothRetardStartRpm <= rpm) {
119 if (engineConfiguration->launchSmoothRetard && (rpm <= smoothRetardEndRpm)) {
120 // https://github.com/rusefi/rusefi/issues/5611#issuecomment-2130431696
121 return interpolateClamped(smoothRetardStartRpm, advanceAngle, smoothRetardEndRpm, launchAngle, rpm);
122 } else {
123 return launchAngle;
124 }
125 }
127 return launchAngle;
128 }
129 }
132 ) {
134 }
137 }
138#endif /* EFI_LAUNCH_CONTROL */
139
140#ifdef MODULE_VVL_CONTROLLER
141 advanceAngle += engine->module<VvlController>().unmock().getTimingModifier();
142#endif /* MODULE_VVL_CONTROLLER */
143
144 return advanceAngle;
145}
146
148 const auto clt = Sensor::get(SensorType::Clt);
149
150 if (!clt)
151 return 0; // this error should be already reported somewhere else, let's just handle it
152
153 return interpolate3d(
155 config->ignitionCltCorrLoadBins, engineLoad,
157 );
158}
159
163
166
167 if (!iat) {
169 } else {
172 config->ignitionIatCorrLoadBins, engineLoad,
174 );
175 }
176
177#if EFI_IDLE_CONTROL
179
181#endif // EFI_IDLE_CONTROL
182
184
185#if EFI_TUNER_STUDIO
187#endif /* EFI_TUNER_STUDIO */
188
193}
194
195/**
196 * @return ignition timing angle advance before TDC for Cranking
197 */
198angle_t getCrankingAdvance(float rpm, float engineLoad) {
199 // get advance from the separate table for Cranking
201 return interpolate2d(rpm, config->crankingAdvanceBins, config->crankingAdvance);
202 }
203
204 // Interpolate the cranking timing angle to the earlier running angle for faster engine start
205 angle_t crankingToRunningTransitionAngle = getRunningAdvance(engineConfiguration->cranking.rpm, engineLoad);
206 // interpolate not from zero, but starting from min. possible rpm detected
208}
209#endif // EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT
210
211angle_t IgnitionState::getAdvance(float rpm, float engineLoad) {
212 if (std::isnan(engineLoad)) {
213 return 0; // any error should already be reported
214 }
215 if (engineConfiguration->timingMode == TM_FIXED) {
216 // fixed timing is the simple: cranking/running does not matter, no corrections!
218 }
219
220 angle_t angle;
221
222 bool isCranking = engine->rpmCalculator.isCranking();
223 if (isCranking) {
224 angle = getCrankingAdvance(rpm, engineLoad);
225 assertAngleRange(angle, "crAngle", ObdCode::CUSTOM_ERR_ANGLE_CR);
226 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "cr_AngleN", 0);
227 } else {
228 angle = getRunningAdvance(rpm, engineLoad);
229
230 if (std::isnan(angle)) {
231 warning(ObdCode::CUSTOM_ERR_6610, "NaN angle from table");
232 return 0;
233 }
234 }
235
236 // Allow if we're either not cranking OR allowed to correct in cranking
237 bool allowCorrections = !isCranking || engineConfiguration->useAdvanceCorrectionsForCranking;
238
239 if (allowCorrections) {
240 angle_t correction = getAdvanceCorrections(engineLoad);
241 if (!std::isnan(correction)) { // correction could be NaN during settings update
242 angle += correction;
243 }
244 }
245
246 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "_AngleN5", 0);
247 return angle;
248}
249
250angle_t IgnitionState::getWrappedAdvance(const float rpm, const float engineLoad) {
251 angle_t angle = getAdvance(rpm, engineLoad) * luaTimingMult + luaTimingAdd;
252 wrapAngle(angle, "getWrappedAdvance", ObdCode::CUSTOM_ERR_ADCANCE_CALC_ANGLE);
253 return angle;
254}
255
256PUBLIC_API_WEAK_SOMETHING_WEIRD
257angle_t getCylinderIgnitionTrim(size_t cylinderNumber, float rpm, float ignitionLoad) {
258 return IgnitionState::getInterpolatedIgnitionTrim(cylinderNumber, rpm, ignitionLoad);
259}
260
261size_t getMultiSparkCount(float rpm) {
262 // Compute multispark (if enabled)
264 && rpm <= engineConfiguration->multisparkMaxRpm
266 // For zero RPM, disable multispark. We don't yet know the engine speed, so multispark may not be safe.
267 if (rpm == 0) {
268 return 0;
269 }
270
272 floatus_t multiDwell = 1000.0f * engineConfiguration->multisparkDwell;
273
274 // dwell times are below 10 seconds here so we use 32 bit type for performance reasons
275 engine->engineState.multispark.delay = (uint32_t)USF2NT(multiDelay);
276 engine->engineState.multispark.dwell = (uint32_t)USF2NT(multiDwell);
277
278 constexpr float usPerDegreeAt1Rpm = 60e6 / 360;
279 floatus_t usPerDegree = usPerDegreeAt1Rpm / rpm;
280
281 // How long is there for sparks? The user configured an angle, convert to time.
282 floatus_t additionalSparksUs = usPerDegree * engineConfiguration->multisparkMaxSparkingAngle;
283 // How long does one spark take?
284 floatus_t oneSparkTime = multiDelay + multiDwell;
285
286 // How many sparks can we fit in the alloted time?
287 float sparksFitInTime = additionalSparksUs / oneSparkTime;
288
289 // Take the floor (convert to uint8_t) - we want to undershoot, not overshoot
290 uint32_t floored = sparksFitInTime;
291
292 // Allow no more than the maximum number of extra sparks
294 } else {
295 return 0;
296 }
297}
298
303
304/**
305 * @return Spark dwell time, in milliseconds. 0 if tables are not ready.
306 */
307floatms_t IgnitionState::getSparkDwell(float rpm, bool isCranking) {
308 float dwellMs;
309 if (isCranking) {
311 } else {
312 efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(rpm), "invalid rpm", NAN);
313
315 dwellVoltageCorrection = interpolate2d(
319 );
320
321 // for compat (table full of zeroes)
322 if (dwellVoltageCorrection < 0.1f) {
324 }
325
327 }
328
329 if (std::isnan(dwellMs) || dwellMs <= 0) {
330 // this could happen during engine configuration reset
331 warning(ObdCode::CUSTOM_ERR_DWELL_DURATION, "invalid dwell: %.2f at rpm=%.0f", dwellMs, rpm);
332 return 0;
333 }
334 return dwellMs;
335}
336
337void IgnitionState::updateDwell(float rpm, bool isCranking) {
338 sparkDwell = getSparkDwell(rpm, isCranking);
339 dwellDurationAngle = std::isnan(rpm) ? NAN : getDwell() / getOneDegreeTimeMs(rpm);
340}
341
345
346 angle_t IgnitionState::getTrailingSparkAngle(const float rpm, const float engineLoad){
347 if (std::isnan(engineLoad)) {
348 // default value from: https://github.com/rusefi/rusefi/commit/86683afca22ed1a8af8fd5ac9231442e2124646e#diff-6e80cdd8c55add68105618ad9e8954170a47f59814201dadd2b888509d6b2e39R176
349 return 10;
350 }
351 return interpolate3d(
353 config->trailingSparkLoadBins, engineLoad,
355 );
356}
357
359 // time => degree
361
362 if (!std::isnan(correction)) {
363 return correction;
364 }
365 return 0;
366}
367
369 return interpolate3d(
373 );
374}
375
377 const size_t cylinderNumber,
378 const float rpm,
379 const float ignitionLoad
380) {
381 return interpolate3d(
382 config->ignTrims[cylinderNumber].table,
385 );
386}
387
388#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:326
IgnitionState ignitionState
Definition engine.h:247
LaunchControlBase launchController
Definition engine.h:224
EngineState engineState
Definition engine.h:352
RpmCalculator rpmCalculator
Definition engine.h:314
AntilagSystemBase antilagController
Definition engine.h:232
TunerStudioOutputChannels outputChannels
Definition engine.h:113
constexpr auto & module()
Definition engine.h:204
ShiftTorqueReductionController shiftTorqueReductionController
Definition engine.h:225
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
float getMinCrankingRpm() const
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:421
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
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, 1420, 1.0, -1.0, -1.0, "")
scaled_channel< int16_t, 100, 1 > timingALSCorrection
uint8_t tractionControlSpeedBins[TRACTION_CONTROL_ETB_DROP_SPEED_SIZE]
int8_t tractionControlTimingDrop[TRACTION_CONTROL_ETB_DROP_SPEED_SIZE][TRACTION_CONTROL_ETB_DROP_SLIP_SIZE]
scaled_channel< uint16_t, 100, 1 > tractionControlSlipBins[TRACTION_CONTROL_ETB_DROP_SLIP_SIZE]
int8_t tractionControlIgnitionSkip[TRACTION_CONTROL_ETB_DROP_SPEED_SIZE][TRACTION_CONTROL_ETB_DROP_SLIP_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, 10, 1 > ALSTimingRetardTable[ALS_SIZE][ALS_RPM_SIZE]
scaled_channel< uint8_t, 1, 50 > trailingSparkRpmBins[TRAILING_SPARK_RPM_SIZE]
scaled_channel< int16_t, 100, 1 > crankingAdvance[CRANKING_ADVANCE_CURVE_SIZE]
scaled_channel< int16_t, 10, 1 > ignitionCltCorrTable[CLT_TIMING_LOAD_AXIS_SIZE][CLT_TIMING_TEMP_AXIS_SIZE]
scaled_channel< int16_t, 10, 1 > ignitionIatCorrTable[IAT_IGN_CORR_LOAD_COUNT][IAT_IGN_CORR_TEMP_COUNT]
scaled_channel< uint8_t, 1, 5 > trailingSparkLoadBins[TRAILING_SPARK_SIZE]
scaled_channel< uint8_t, 50, 1 > dwellVoltageCorrValues[DWELL_CURVE_SIZE]
scaled_channel< uint8_t, 1, 5 > ignitionCltCorrLoadBins[CLT_TIMING_LOAD_AXIS_SIZE]
scaled_channel< int16_t, 1, 1 > ignitionCltCorrTempBins[CLT_TIMING_TEMP_AXIS_SIZE]
scaled_channel< uint8_t, 1, 50 > idleAdvanceBins[IDLE_ADVANCE_CURVE_SIZE]
scaled_channel< int8_t, 10, 1 > trailingSparkTable[TRAILING_SPARK_SIZE][TRAILING_SPARK_RPM_SIZE]
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]
void wrapAngle(angle_t &angle, const char *msg, ObdCode code)