GCC Code Coverage Report


Directory: ./
File: firmware/controllers/math/engine_math.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 83.6% 61 0 73
Functions: 100.0% 12 0 12
Branches: 68.4% 26 0 38
Decisions: 71.4% 15 - 21

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 1048766 floatms_t getEngineCycleDuration(float rpm) {
34
2/2
✓ Branch 3 taken 2790 times.
✓ Branch 4 taken 1045976 times.
1048766 return getCrankshaftRevolutionTimeMs(rpm) * (getEngineRotationState()->getOperationMode() == TWO_STROKE ? 1 : 2);
35 }
36
37 /**
38 * @return number of milliseconds in one crank shaft revolution
39 */
40 1571721 floatms_t getCrankshaftRevolutionTimeMs(float rpm) {
41
2/2
✓ Branch 0 taken 588012 times.
✓ Branch 1 taken 983709 times.
2/2
✓ Decision 'true' taken 588012 times.
✓ Decision 'false' taken 983709 times.
1571721 if (rpm == 0) {
42 588012 return NAN;
43 }
44 983709 return 360 * getOneDegreeTimeMs(rpm);
45 }
46
47 1125 float getFuelingLoad() {
48 1125 return getEngineState()->fuelingLoad;
49 }
50
51 2241 float getIgnitionLoad() {
52 2241 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 532193 ignition_mode_e getCurrentIgnitionMode() {
79 532193 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 121756 times.
✓ Branch 1 taken 410437 times.
2/2
✓ Decision 'true' taken 121756 times.
✓ Decision 'false' taken 410437 times.
532193 if (ignitionMode == IM_INDIVIDUAL_COILS) {
83 bool missingPhaseInfoForSequential =
84 121756 !engine->triggerCentral.triggerState.hasSynchronizedPhase();
85
86
6/6
✓ Branch 1 taken 86637 times.
✓ Branch 2 taken 35119 times.
✓ Branch 3 taken 52627 times.
✓ Branch 4 taken 34010 times.
✓ Branch 5 taken 87746 times.
✓ Branch 6 taken 34010 times.
2/2
✓ Decision 'true' taken 87746 times.
✓ Decision 'false' taken 34010 times.
121756 if (engine->rpmCalculator.isSpinningUp() || missingPhaseInfoForSequential) {
87 87746 ignitionMode = IM_WASTED_SPARK;
88 }
89 }
90 #endif /* EFI_SHAFT_POSITION_INPUT */
91 532193 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 1490 times.
1490 void prepareOutputSignals() {
100 1490 auto operationMode = getEngineRotationState()->getOperationMode();
101 1490 getEngineState()->engineCycle = getEngineCycle(operationMode);
102
103 1490 bool isOddFire = false;
104
2/2
✓ Branch 0 taken 5574 times.
✓ Branch 1 taken 1460 times.
2/2
✓ Decision 'true' taken 5574 times.
✓ Decision 'false' taken 1460 times.
7034 for (size_t i = 0; i < engineConfiguration->cylindersCount; i++) {
105
2/2
✓ Branch 1 taken 30 times.
✓ Branch 2 taken 5544 times.
2/2
✓ Decision 'true' taken 30 times.
✓ Decision 'false' taken 5544 times.
5574 if (engineConfiguration->timing_offset_cylinder[i] != 0) {
106 30 isOddFire = true;
107 30 break;
108 }
109 }
110
111 1490 EngineCylinders::updateCylinders();
112
113 // Use odd fire wasted spark logic if not two stroke, and an odd fire or odd cylinder # engine
114 1490 getEngineState()->useOddFireWastedSpark = operationMode != TWO_STROKE
115
4/4
✓ Branch 0 taken 1477 times.
✓ Branch 1 taken 13 times.
✓ Branch 2 taken 40 times.
✓ Branch 3 taken 1437 times.
1490 && (isOddFire | (engineConfiguration->cylindersCount % 2 == 1));
116
117 #if EFI_UNIT_TEST
118
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1490 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1490 times.
1490 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 1490 engine->triggerCentral.prepareTriggerShape();
125 #endif // EFI_SHAFT_POSITION_INPUT
126
127 // Fuel schedule may now be completely wrong, force a reset
128 1490 engine->injectionEvents.invalidate();
129 1490 }
130
131 9170 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 9170 auto firingOrderOffset = engine->engineState.engineCycle * cylinderIndex / engineConfiguration->cylindersCount;
135
136 // Plus or minus any adjustment if this is an odd-fire engine
137 9170 auto adjustment = engineConfiguration->timing_offset_cylinder[cylinderNumber];
138
139 9170 auto result = firingOrderOffset + adjustment;
140
141
2/4
✓ Branch 0 taken 9170 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9170 times.
9170 assertAngleRange(result, "getCylinderAngle", ObdCode::CUSTOM_ERR_CYL_ANGLE);
142
143 9170 return result;
144 }
145
146 586 void setTimingRpmBin(float from, float to) {
147 586 setRpmBin(config->ignitionRpmBins, IGN_RPM_COUNT, from, to);
148 586 }
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 11258 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 11258 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 11258 times.
✗ Decision 'false' not taken.
11258 if (cfg.blendParameter == GPPWM_Zero) {
164 11258 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