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 |