| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 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 | |||
| 31 | float minCrankingRpm = 0; | |||
| 32 | ||||
| 33 | static Map3D<TRACTION_CONTROL_ETB_DROP_SLIP_SIZE, TRACTION_CONTROL_ETB_DROP_SPEED_SIZE, int8_t, uint16_t, uint8_t> tcTimingDropTable{"tct"}; | |||
| 34 | static Map3D<TRACTION_CONTROL_ETB_DROP_SLIP_SIZE, TRACTION_CONTROL_ETB_DROP_SPEED_SIZE, int8_t, uint16_t, uint8_t> tcSparkSkipTable{"tcs"}; | |||
| 35 | ||||
| 36 | #if EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
| 37 | ||||
| 38 | /** | |||
| 39 | * @return ignition timing angle advance before TDC | |||
| 40 | */ | |||
| 41 | 1086 | angle_t getRunningAdvance(float rpm, float engineLoad) { | ||
| 42 |
2/2✓ Branch 1 taken 2 times.
✓ Branch 2 taken 1084 times.
|
2/2✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 1084 times.
|
1086 | if (std::isnan(engineLoad)) { |
| 43 | 2 | warning(ObdCode::CUSTOM_NAN_ENGINE_LOAD, "NaN engine load"); | ||
| 44 | 2 | return NAN; | ||
| 45 | } | |||
| 46 | ||||
| 47 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1084 times.
|
1084 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(engineLoad), "invalid el", NAN); | |
| 48 | ||||
| 49 | // compute base ignition angle from main table | |||
| 50 | 1084 | float advanceAngle = IgnitionState::getInterpolatedIgnitionAngle(rpm, engineLoad); | ||
| 51 | ||||
| 52 | 1084 | float vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed); | ||
| 53 | 1084 | float wheelSlip = Sensor::getOrZero(SensorType::WheelSlipRatio); | ||
| 54 |
1/1✓ Branch 2 taken 1084 times.
|
1084 | engine->ignitionState.tractionAdvanceDrop = tcTimingDropTable.getValue(wheelSlip, vehicleSpeed); | |
| 55 | 1084 | engine->engineState.tractionControlSparkSkip = tcSparkSkipTable.getValue(wheelSlip, vehicleSpeed); | ||
| 56 | 1084 | engine->engineState.updateSparkSkip(); | ||
| 57 | ||||
| 58 | 1084 | advanceAngle += engine->ignitionState.tractionAdvanceDrop; | ||
| 59 | ||||
| 60 | #if EFI_ANTILAG_SYSTEM | |||
| 61 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1084 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1084 times.
|
1084 | if (engine->antilagController.isAntilagCondition) { |
| 62 | ✗ | float throttleIntent = Sensor::getOrZero(SensorType::DriverThrottleIntent); | ||
| 63 | ✗ | engine->antilagController.timingALSCorrection = interpolate3d( | ||
| 64 | ✗ | config->ALSTimingRetardTable, | ||
| 65 | ✗ | config->alsIgnRetardLoadBins, throttleIntent, | ||
| 66 | ✗ | config->alsIgnRetardrpmBins, rpm | ||
| 67 | ); | |||
| 68 | ✗ | advanceAngle += engine->antilagController.timingALSCorrection; | ||
| 69 | } | |||
| 70 | #endif /* EFI_ANTILAG_SYSTEM */ | |||
| 71 | ||||
| 72 | // Add any adjustments if configured | |||
| 73 |
2/2✓ Branch 1 taken 4336 times.
✓ Branch 2 taken 1084 times.
|
2/2✓ Decision 'true' taken 4336 times.
✓ Decision 'false' taken 1084 times.
|
5420 | for (size_t i = 0; i < efi::size(config->ignBlends); i++) { |
| 74 | 4336 | auto result = calculateBlend(config->ignBlends[i], rpm, engineLoad); | ||
| 75 | ||||
| 76 | 4336 | engine->outputChannels.ignBlendParameter[i] = result.BlendParameter; | ||
| 77 | 4336 | engine->outputChannels.ignBlendBias[i] = result.Bias; | ||
| 78 | 4336 | engine->outputChannels.ignBlendOutput[i] = result.Value; | ||
| 79 | 4336 | engine->outputChannels.ignBlendYAxis[i] = result.TableYAxis; | ||
| 80 | ||||
| 81 | 4336 | advanceAngle += result.Value; | ||
| 82 | } | |||
| 83 | ||||
| 84 | // get advance from the separate table for Idle | |||
| 85 | #if EFI_IDLE_CONTROL | |||
| 86 |
4/4✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1077 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1079 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1086 times.
|
1091 | if (engineConfiguration->useSeparateAdvanceForIdle && |
| 87 |
3/4✓ Branch 3 taken 2 times.
✓ Branch 4 taken 5 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 2 times.
|
7 | (engine->module<IdleController>()->isIdlingOrTaper() || engine->module<IdleController>()->isCoastingAdvance())) { | |
| 88 |
1/1✓ Branch 1 taken 5 times.
|
5 | float idleAdvance = interpolate2d(rpm, config->idleAdvanceBins, config->idleAdvance); | |
| 89 | ||||
| 90 |
1/1✓ Branch 2 taken 5 times.
|
5 | auto tps = Sensor::get(SensorType::DriverThrottleIntent); | |
| 91 |
1/2✓ Branch 1 taken 5 times.
✗ Branch 2 not taken.
|
1/2✓ Decision 'true' taken 5 times.
✗ Decision 'false' not taken.
|
5 | 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 | |||
| 96 | 5 | float idleThreshold = engineConfiguration->idlePidDeactivationTpsThreshold; | ||
| 97 |
1/1✓ Branch 1 taken 5 times.
|
5 | 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 | |||
| 104 |
6/6✓ Branch 0 taken 7 times.
✓ Branch 1 taken 1077 times.
✓ Branch 5 taken 5 times.
✓ Branch 6 taken 2 times.
✓ Branch 7 taken 5 times.
✓ Branch 8 taken 1079 times.
|
2/2✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 1079 times.
|
1084 | if(engineConfiguration->useSeparateAdvanceForIdle && engine->module<IdleController>()->isIdlingOrTaper()){ |
| 105 | 5 | engine->ignitionState.rpmForIgnitionIdleTableDot = rpm; | ||
| 106 | 5 | engine->ignitionState.rpmForIgnitionTableDot = -1; | ||
| 107 | 5 | engine->ignitionState.loadForIgnitionTableDot = -1; | ||
| 108 | } else { | |||
| 109 | 1079 | engine->ignitionState.rpmForIgnitionIdleTableDot = -1; | ||
| 110 | 1079 | engine->ignitionState.rpmForIgnitionTableDot = rpm; | ||
| 111 | 1079 | engine->ignitionState.loadForIgnitionTableDot = engineLoad; | ||
| 112 | } | |||
| 113 | #endif | |||
| 114 | ||||
| 115 | #if EFI_LAUNCH_CONTROL | |||
| 116 |
4/4✓ Branch 0 taken 92 times.
✓ Branch 1 taken 992 times.
✓ Branch 2 taken 64 times.
✓ Branch 3 taken 28 times.
|
2/2✓ Decision 'true' taken 64 times.
✓ Decision 'false' taken 1020 times.
|
1084 | if (engineConfiguration->launchControlEnabled && engineConfiguration->enableLaunchRetard) { |
| 117 | 64 | const float launchAngle = engineConfiguration->launchTimingRetard; | ||
| 118 |
2/2✓ Branch 0 taken 12 times.
✓ Branch 1 taken 52 times.
|
2/2✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 52 times.
|
64 | if (engine->launchController.isPreLaunchCondition) { |
| 119 | 12 | const int launchRpm = engineConfiguration->launchRpm; | ||
| 120 | 12 | const int smoothRetardStartRpm = (launchRpm - engineConfiguration->launchRpmWindow); | ||
| 121 | 12 | const int smoothRetardEndRpm = (launchRpm - engineConfiguration->launchCorrectionsEndRpm); | ||
| 122 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 12 times.
✗ Decision 'false' not taken.
|
12 | if (smoothRetardStartRpm <= rpm) { |
| 123 |
4/4✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 2 times.
|
2/2✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 8 times.
|
12 | if (engineConfiguration->launchSmoothRetard && (rpm <= smoothRetardEndRpm)) { |
| 124 | // https://github.com/rusefi/rusefi/issues/5611#issuecomment-2130431696 | |||
| 125 | 4 | return interpolateClamped(smoothRetardStartRpm, advanceAngle, smoothRetardEndRpm, launchAngle, rpm); | ||
| 126 | } else { | |||
| 127 | 8 | return launchAngle; | ||
| 128 | } | |||
| 129 | } | |||
| 130 |
2/2✓ Branch 0 taken 10 times.
✓ Branch 1 taken 42 times.
|
2/2✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 42 times.
|
52 | } else if (engine->launchController.isLaunchCondition) { |
| 131 | 10 | return launchAngle; | ||
| 132 | } | |||
| 133 | } | |||
| 134 |
2/2✓ Branch 0 taken 533 times.
✓ Branch 1 taken 529 times.
|
1062 | if (engineConfiguration->torqueReductionEnabled | |
| 135 |
2/2✓ Branch 0 taken 11 times.
✓ Branch 1 taken 522 times.
|
533 | && engine->shiftTorqueReductionController.isFlatShiftConditionSatisfied | |
| 136 | ) { | |||
| 137 | 11 | return engine->shiftTorqueReductionController.getTorqueReductionIgnitionRetard(); | ||
| 138 | } | |||
| 139 |
6/6✓ Branch 0 taken 12 times.
✓ Branch 1 taken 1039 times.
✓ Branch 4 taken 4 times.
✓ Branch 5 taken 8 times.
✓ Branch 6 taken 4 times.
✓ Branch 7 taken 1047 times.
|
1051 | if (engineConfiguration->nitrousControlEnabled && engine->module<NitrousController>()->isNitrousCondition) { | |
| 140 | 4 | advanceAngle -= engineConfiguration->nitrousIgnitionRetard; | ||
| 141 | } | |||
| 142 | #endif /* EFI_LAUNCH_CONTROL */ | |||
| 143 | ||||
| 144 | #ifdef MODULE_VVL_CONTROLLER | |||
| 145 | 1051 | advanceAngle += engine->module<VvlController>().unmock().getTimingModifier(); | ||
| 146 | #endif /* MODULE_VVL_CONTROLLER */ | |||
| 147 | ||||
| 148 | 1051 | return advanceAngle; | ||
| 149 | } | |||
| 150 | ||||
| 151 | 1101 | angle_t getCltTimingCorrection(float engineLoad) { | ||
| 152 |
1/1✓ Branch 2 taken 1101 times.
|
1101 | const auto clt = Sensor::get(SensorType::Clt); | |
| 153 | ||||
| 154 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1101 times.
|
1101 | if (!clt) | |
| 155 | ✗ | return 0; // this error should be already reported somewhere else, let's just handle it | ||
| 156 | ||||
| 157 | 2202 | return interpolate3d( | ||
| 158 | 1101 | config->ignitionCltCorrTable, | ||
| 159 | 1101 | config->ignitionCltCorrLoadBins, engineLoad, | ||
| 160 |
1/1✓ Branch 1 taken 1101 times.
|
1101 | config->ignitionCltCorrTempBins, clt.Value | |
| 161 | 1101 | ); | ||
| 162 | } | |||
| 163 | ||||
| 164 | 1101 | void IgnitionState::updateAdvanceCorrections(float engineLoad) { | ||
| 165 |
1/1✓ Branch 2 taken 1101 times.
|
1101 | cltTimingCorrection = getCltTimingCorrection(engineLoad); | |
| 166 | 1101 | } | ||
| 167 | ||||
| 168 | 941 | angle_t getAdvanceCorrections(float engineLoad) { | ||
| 169 |
1/1✓ Branch 2 taken 941 times.
|
941 | auto iat = Sensor::get(SensorType::Iat); | |
| 170 | ||||
| 171 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 941 times.
|
941 | if (!iat) { | |
| 172 | ✗ | engine->ignitionState.timingIatCorrection = 0; | ||
| 173 | } else { | |||
| 174 | 1882 | engine->ignitionState.timingIatCorrection = interpolate3d( | ||
| 175 | 941 | config->ignitionIatCorrTable, | ||
| 176 | 941 | config->ignitionIatCorrLoadBins, engineLoad, | ||
| 177 |
1/1✓ Branch 1 taken 941 times.
|
941 | config->ignitionIatCorrTempBins, iat.Value | |
| 178 | ); | |||
| 179 | } | |||
| 180 | ||||
| 181 | #if EFI_IDLE_CONTROL | |||
| 182 | 941 | float instantRpm = engine->triggerCentral.instantRpm.getInstantRpm(); | ||
| 183 | ||||
| 184 |
2/2✓ Branch 2 taken 941 times.
✓ Branch 6 taken 941 times.
|
941 | engine->ignitionState.timingPidCorrection = engine->module<IdleController>()->getIdleTimingAdjustment(instantRpm); | |
| 185 | #endif // EFI_IDLE_CONTROL | |||
| 186 | ||||
| 187 |
2/2✓ Branch 2 taken 941 times.
✓ Branch 6 taken 941 times.
|
941 | engine->ignitionState.dfcoTimingRetard = engine->module<DfcoController>()->getTimingRetard(); | |
| 188 | ||||
| 189 | #if EFI_TUNER_STUDIO | |||
| 190 | 941 | engine->outputChannels.multiSparkCounter = engine->engineState.multispark.count; | ||
| 191 | #endif /* EFI_TUNER_STUDIO */ | |||
| 192 | ||||
| 193 | 941 | return engine->ignitionState.timingIatCorrection | ||
| 194 | 941 | + engine->ignitionState.cltTimingCorrection | ||
| 195 | 941 | + engine->ignitionState.timingPidCorrection | ||
| 196 | 1882 | - engine->ignitionState.dfcoTimingRetard; | ||
| 197 | } | |||
| 198 | ||||
| 199 | /** | |||
| 200 | * @return ignition timing angle advance before TDC for Cranking | |||
| 201 | */ | |||
| 202 | 131 | angle_t getCrankingAdvance(float rpm, float engineLoad) { | ||
| 203 | // get advance from the separate table for Cranking | |||
| 204 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 131 times.
|
131 | if (engineConfiguration->useSeparateAdvanceForCranking) { | |
| 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 | 131 | angle_t crankingToRunningTransitionAngle = getRunningAdvance(engineConfiguration->cranking.rpm, engineLoad); | ||
| 210 | // interpolate not from zero, but starting from min. possible rpm detected | |||
| 211 |
4/4✓ Branch 0 taken 128 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 86 times.
✓ Branch 3 taken 42 times.
|
131 | if (rpm < minCrankingRpm || minCrankingRpm == 0) | |
| 212 | 89 | minCrankingRpm = rpm; | ||
| 213 | 131 | return interpolateClamped(minCrankingRpm, engineConfiguration->crankingTimingAngle, engineConfiguration->cranking.rpm, crankingToRunningTransitionAngle, rpm); | ||
| 214 | } | |||
| 215 | #endif // EFI_ENGINE_CONTROL && EFI_SHAFT_POSITION_INPUT | |||
| 216 | ||||
| 217 | 1103 | angle_t IgnitionState::getAdvance(float rpm, float engineLoad) { | ||
| 218 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1103 times.
|
1103 | if (std::isnan(engineLoad)) { | |
| 219 | ✗ | return 0; // any error should already be reported | ||
| 220 | } | |||
| 221 |
2/2✓ Branch 0 taken 33 times.
✓ Branch 1 taken 1070 times.
|
1103 | if (engineConfiguration->timingMode == TM_FIXED) { | |
| 222 | // fixed timing is the simple: cranking/running does not matter, no corrections! | |||
| 223 | 33 | return engineConfiguration->fixedTiming; | ||
| 224 | } | |||
| 225 | ||||
| 226 | angle_t angle; | |||
| 227 | ||||
| 228 | 1070 | bool isCranking = engine->rpmCalculator.isCranking(); | ||
| 229 |
2/2✓ Branch 0 taken 130 times.
✓ Branch 1 taken 940 times.
|
1070 | if (isCranking) { | |
| 230 | 130 | angle = getCrankingAdvance(rpm, engineLoad); | ||
| 231 |
2/4✓ Branch 0 taken 130 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
|
130 | assertAngleRange(angle, "crAngle", ObdCode::CUSTOM_ERR_ANGLE_CR); | |
| 232 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 130 times.
|
130 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "cr_AngleN", 0); | |
| 233 | } else { | |||
| 234 | 940 | angle = getRunningAdvance(rpm, engineLoad); | ||
| 235 | ||||
| 236 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 940 times.
|
940 | 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 |
3/4✓ Branch 0 taken 130 times.
✓ Branch 1 taken 940 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 130 times.
|
1070 | bool allowCorrections = !isCranking || engineConfiguration->useAdvanceCorrectionsForCranking; | |
| 244 | ||||
| 245 |
2/2✓ Branch 0 taken 940 times.
✓ Branch 1 taken 130 times.
|
1070 | if (allowCorrections) { | |
| 246 | 940 | angle_t correction = getAdvanceCorrections(engineLoad); | ||
| 247 |
1/2✓ Branch 1 taken 940 times.
✗ Branch 2 not taken.
|
940 | if (!std::isnan(correction)) { // correction could be NaN during settings update | |
| 248 | 940 | angle += correction; | ||
| 249 | } | |||
| 250 | } | |||
| 251 | ||||
| 252 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1070 times.
|
1070 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(angle), "_AngleN5", 0); | |
| 253 | 1070 | return angle; | ||
| 254 | } | |||
| 255 | ||||
| 256 | 1103 | angle_t IgnitionState::getWrappedAdvance(const float rpm, const float engineLoad) { | ||
| 257 |
1/1✓ Branch 2 taken 1103 times.
|
1103 | angle_t angle = getAdvance(rpm, engineLoad) * luaTimingMult + luaTimingAdd; | |
| 258 |
1/1✓ Branch 1 taken 1103 times.
|
1103 | wrapAngle(angle, "getWrappedAdvance", ObdCode::CUSTOM_ERR_ADCANCE_CALC_ANGLE); | |
| 259 | 1103 | return angle; | ||
| 260 | } | |||
| 261 | ||||
| 262 | PUBLIC_API_WEAK_SOMETHING_WEIRD | |||
| 263 | 4437 | angle_t getCylinderIgnitionTrim(size_t cylinderNumber, float rpm, float ignitionLoad) { | ||
| 264 | 4437 | return IgnitionState::getInterpolatedIgnitionTrim(cylinderNumber, rpm, ignitionLoad); | ||
| 265 | } | |||
| 266 | ||||
| 267 | 1122 | size_t getMultiSparkCount(float rpm) { | ||
| 268 | // Compute multispark (if enabled) | |||
| 269 | 2244 | if (engineConfiguration->multisparkEnable | ||
| 270 |
2/2✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
|
13 | && rpm <= engineConfiguration->multisparkMaxRpm | |
| 271 |
5/6✓ Branch 0 taken 13 times.
✓ Branch 1 taken 1109 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 1111 times.
|
1135 | && engineConfiguration->multisparkMaxExtraSparkCount > 0) { | |
| 272 | // For zero RPM, disable multispark. We don't yet know the engine speed, so multispark may not be safe. | |||
| 273 |
2/2✓ Branch 0 taken 1 time.
✓ Branch 1 taken 10 times.
|
11 | if (rpm == 0) { | |
| 274 | 1 | return 0; | ||
| 275 | } | |||
| 276 | ||||
| 277 | 10 | floatus_t multiDelay = 1000.0f * engineConfiguration->multisparkSparkDuration; | ||
| 278 | 10 | 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 | 10 | engine->engineState.multispark.delay = (uint32_t)USF2NT(multiDelay); | ||
| 282 | 10 | engine->engineState.multispark.dwell = (uint32_t)USF2NT(multiDwell); | ||
| 283 | ||||
| 284 | 10 | constexpr float usPerDegreeAt1Rpm = 60e6 / 360; | ||
| 285 | 10 | floatus_t usPerDegree = usPerDegreeAt1Rpm / rpm; | ||
| 286 | ||||
| 287 | // How long is there for sparks? The user configured an angle, convert to time. | |||
| 288 | 10 | floatus_t additionalSparksUs = usPerDegree * engineConfiguration->multisparkMaxSparkingAngle; | ||
| 289 | // How long does one spark take? | |||
| 290 | 10 | floatus_t oneSparkTime = multiDelay + multiDwell; | ||
| 291 | ||||
| 292 | // How many sparks can we fit in the alloted time? | |||
| 293 | 10 | float sparksFitInTime = additionalSparksUs / oneSparkTime; | ||
| 294 | ||||
| 295 | // Take the floor (convert to uint8_t) - we want to undershoot, not overshoot | |||
| 296 | 10 | uint32_t floored = sparksFitInTime; | ||
| 297 | ||||
| 298 | // Allow no more than the maximum number of extra sparks | |||
| 299 | 10 | return minI(floored, engineConfiguration->multisparkMaxExtraSparkCount); | ||
| 300 | } else { | |||
| 301 | 1111 | return 0; | ||
| 302 | } | |||
| 303 | } | |||
| 304 | ||||
| 305 | 588 | void initIgnitionAdvanceControl() { | ||
| 306 | 588 | tcTimingDropTable.initTable(engineConfiguration->tractionControlTimingDrop, engineConfiguration->tractionControlSlipBins, engineConfiguration->tractionControlSpeedBins); | ||
| 307 | 588 | tcSparkSkipTable.initTable(engineConfiguration->tractionControlIgnitionSkip, engineConfiguration->tractionControlSlipBins, engineConfiguration->tractionControlSpeedBins); | ||
| 308 | 588 | } | ||
| 309 | ||||
| 310 | /** | |||
| 311 | * @return Spark dwell time, in milliseconds. 0 if tables are not ready. | |||
| 312 | */ | |||
| 313 | 1105 | floatms_t IgnitionState::getSparkDwell(float rpm, bool isCranking) { | ||
| 314 | float dwellMs; | |||
| 315 |
2/2✓ Branch 0 taken 146 times.
✓ Branch 1 taken 959 times.
|
1105 | if (isCranking) { | |
| 316 | 146 | dwellMs = engineConfiguration->ignitionDwellForCrankingMs; | ||
| 317 | } else { | |||
| 318 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 959 times.
|
959 | efiAssert(ObdCode::CUSTOM_ERR_ASSERT, !std::isnan(rpm), "invalid rpm", NAN); | |
| 319 | ||||
| 320 | 959 | baseDwell = interpolate2d(rpm, config->sparkDwellRpmBins, config->sparkDwellValues); | ||
| 321 | 959 | dwellVoltageCorrection = interpolate2d( | ||
| 322 | Sensor::getOrZero(SensorType::BatteryVoltage), | |||
| 323 | 959 | config->dwellVoltageCorrVoltBins, | ||
| 324 | 959 | config->dwellVoltageCorrValues | ||
| 325 | ); | |||
| 326 | ||||
| 327 | // for compat (table full of zeroes) | |||
| 328 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 959 times.
|
959 | if (dwellVoltageCorrection < 0.1f) { | |
| 329 | ✗ | dwellVoltageCorrection = 1; | ||
| 330 | } | |||
| 331 | ||||
| 332 | 959 | dwellMs = baseDwell * dwellVoltageCorrection; | ||
| 333 | } | |||
| 334 | ||||
| 335 |
3/6✓ Branch 1 taken 1105 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1105 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 1105 times.
|
1105 | 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 | 1105 | return dwellMs; | ||
| 341 | } | |||
| 342 | ||||
| 343 | 1105 | void IgnitionState::updateDwell(float rpm, bool isCranking) { | ||
| 344 | 1105 | sparkDwell = getSparkDwell(rpm, isCranking); | ||
| 345 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1105 times.
|
1105 | dwellDurationAngle = std::isnan(rpm) ? NAN : getDwell() / getOneDegreeTimeMs(rpm); | |
| 346 | 1105 | } | ||
| 347 | ||||
| 348 | 572016 | floatms_t IgnitionState::getDwell() const { | ||
| 349 | 572016 | return sparkDwell; | ||
| 350 | } | |||
| 351 | ||||
| 352 | 1101 | angle_t IgnitionState::getTrailingSparkAngle(const float rpm, const float engineLoad){ | ||
| 353 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 1101 times.
|
1101 | if (std::isnan(engineLoad)) { | |
| 354 | // default value from: https://github.com/rusefi/rusefi/commit/86683afca22ed1a8af8fd5ac9231442e2124646e#diff-6e80cdd8c55add68105618ad9e8954170a47f59814201dadd2b888509d6b2e39R176 | |||
| 355 | ✗ | return 10; | ||
| 356 | } | |||
| 357 | 2202 | return interpolate3d( | ||
| 358 | 1101 | config->trailingSparkTable, | ||
| 359 | 1101 | config->trailingSparkLoadBins, engineLoad, | ||
| 360 | 1101 | config->trailingSparkRpmBins, rpm | ||
| 361 | 1101 | ); | ||
| 362 | } | |||
| 363 | ||||
| 364 | 4437 | angle_t IgnitionState::getSparkHardwareLatencyCorrection(){ | ||
| 365 | // time => degree | |||
| 366 | 4437 | angle_t correction = engineConfiguration->sparkHardwareLatencyCorrection / engine->rpmCalculator.oneDegreeUs; | ||
| 367 | ||||
| 368 |
2/2✓ Branch 1 taken 1226 times.
✓ Branch 2 taken 3211 times.
|
4437 | if (!std::isnan(correction)) { | |
| 369 | 1226 | return correction; | ||
| 370 | } | |||
| 371 | 3211 | return 0; | ||
| 372 | } | |||
| 373 | ||||
| 374 | 1084 | angle_t IgnitionState::getInterpolatedIgnitionAngle(const float rpm, const float ignitionLoad) { | ||
| 375 | 2168 | return interpolate3d( | ||
| 376 | 1084 | config->ignitionTable, | ||
| 377 | 1084 | config->ignitionLoadBins, ignitionLoad, | ||
| 378 | 1084 | config->ignitionRpmBins, rpm | ||
| 379 | 1084 | ); | ||
| 380 | } | |||
| 381 | ||||
| 382 | 4437 | angle_t IgnitionState::getInterpolatedIgnitionTrim( | ||
| 383 | const size_t cylinderNumber, | |||
| 384 | const float rpm, | |||
| 385 | const float ignitionLoad | |||
| 386 | ) { | |||
| 387 | 8874 | return interpolate3d( | ||
| 388 | 4437 | config->ignTrims[cylinderNumber].table, | ||
| 389 | 4437 | config->ignTrimLoadBins, ignitionLoad, | ||
| 390 | 4437 | config->ignTrimRpmBins, rpm | ||
| 391 | 4437 | ); | ||
| 392 | } | |||
| 393 | ||||
| 394 | #endif // EFI_ENGINE_CONTROL | |||
| 395 |