| Line | Branch | Decision | Exec | Source |
|---|---|---|---|---|
| 1 | /* | |||
| 2 | * @file wall_fuel.cpp | |||
| 3 | * | |||
| 4 | * @author Matthew Kennedy | |||
| 5 | */ | |||
| 6 | ||||
| 7 | #include "pch.h" | |||
| 8 | #include "wall_fuel.h" | |||
| 9 | ||||
| 10 | ✗ | void WallFuel::resetWF() { | ||
| 11 | ✗ | wallFuel = 0; | ||
| 12 | ✗ | } | ||
| 13 | ||||
| 14 | 9403 | float WallFuel::adjust(float desiredMassGrams) { | ||
| 15 | 9403 | invocationCounter++; | ||
| 16 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 9403 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 9403 times.
|
9403 | if (std::isnan(desiredMassGrams)) { |
| 17 | ✗ | return desiredMassGrams; | ||
| 18 | } | |||
| 19 | ||||
| 20 | 9403 | ScopePerf perf(PE::WallFuelAdjust); | ||
| 21 | ||||
| 22 | /* | |||
| 23 | this math is based on | |||
| 24 | SAE 810494 by C. F. Aquino | |||
| 25 | SAE 1999-01-0553 by Peter J Maloney | |||
| 26 | ||||
| 27 | M_cmd = commanded fuel mass (output of this function) | |||
| 28 | desiredMassGrams = desired fuel mass (input to this function) | |||
| 29 | fuelFilmMass = fuel film mass (how much is currently on the wall) | |||
| 30 | ||||
| 31 | First we compute how much fuel to command, by accounting for | |||
| 32 | a) how much fuel will evaporate from the walls, entering the air | |||
| 33 | b) how much fuel from the injector will hit the walls, being deposited | |||
| 34 | ||||
| 35 | Next, we compute how much fuel will be deposited on the walls. The net | |||
| 36 | effect of these two steps is computed (some leaves walls, some is deposited) | |||
| 37 | and stored back in fuelFilmMass. | |||
| 38 | ||||
| 39 | alpha describes the amount of fuel that REMAINS on the wall per cycle. | |||
| 40 | It is computed as a function of the evaporation time constant (tau) and | |||
| 41 | the time the fuel spent on the wall this cycle, (recriprocal RPM). | |||
| 42 | ||||
| 43 | beta describes the amount of fuel that hits the wall. | |||
| 44 | */ | |||
| 45 | ||||
| 46 | // If disabled, pass value through | |||
| 47 |
4/4✓ Branch 1 taken 9403 times.
✓ Branch 5 taken 9403 times.
✓ Branch 7 taken 9293 times.
✓ Branch 8 taken 110 times.
|
2/2✓ Decision 'true' taken 9293 times.
✓ Decision 'false' taken 110 times.
|
9403 | if (!engine->module<WallFuelController>()->getEnable()) { |
| 48 | 9293 | return desiredMassGrams; | ||
| 49 | } | |||
| 50 | ||||
| 51 |
2/2✓ Branch 1 taken 110 times.
✓ Branch 5 taken 110 times.
|
110 | float alpha = engine->module<WallFuelController>()->getAlpha(); | |
| 52 |
2/2✓ Branch 1 taken 110 times.
✓ Branch 5 taken 110 times.
|
110 | float beta = engine->module<WallFuelController>()->getBeta(); | |
| 53 | ||||
| 54 | 110 | float fuelFilmMass = wallFuel; | ||
| 55 | 110 | float M_cmd = (desiredMassGrams - (1 - alpha) * fuelFilmMass) / (1 - beta); | ||
| 56 | ||||
| 57 | // We can't inject a negative amount of fuel | |||
| 58 | // If this goes below zero we will be over-fueling slightly, | |||
| 59 | // but that's ok. | |||
| 60 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 110 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 110 times.
|
110 | if (M_cmd <= 0) { |
| 61 | ✗ | M_cmd = 0; | ||
| 62 | } | |||
| 63 | ||||
| 64 | // remainder on walls from last time + new from this time | |||
| 65 | 110 | float fuelFilmMassNext = alpha * fuelFilmMass + beta * M_cmd; | ||
| 66 | ||||
| 67 | 110 | wallFuel = fuelFilmMassNext; | ||
| 68 | 110 | wallFuelCorrection = M_cmd - desiredMassGrams; | ||
| 69 | 110 | return M_cmd; | ||
| 70 | } | |||
| 71 | ||||
| 72 | 531078 | float WallFuel::getWallFuel() const { | ||
| 73 | 531078 | return wallFuel; | ||
| 74 | } | |||
| 75 | ||||
| 76 | 955 | float WallFuelController::computeTau() const { | ||
| 77 |
1/2✓ Branch 0 taken 955 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 955 times.
✗ Decision 'false' not taken.
|
955 | if (!engineConfiguration->complexWallModel) { |
| 78 | 955 | return engineConfiguration->wwaeTau; | ||
| 79 | } | |||
| 80 | ||||
| 81 | // Default to normal operating temperature in case of | |||
| 82 | // CLT failure, this is not critical to get perfect | |||
| 83 | ✗ | float clt = Sensor::get(SensorType::Clt).value_or(90); | ||
| 84 | ||||
| 85 | ✗ | float tau = interpolate2d( | ||
| 86 | clt, | |||
| 87 | ✗ | config->wwCltBins, | ||
| 88 | ✗ | config->wwTauCltValues | ||
| 89 | ); | |||
| 90 | ||||
| 91 | // If you have a MAP sensor, apply MAP correction | |||
| 92 | ✗ | if (Sensor::hasSensor(SensorType::Map)) { | ||
| 93 | ✗ | auto map = Sensor::get(SensorType::Map).value_or(60); | ||
| 94 | ||||
| 95 | ✗ | tau *= interpolate2d( | ||
| 96 | map, | |||
| 97 | ✗ | config->wwMapBins, | ||
| 98 | ✗ | config->wwTauMapValues | ||
| 99 | ); | |||
| 100 | } | |||
| 101 | ||||
| 102 | ✗ | return tau; | ||
| 103 | } | |||
| 104 | ||||
| 105 | 955 | float WallFuelController::computeBeta() const { | ||
| 106 |
1/2✓ Branch 0 taken 955 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 955 times.
✗ Decision 'false' not taken.
|
955 | if (!engineConfiguration->complexWallModel) { |
| 107 | 955 | return engineConfiguration->wwaeBeta; | ||
| 108 | } | |||
| 109 | ||||
| 110 | // Default to normal operating temperature in case of | |||
| 111 | // CLT failure, this is not critical to get perfect | |||
| 112 | ✗ | float clt = Sensor::get(SensorType::Clt).value_or(90); | ||
| 113 | ||||
| 114 | ✗ | float beta = interpolate2d( | ||
| 115 | clt, | |||
| 116 | ✗ | config->wwCltBins, | ||
| 117 | ✗ | config->wwBetaCltValues | ||
| 118 | ); | |||
| 119 | ||||
| 120 | // If you have a MAP sensor, apply MAP correction | |||
| 121 | ✗ | if (Sensor::hasSensor(SensorType::Map)) { | ||
| 122 | ✗ | auto map = Sensor::get(SensorType::Map).value_or(60); | ||
| 123 | ||||
| 124 | ✗ | beta *= interpolate2d( | ||
| 125 | map, | |||
| 126 | ✗ | config->wwMapBins, | ||
| 127 | ✗ | config->wwBetaMapValues | ||
| 128 | ); | |||
| 129 | } | |||
| 130 | ||||
| 131 | // Clamp to 0..1 (you can't have more than 100% of the fuel hit the wall!) | |||
| 132 | ✗ | return clampF(0, beta, 1); | ||
| 133 | } | |||
| 134 | ||||
| 135 | 1101 | void WallFuelController::onFastCallback() { | ||
| 136 | // disable wall wetting cranking | |||
| 137 | // TODO: is this correct? Why not correct for cranking? | |||
| 138 |
2/2✓ Branch 1 taken 146 times.
✓ Branch 2 taken 955 times.
|
2/2✓ Decision 'true' taken 146 times.
✓ Decision 'false' taken 955 times.
|
1101 | if (engine->rpmCalculator.isCranking()) { |
| 139 | 146 | m_enable = false; | ||
| 140 | 146 | return; | ||
| 141 | } | |||
| 142 | ||||
| 143 | 955 | float tau = computeTau(); | ||
| 144 | 955 | float beta = computeBeta(); | ||
| 145 | ||||
| 146 | // if tau or beta is really small, we get div/0. | |||
| 147 | // you probably meant to disable wwae. | |||
| 148 |
3/4✓ Branch 0 taken 10 times.
✓ Branch 1 taken 945 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
1/2✓ Decision 'true' taken 955 times.
✗ Decision 'false' not taken.
|
955 | if (tau < 0.01f || beta < 0.01f) { |
| 149 | 955 | m_enable = false; | ||
| 150 | 955 | return; | ||
| 151 | } | |||
| 152 | ||||
| 153 | ✗ | auto rpm = Sensor::getOrZero(SensorType::Rpm); | ||
| 154 | ||||
| 155 | // Ignore low RPM | |||
| 156 | ✗ | if (rpm < 100) { | ||
| 157 | ✗ | m_enable = false; | ||
| 158 | ✗ | return; | ||
| 159 | } | |||
| 160 | ||||
| 161 | ✗ | float alpha = expf_taylor(-120 / (rpm * tau)); | ||
| 162 | ||||
| 163 | // If beta is larger than alpha, the system is underdamped. | |||
| 164 | // For reasonable values {tau, beta}, this should only be possible | |||
| 165 | // at extremely low engine speeds (<300rpm ish) | |||
| 166 | // Clamp beta to less than alpha. | |||
| 167 | ✗ | if (beta > alpha) { | ||
| 168 | ✗ | beta = alpha; | ||
| 169 | } | |||
| 170 | ||||
| 171 | // Store parameters so the model can read them | |||
| 172 | ✗ | m_alpha = alpha; | ||
| 173 | ✗ | m_beta = beta; | ||
| 174 | ✗ | m_enable = true; | ||
| 175 | } | |||
| 176 |