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 | 8625 | float WallFuel::adjust(float desiredMassGrams) { | ||
15 | 8625 | invocationCounter++; | ||
16 |
1/2✗ Branch 1 not taken.
✓ Branch 2 taken 8625 times.
|
1/2✗ Decision 'true' not taken.
✓ Decision 'false' taken 8625 times.
|
8625 | if (std::isnan(desiredMassGrams)) { |
17 | ✗ | return desiredMassGrams; | ||
18 | } | |||
19 | ||||
20 | 8625 | 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 8625 times.
✓ Branch 5 taken 8625 times.
✓ Branch 7 taken 8515 times.
✓ Branch 8 taken 110 times.
|
2/2✓ Decision 'true' taken 8515 times.
✓ Decision 'false' taken 110 times.
|
8625 | if (!engine->module<WallFuelController>()->getEnable()) { |
48 | 8515 | 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 | 522954 | float WallFuel::getWallFuel() const { | ||
73 | 522954 | return wallFuel; | ||
74 | } | |||
75 | ||||
76 | 975 | float WallFuelController::computeTau() const { | ||
77 |
1/2✓ Branch 0 taken 975 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 975 times.
✗ Decision 'false' not taken.
|
975 | if (!engineConfiguration->complexWallModel) { |
78 | 975 | 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 | 975 | float WallFuelController::computeBeta() const { | ||
106 |
1/2✓ Branch 0 taken 975 times.
✗ Branch 1 not taken.
|
1/2✓ Decision 'true' taken 975 times.
✗ Decision 'false' not taken.
|
975 | if (!engineConfiguration->complexWallModel) { |
107 | 975 | 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 | 1120 | void WallFuelController::onFastCallback() { | ||
136 | // disable wall wetting cranking | |||
137 | // TODO: is this correct? Why not correct for cranking? | |||
138 |
2/2✓ Branch 1 taken 145 times.
✓ Branch 2 taken 975 times.
|
2/2✓ Decision 'true' taken 145 times.
✓ Decision 'false' taken 975 times.
|
1120 | if (engine->rpmCalculator.isCranking()) { |
139 | 145 | m_enable = false; | ||
140 | 145 | return; | ||
141 | } | |||
142 | ||||
143 | 975 | float tau = computeTau(); | ||
144 | 975 | 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 965 times.
✓ Branch 2 taken 10 times.
✗ Branch 3 not taken.
|
1/2✓ Decision 'true' taken 975 times.
✗ Decision 'false' not taken.
|
975 | if (tau < 0.01f || beta < 0.01f) { |
149 | 975 | m_enable = false; | ||
150 | 975 | 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 |