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 |