| 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 |