| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /** | |||
| 2 | * @file engine_math.cpp | |||
| 3 | * @brief | |||
| 4 | * | |||
| 5 | * @date Jul 13, 2013 | |||
| 6 | * @author Andrey Belomutskiy, (c) 2012-2020 | |||
| 7 | * | |||
| 8 | * This file is part of rusEfi - see http://rusefi.com | |||
| 9 | * | |||
| 10 | * rusEfi is free software; you can redistribute it and/or modify it under the terms of | |||
| 11 | * the GNU General Public License as published by the Free Software Foundation; either | |||
| 12 | * version 3 of the License, or (at your option) any later version. | |||
| 13 | * | |||
| 14 | * rusEfi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without | |||
| 15 | * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
| 16 | * GNU General Public License for more details. | |||
| 17 | * | |||
| 18 | * You should have received a copy of the GNU General Public License along with this program. | |||
| 19 | * If not, see <http://www.gnu.org/licenses/>. | |||
| 20 | */ | |||
| 21 | ||||
| 22 | #include "pch.h" | |||
| 23 | ||||
| 24 | #include "event_registry.h" | |||
| 25 | #include "fuel_math.h" | |||
| 26 | #include "advance_map.h" | |||
| 27 | #include "gppwm_channel_reader.h" | |||
| 28 | ||||
| 29 | #if EFI_UNIT_TEST | |||
| 30 | extern bool verboseMode; | |||
| 31 | #endif /* EFI_UNIT_TEST */ | |||
| 32 | ||||
| 33 | 1064981 | floatms_t getEngineCycleDuration(float rpm) { | ||
| 34 |
2/2✓ Branch 3 taken 2790 times.
✓ Branch 4 taken 1062191 times.
|
1064981 | return getCrankshaftRevolutionTimeMs(rpm) * (getEngineRotationState()->getOperationMode() == TWO_STROKE ? 1 : 2); | |
| 35 | } | |||
| 36 | ||||
| 37 | /** | |||
| 38 | * @return number of milliseconds in one crank shaft revolution | |||
| 39 | */ | |||
| 40 | 1596060 | floatms_t getCrankshaftRevolutionTimeMs(float rpm) { | ||
| 41 |
2/2✓ Branch 0 taken 588144 times.
✓ Branch 1 taken 1007916 times.
|
2/2✓ Decision 'true' taken 588144 times.
✓ Decision 'false' taken 1007916 times.
|
1596060 | if (rpm == 0) { |
| 42 | 588144 | return NAN; | ||
| 43 | } | |||
| 44 | 1007916 | return 360 * getOneDegreeTimeMs(rpm); | ||
| 45 | } | |||
| 46 | ||||
| 47 | 1106 | float getFuelingLoad() { | ||
| 48 | 1106 | return getEngineState()->fuelingLoad; | ||
| 49 | } | |||
| 50 | ||||
| 51 | 2203 | float getIgnitionLoad() { | ||
| 52 | 2203 | return getEngineState()->ignitionLoad; | ||
| 53 | } | |||
| 54 | ||||
| 55 | /** | |||
| 56 | * see also setConstantDwell | |||
| 57 | */ | |||
| 58 | 4 | void setSingleCoilDwell() { | ||
| 59 |
2/2✓ Branch 0 taken 32 times.
✓ Branch 1 taken 4 times.
|
2/2✓ Decision 'true' taken 32 times.
✓ Decision 'false' taken 4 times.
|
36 | for (int i = 0; i < DWELL_CURVE_SIZE; i++) { |
| 60 | 32 | config->sparkDwellRpmBins[i] = (i + 1) * 50; | ||
| 61 | 32 | config->sparkDwellValues[i] = 4; | ||
| 62 | } | |||
| 63 | ||||
| 64 | 4 | config->sparkDwellRpmBins[5] = 500; | ||
| 65 | 4 | config->sparkDwellValues[5] = 4; | ||
| 66 | ||||
| 67 | 4 | config->sparkDwellRpmBins[6] = 4500; | ||
| 68 | 4 | config->sparkDwellValues[6] = 4; | ||
| 69 | ||||
| 70 | 4 | config->sparkDwellRpmBins[7] = 12500; | ||
| 71 | 4 | config->sparkDwellValues[7] = 0; | ||
| 72 | 4 | } | ||
| 73 | ||||
| 74 | /** | |||
| 75 | * @return IM_WASTED_SPARK if in SPINNING mode and IM_INDIVIDUAL_COILS setting | |||
| 76 | * @return engineConfiguration->ignitionMode otherwise | |||
| 77 | */ | |||
| 78 | 540978 | ignition_mode_e getCurrentIgnitionMode() { | ||
| 79 | 540978 | ignition_mode_e ignitionMode = engineConfiguration->ignitionMode; | ||
| 80 | #if EFI_SHAFT_POSITION_INPUT | |||
| 81 | // In spin-up cranking mode we don't have full phase sync info yet, so wasted spark mode is better | |||
| 82 |
2/2✓ Branch 0 taken 121728 times.
✓ Branch 1 taken 419250 times.
|
2/2✓ Decision 'true' taken 121728 times.
✓ Decision 'false' taken 419250 times.
|
540978 | if (ignitionMode == IM_INDIVIDUAL_COILS) { |
| 83 | bool missingPhaseInfoForSequential = | |||
| 84 | 121728 | !engine->triggerCentral.triggerState.hasSynchronizedPhase(); | ||
| 85 | ||||
| 86 |
6/6✓ Branch 1 taken 86616 times.
✓ Branch 2 taken 35112 times.
✓ Branch 3 taken 52627 times.
✓ Branch 4 taken 33989 times.
✓ Branch 5 taken 87739 times.
✓ Branch 6 taken 33989 times.
|
2/2✓ Decision 'true' taken 87739 times.
✓ Decision 'false' taken 33989 times.
|
121728 | if (engine->rpmCalculator.isSpinningUp() || missingPhaseInfoForSequential) { |
| 87 | 87739 | ignitionMode = IM_WASTED_SPARK; | ||
| 88 | } | |||
| 89 | } | |||
| 90 | #endif /* EFI_SHAFT_POSITION_INPUT */ | |||
| 91 | 540978 | return ignitionMode; | ||
| 92 | } | |||
| 93 | ||||
| 94 | #if EFI_ENGINE_CONTROL | |||
| 95 | ||||
| 96 | /** | |||
| 97 | * This heavy method is only invoked in case of a configuration change or initialization. | |||
| 98 | */ | |||
| 99 |
1/1✓ Decision 'true' taken 1494 times.
|
1494 | void prepareOutputSignals() { | |
| 100 | 1494 | auto operationMode = getEngineRotationState()->getOperationMode(); | ||
| 101 | 1494 | getEngineState()->engineCycle = getEngineCycle(operationMode); | ||
| 102 | ||||
| 103 | 1494 | bool isOddFire = false; | ||
| 104 |
2/2✓ Branch 0 taken 5590 times.
✓ Branch 1 taken 1464 times.
|
2/2✓ Decision 'true' taken 5590 times.
✓ Decision 'false' taken 1464 times.
|
7054 | for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) { |
| 105 |
2/2✓ Branch 1 taken 30 times.
✓ Branch 2 taken 5560 times.
|
2/2✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 5560 times.
|
5590 | if (engineConfiguration->timing_offset_cylinder[i] != 0) { |
| 106 | 30 | isOddFire = true; | ||
| 107 | 30 | break; | ||
| 108 | } | |||
| 109 | } | |||
| 110 | ||||
| 111 | 1494 | EngineCylinders::updateCylinders(); | ||
| 112 | ||||
| 113 | // Use odd fire wasted spark logic if not two stroke, and an odd fire or odd cylinder # engine | |||
| 114 | 1494 | getEngineState()->useOddFireWastedSpark = operationMode != TWO_STROKE | ||
| 115 |
4/4✓ Branch 0 taken 1481 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 1441 times.
|
1494 | && (isOddFire | (engineConfiguration->cylindersCount % 2 == 1)); | |
| 116 | ||||
| 117 | #if EFI_UNIT_TEST | |||
| 118 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1494 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 1494 times.
|
1494 | if (verboseMode) { |
| 119 | ✗ | printf("prepareOutputSignals %d %s\r\n", engineConfiguration->trigger.type, getIgnition_mode_e(engineConfiguration->ignitionMode)); | ||
| 120 | } | |||
| 121 | #endif /* EFI_UNIT_TEST */ | |||
| 122 | ||||
| 123 | #if EFI_SHAFT_POSITION_INPUT | |||
| 124 | 1494 | engine->triggerCentral.prepareTriggerShape(); | ||
| 125 | #endif // EFI_SHAFT_POSITION_INPUT | |||
| 126 | ||||
| 127 | // Fuel schedule may now be completely wrong, force a reset | |||
| 128 | 1494 | engine->injectionEvents.invalidate(); | ||
| 129 | 1494 | } | ||
| 130 | ||||
| 131 | 9848 | angle_t getPerCylinderFiringOrderOffset(uint8_t cylinderIndex, uint8_t cylinderNumber) { | ||
| 132 | // base = position of this cylinder in the firing order. | |||
| 133 | // We get a cylinder every n-th of an engine cycle where N is the number of cylinders | |||
| 134 | 9848 | auto firingOrderOffset = engine->engineState.engineCycle * cylinderIndex / engineConfiguration->cylindersCount; | ||
| 135 | ||||
| 136 | // Plus or minus any adjustment if this is an odd-fire engine | |||
| 137 | 9848 | auto adjustment = engineConfiguration->timing_offset_cylinder[cylinderNumber]; | ||
| 138 | ||||
| 139 | 9848 | auto result = firingOrderOffset + adjustment; | ||
| 140 | ||||
| 141 |
2/4✓ Branch 0 taken 9848 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9848 times.
|
9848 | assertAngleRange(result, "getCylinderAngle", ObdCode::CUSTOM_ERR_CYL_ANGLE); | |
| 142 | ||||
| 143 | 9848 | return result; | ||
| 144 | } | |||
| 145 | ||||
| 146 | 587 | void setTimingRpmBin(float from, float to) { | ||
| 147 | 587 | setRpmBin(config->ignitionRpmBins, IGN_RPM_COUNT, from, to); | ||
| 148 | 587 | } | ||
| 149 | ||||
| 150 | /** | |||
| 151 | * this method sets algorithm and ignition table scale | |||
| 152 | */ | |||
| 153 | 20 | void setAlgorithm(engine_load_mode_e algo) { | ||
| 154 | 20 | engineConfiguration->fuelAlgorithm = algo; | ||
| 155 | 20 | } | ||
| 156 | ||||
| 157 | 28 | void setFlatInjectorLag(float value) { | ||
| 158 | 28 | setTable(engineConfiguration->injector.battLagCorrTable, value); | ||
| 159 | 28 | } | ||
| 160 | ||||
| 161 | 11148 | BlendResult calculateBlend(blend_table_s& cfg, float rpm, float load) { | ||
| 162 | // If set to 0, skip the math as its disabled | |||
| 163 |
1/2✓ Branch 0 taken 11148 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 11148 times.
✗ Decision 'false' not taken.
|
11148 | if (cfg.blendParameter == GPPWM_Zero) { |
| 164 | 11148 | return { 0, 0, 0, 0 }; | ||
| 165 | } | |||
| 166 | ||||
| 167 | ✗ | auto value = readGppwmChannel(cfg.blendParameter); | ||
| 168 | ||||
| 169 | ✗ | if (!value) { | ||
| 170 | ✗ | return { 0, 0, 0, 0 }; | ||
| 171 | } | |||
| 172 | ||||
| 173 | // Override Y axis value (if necessary) | |||
| 174 | ✗ | if (cfg.yAxisOverride != GPPWM_Zero) { | ||
| 175 | // TODO: is this value_or(0) correct or even reasonable? | |||
| 176 | ✗ | load = readGppwmChannel(cfg.yAxisOverride).value_or(0); | ||
| 177 | } | |||
| 178 | ||||
| 179 | ✗ | float tableValue = interpolate3d( | ||
| 180 | ✗ | cfg.table, | ||
| 181 | ✗ | cfg.loadBins, load, | ||
| 182 | ✗ | cfg.rpmBins, rpm | ||
| 183 | ); | |||
| 184 | ||||
| 185 | ✗ | float blendFactor = interpolate2d(value.Value, cfg.blendBins, cfg.blendValues); | ||
| 186 | ||||
| 187 | ✗ | return { value.Value, blendFactor, 0.01f * blendFactor * tableValue, load }; | ||
| 188 | } | |||
| 189 | ||||
| 190 | #endif /* EFI_ENGINE_CONTROL */ | |||
| 191 |