GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/accel_enrichment.cpp
Date: 2025-10-24 14:26:41
Coverage Exec Excl Total
Lines: 95.6% 109 0 114
Functions: 100.0% 13 0 13
Branches: 84.4% 38 0 45
Decisions: 86.7% 26 - 30

Line Branch Decision Exec Source
1 /**
2 * @file accel_enrichment.cpp
3 * @brief Acceleration enrichment calculator
4 *
5 * In this file we have three strategies for acceleration/deceleration fuel correction
6 *
7 * 1) MAP rate-of-change correction
8 * 2) TPS rate-of-change correction
9 * 3) fuel film/wal wetting correction
10 * AWC Added to Wall Coefficient, %
11 * AWA Added to Wall Amount
12 * SOC Sucked Off wall Coefficient, %
13 * SOA Sucked Off wall amount
14 * WF current on-Wall Fuel amount
15 *
16 *
17 * http://rusefi.com/wiki/index.php?title=Manual:Software:Fuel_Control
18 * @date Apr 21, 2014
19 * @author Dmitry Sidin
20 * @author Andrey Belomutskiy, (c) 2012-2020
21 * @author Matthew Kennedy
22 */
23
24 #include "pch.h"
25 #include "accel_enrichment.h"
26 #include "tunerstudio.h"
27
28
29 // on this level we do not distinguish between multiplier and 'ms adder' modes
30 1143 float TpsAccelEnrichment::getTpsEnrichment() {
31 1143 ScopePerf perf(PE::GetTpsEnrichment);
32
33 // If predictive MAP mode is active, the old "adder" logic is disabled.
34
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1142 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 1142 times.
1143 if (engineConfiguration->accelEnrichmentMode == AE_MODE_PREDICTIVE_MAP) {
35 1 return 0;
36 }
37
38
2/2
✓ Branch 1 taken 1099 times.
✓ Branch 2 taken 43 times.
2/2
✓ Decision 'true' taken 1099 times.
✓ Decision 'false' taken 43 times.
1142 if (engineConfiguration->tpsAccelLookback == 0) {
39 // If disabled, return 0.
40 1099 return 0;
41 }
42
43 #if EFI_TUNER_STUDIO
44
2/3
✓ Branch 1 taken 43 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 43 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 43 times.
43 if (isTuningVeNow()) {
45 return 0;
46 }
47 #endif
48
49
1/1
✓ Branch 1 taken 43 times.
43 float rpm = Sensor::getOrZero(SensorType::Rpm);
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 43 times.
43 if (rpm < engineConfiguration->cranking.rpm) {
51 return 0;
52 }
53
54
2/2
✓ Branch 0 taken 29 times.
✓ Branch 1 taken 14 times.
2/2
✓ Decision 'true' taken 29 times.
✓ Decision 'false' taken 14 times.
43 if (isAboveAccelThreshold) {
55 58 valueFromTable = interpolate3d(config->tpsTpsAccelTable,
56 29 config->tpsTpsAccelToRpmBins, tpsTo,
57
1/1
✓ Branch 1 taken 29 times.
29 config->tpsTpsAccelFromRpmBins, tpsFrom);
58 29 extraFuel = valueFromTable;
59
1/1
✓ Branch 1 taken 29 times.
29 m_timeSinceAccel.reset();
60
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 14 times.
14 } else if (isBelowDecelThreshold) {
61 extraFuel = deltaTps * engineConfiguration->tpsDecelEnleanmentMultiplier;
62 m_timeSinceAccel.reset();
63 } else {
64 14 extraFuel = 0;
65 }
66
67 // Fractional enrichment (fuel portions are accumulated and split between several engine cycles.
68 // This is a crude imitation of carburetor's acceleration pump.
69
4/4
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 4 times.
✓ Branch 3 taken 31 times.
43 isFractionalEnrichment = engineConfiguration->tpsAccelFractionPeriod > 1 || engineConfiguration->tpsAccelFractionDivisor > 1.0f;
70
2/2
✓ Branch 0 taken 12 times.
✓ Branch 1 taken 31 times.
2/2
✓ Decision 'true' taken 12 times.
✓ Decision 'false' taken 31 times.
43 if (isFractionalEnrichment) {
71 // make sure both values are non-zero
72 12 float periodF = std::max<int>(engineConfiguration->tpsAccelFractionPeriod, 1);
73 12 float divisor = std::max(engineConfiguration->tpsAccelFractionDivisor, 1.0f);
74
75 // if current extra fuel portion is not "strong" enough, then we keep up the "pump pressure" with the accumulated portion
76 12 floatms_t maxExtraFuel = std::max(extraFuel, accumulatedValue);
77 // use only a fixed fraction of the accumulated portion
78 12 fractionalInjFuel = maxExtraFuel / divisor;
79
80 // update max counters
81 12 maxExtraPerCycle = std::max(extraFuel, maxExtraPerCycle);
82 12 maxInjectedPerPeriod = std::max(fractionalInjFuel, maxInjectedPerPeriod);
83
84 // evenly split it between several engine cycles
85 12 extraFuel = fractionalInjFuel / periodF;
86 } else {
87
1/1
✓ Branch 1 taken 31 times.
31 resetFractionValues();
88 }
89
90 86 float mult = interpolate2d(rpm, config->tpsTspCorrValuesBins,
91
1/1
✓ Branch 1 taken 43 times.
43 config->tpsTspCorrValues);
92
3/6
✓ Branch 0 taken 43 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 43 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 43 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 43 times.
43 if (mult != 0 && (mult < 0.01 || mult > 100)) {
93 mult = 1;
94 }
95
96 43 return extraFuel * mult;
97 }
98
99 2460 void TpsAccelEnrichment::onEngineCycleTps() {
100 // we update values in handleFuel() directly by calling onNewValue()
101
102 2460 onUpdateInvocationCounter++;
103
104 // we used some extra fuel during the current cycle, so we "charge" our "acceleration pump" with it
105 2460 accumulatedValue -= maxExtraPerPeriod;
106 2460 maxExtraPerPeriod = std::max(maxExtraPerCycle, maxExtraPerPeriod);
107 2460 maxExtraPerCycle = 0;
108 2460 accumulatedValue += maxExtraPerPeriod;
109
110 // update the accumulated value every 'Period' engine cycles
111 2460 isTimeToResetAccumulator = --cycleCnt <= 0;
112
2/2
✓ Branch 0 taken 2456 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2456 times.
✓ Decision 'false' taken 4 times.
2460 if (isTimeToResetAccumulator) {
113 2456 maxExtraPerPeriod = 0;
114
115 // we've injected this portion during the cycle, so we set what's left for the next cycle
116 2456 accumulatedValue -= maxInjectedPerPeriod;
117 2456 maxInjectedPerPeriod = 0;
118
119 // it's an infinitely convergent series, so we set a limit at some point
120 // (also make sure that accumulatedValue is positive, for safety)
121 static const floatms_t smallEpsilon = 0.001f;
122 2456 belowEpsilon = accumulatedValue < smallEpsilon;
123
2/2
✓ Branch 0 taken 2452 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2452 times.
✓ Decision 'false' taken 4 times.
2456 if (belowEpsilon) {
124 2452 accumulatedValue = 0;
125 }
126
127 // reset the counter
128 2456 cycleCnt = engineConfiguration->tpsAccelFractionPeriod;
129 }
130 2460 }
131
132 1120 int TpsAccelEnrichment::getMaxDeltaIndex() {
133 1120 int len = minI(cb.getSize(), cb.getCount());
134 1120 tooShort = len < 2;
135
2/2
✓ Branch 0 taken 591 times.
✓ Branch 1 taken 529 times.
2/2
✓ Decision 'true' taken 591 times.
✓ Decision 'false' taken 529 times.
1120 if (tooShort) {
136 591 return 0;
137 }
138 529 int ci = cb.currentIndex - 1;
139 529 float maxValue = cb.get(ci) - cb.get(ci - 1);
140 529 int resultIndex = ci;
141
142 // todo: 'get' method is maybe a bit heavy because of the branching
143 // todo: this could be optimized with some careful magic
144
145
2/2
✓ Branch 0 taken 851 times.
✓ Branch 1 taken 529 times.
2/2
✓ Decision 'true' taken 851 times.
✓ Decision 'false' taken 529 times.
1380 for (int i = 1; i<len - 1;i++) {
146 851 float v = cb.get(ci - i) - cb.get(ci - i - 1);
147
2/2
✓ Branch 0 taken 10 times.
✓ Branch 1 taken 841 times.
2/2
✓ Decision 'true' taken 10 times.
✓ Decision 'false' taken 841 times.
851 if (v > maxValue) {
148 10 maxValue = v;
149 10 resultIndex = ci - i;
150 }
151 }
152
153 529 return resultIndex;
154 }
155
156 7 float TpsAccelEnrichment::getMaxDelta() {
157 7 int index = getMaxDeltaIndex();
158
159 7 return (cb.get(index) - (cb.get(index - 1)));
160 }
161
162 682 void TpsAccelEnrichment::resetAE() {
163 682 cb.clear();
164 682 resetFractionValues();
165 682 }
166
167 713 void TpsAccelEnrichment::resetFractionValues() {
168 713 accumulatedValue = 0;
169 713 maxExtraPerCycle = 0;
170 713 maxExtraPerPeriod = 0;
171 713 maxInjectedPerPeriod = 0;
172 713 cycleCnt = 0;
173 713 }
174
175 5 void TpsAccelEnrichment::setLength(int length) {
176 5 cb.setSize(length);
177 5 }
178
179 1113 void TpsAccelEnrichment::onNewValue(float currentValue) {
180 // Push new value in to the history buffer
181 1113 cb.add(currentValue);
182
183 // Update deltas
184 1113 int maxDeltaIndex = getMaxDeltaIndex();
185 1113 tpsFrom = cb.get(maxDeltaIndex - 1);
186 1113 tpsTo = cb.get(maxDeltaIndex);
187 1113 deltaTps = tpsTo - tpsFrom;
188
189 // Update threshold detection
190 1113 isAboveAccelThreshold = deltaTps > engineConfiguration->tpsAccelEnrichmentThreshold;
191
192 // If an acceleration event just happened, latch the flag so it can be read once.
193
2/2
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 1102 times.
2/2
✓ Decision 'true' taken 11 times.
✓ Decision 'false' taken 1102 times.
1113 if (isAboveAccelThreshold) {
194 11 m_accelEventJustOccurred = true;
195 }
196
197 // TODO: can deltaTps actually be negative? Will this ever trigger?
198 1113 isBelowDecelThreshold = deltaTps < -engineConfiguration->tpsDecelEnleanmentThreshold;
199 1113 }
200
201 4 bool TpsAccelEnrichment::isAccelEventTriggered() {
202 // Read the flag
203 4 bool result = m_accelEventJustOccurred;
204 // Reset it so we only fire once per event
205 4 m_accelEventJustOccurred = false;
206 4 return result;
207 }
208
209 678 TpsAccelEnrichment::TpsAccelEnrichment() {
210 678 resetAE();
211 678 cb.setSize(4);
212 678 }
213
214 807 void TpsAccelEnrichment::onConfigurationChange(engine_configuration_s const* /*previousConfig*/) {
215 807 constexpr float slowCallbackPeriodSecond = SLOW_CALLBACK_PERIOD_MS / 1000.0f;
216 807 int length = engineConfiguration->tpsAccelLookback / slowCallbackPeriodSecond;
217
218
2/2
✓ Branch 0 taken 804 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 804 times.
✓ Decision 'false' taken 3 times.
807 if (length < 1) {
219 804 efiPrintf("setTpsAccelLen: Length should be positive [%d]", length);
220 804 return;
221 }
222
223 3 setLength(length);
224 }
225
226 1 float TpsAccelEnrichment::getTimeSinceAcell() const {
227 1 return m_timeSinceAccel.getElapsedSeconds();
228 }
229
230 586 void initAccelEnrichment() {
231 586 engine->module<TpsAccelEnrichment>()->onConfigurationChange(nullptr);
232 586 }
233