GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/accel_enrichment.cpp
Date: 2025-10-03 00:57:22
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 1162 float TpsAccelEnrichment::getTpsEnrichment() {
31 1162 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 1161 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 1161 times.
1162 if (engineConfiguration->accelEnrichmentMode == AE_MODE_PREDICTIVE_MAP) {
35 1 return 0;
36 }
37
38
2/2
✓ Branch 1 taken 1118 times.
✓ Branch 2 taken 43 times.
2/2
✓ Decision 'true' taken 1118 times.
✓ Decision 'false' taken 43 times.
1161 if (engineConfiguration->tpsAccelLookback == 0) {
39 // If disabled, return 0.
40 1118 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 2266 void TpsAccelEnrichment::onEngineCycleTps() {
100 // we update values in handleFuel() directly by calling onNewValue()
101
102 2266 onUpdateInvocationCounter++;
103
104 // we used some extra fuel during the current cycle, so we "charge" our "acceleration pump" with it
105 2266 accumulatedValue -= maxExtraPerPeriod;
106 2266 maxExtraPerPeriod = std::max(maxExtraPerCycle, maxExtraPerPeriod);
107 2266 maxExtraPerCycle = 0;
108 2266 accumulatedValue += maxExtraPerPeriod;
109
110 // update the accumulated value every 'Period' engine cycles
111 2266 isTimeToResetAccumulator = --cycleCnt <= 0;
112
2/2
✓ Branch 0 taken 2262 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2262 times.
✓ Decision 'false' taken 4 times.
2266 if (isTimeToResetAccumulator) {
113 2262 maxExtraPerPeriod = 0;
114
115 // we've injected this portion during the cycle, so we set what's left for the next cycle
116 2262 accumulatedValue -= maxInjectedPerPeriod;
117 2262 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 2262 belowEpsilon = accumulatedValue < smallEpsilon;
123
2/2
✓ Branch 0 taken 2258 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2258 times.
✓ Decision 'false' taken 4 times.
2262 if (belowEpsilon) {
124 2258 accumulatedValue = 0;
125 }
126
127 // reset the counter
128 2262 cycleCnt = engineConfiguration->tpsAccelFractionPeriod;
129 }
130 2266 }
131
132 1119 int TpsAccelEnrichment::getMaxDeltaIndex() {
133 1119 int len = minI(cb.getSize(), cb.getCount());
134 1119 tooShort = len < 2;
135
2/2
✓ Branch 0 taken 590 times.
✓ Branch 1 taken 529 times.
2/2
✓ Decision 'true' taken 590 times.
✓ Decision 'false' taken 529 times.
1119 if (tooShort) {
136 590 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 681 void TpsAccelEnrichment::resetAE() {
163 681 cb.clear();
164 681 resetFractionValues();
165 681 }
166
167 712 void TpsAccelEnrichment::resetFractionValues() {
168 712 accumulatedValue = 0;
169 712 maxExtraPerCycle = 0;
170 712 maxExtraPerPeriod = 0;
171 712 maxInjectedPerPeriod = 0;
172 712 cycleCnt = 0;
173 712 }
174
175 5 void TpsAccelEnrichment::setLength(int length) {
176 5 cb.setSize(length);
177 5 }
178
179 1112 void TpsAccelEnrichment::onNewValue(float currentValue) {
180 // Push new value in to the history buffer
181 1112 cb.add(currentValue);
182
183 // Update deltas
184 1112 int maxDeltaIndex = getMaxDeltaIndex();
185 1112 tpsFrom = cb.get(maxDeltaIndex - 1);
186 1112 tpsTo = cb.get(maxDeltaIndex);
187 1112 deltaTps = tpsTo - tpsFrom;
188
189 // Update threshold detection
190 1112 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 1101 times.
2/2
✓ Decision 'true' taken 11 times.
✓ Decision 'false' taken 1101 times.
1112 if (isAboveAccelThreshold) {
194 11 m_accelEventJustOccurred = true;
195 }
196
197 // TODO: can deltaTps actually be negative? Will this ever trigger?
198 1112 isBelowDecelThreshold = deltaTps < -engineConfiguration->tpsDecelEnleanmentThreshold;
199 1112 }
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 677 TpsAccelEnrichment::TpsAccelEnrichment() {
210 677 resetAE();
211 677 cb.setSize(4);
212 677 }
213
214 804 void TpsAccelEnrichment::onConfigurationChange(engine_configuration_s const* /*previousConfig*/) {
215 804 constexpr float slowCallbackPeriodSecond = SLOW_CALLBACK_PERIOD_MS / 1000.0f;
216 804 int length = engineConfiguration->tpsAccelLookback / slowCallbackPeriodSecond;
217
218
2/2
✓ Branch 0 taken 801 times.
✓ Branch 1 taken 3 times.
2/2
✓ Decision 'true' taken 801 times.
✓ Decision 'false' taken 3 times.
804 if (length < 1) {
219 801 efiPrintf("setTpsAccelLen: Length should be positive [%d]", length);
220 801 return;
221 }
222
223 3 setLength(length);
224 }
225
226 1 float TpsAccelEnrichment::getTimeSinceAcell() const {
227 1 return m_timeSinceAccel.getElapsedSeconds();
228 }
229
230 585 void initAccelEnrichment() {
231 585 engine->module<TpsAccelEnrichment>()->onConfigurationChange(nullptr);
232 585 }
233