GCC Code Coverage Report


Directory: ./
File: firmware/init/sensor/init_tps.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 96.0% 120 0 125
Functions: 100.0% 15 0 15
Branches: 69.0% 40 0 58
Decisions: 76.7% 23 - 30

Line Branch Decision Exec Source
1 #include "pch.h"
2
3 #include "adc_subscription.h"
4 #include "functional_sensor.h"
5 #include "redundant_sensor.h"
6 #include "redundant_ford_tps.h"
7 #include "proxy_sensor.h"
8 #include "linear_func.h"
9 #include "tps.h"
10 #include "auto_generated_sensor.h"
11 #include "defaults.h"
12
13 struct TpsConfig {
14 adc_channel_e channel;
15 float closed;
16 float open;
17 float min;
18 float max;
19 };
20
21 35 PUBLIC_API_WEAK float getFuncPairAllowedSplit() {
22 35 return 0.5f;
23 }
24
25 class FuncSensPair {
26 public:
27 AdcSubscriptionEntry *adc = nullptr;
28 8 FuncSensPair(float divideInput, SensorType type)
29 8 : m_func(divideInput)
30 8 , m_sens(type, MS2NT(10))
31 {
32 8 m_sens.setFunction(m_func);
33 8 }
34
35 113 bool init(const TpsConfig& cfg) {
36 // If the configuration was invalid, don't continue to configure the sensor
37
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 33 times.
2/2
✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 33 times.
113 if (!configure(cfg)) {
38 78 return false;
39 }
40
41 33 adc = AdcSubscription::SubscribeSensor(m_sens, cfg.channel, /*lowpassCutoffHz*/ 200);
42
43 33 return m_sens.Register();
44 }
45
46 16 void deinit() {
47 16 AdcSubscription::UnsubscribeSensor(m_sens);
48 16 }
49
50 6 SensorType type() const {
51 6 return m_sens.type();
52 }
53
54 113 const char* name() const {
55 113 return m_sens.getSensorName();
56 }
57
58 private:
59 113 bool configure(const TpsConfig& cfg) {
60 // Only configure if we have a channel
61
2/2
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 35 times.
2/2
✓ Decision 'true' taken 78 times.
✓ Decision 'false' taken 35 times.
113 if (!isAdcChannelValid(cfg.channel)) {
62 #if EFI_UNIT_TEST
63 78 printf("Configured NO hardware %s\n", name());
64 #endif
65 78 return false;
66 }
67
68 35 float scaledClosed = cfg.closed / m_func.getDivideInput();
69 35 float scaledOpen = cfg.open / m_func.getDivideInput();
70
71 35 float split = std::abs(scaledOpen - scaledClosed);
72
73 // If the voltage for closed vs. open is very near, something is wrong with your calibration
74
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 33 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 33 times.
35 if (split < getFuncPairAllowedSplit()) {
75 2 firmwareError(ObdCode::OBD_TPS_Configuration, "\"%s\" problem: open %.2f/closed %.2f cal values are too close together. Check your calibration and wiring!", name(),
76 2 cfg.open,
77 2 cfg.closed);
78 return false;
79 }
80
81 33 m_func.configure(
82 33 cfg.closed, 0,
83 33 cfg.open, POSITION_FULLY_OPEN,
84 33 cfg.min, cfg.max
85 );
86
87 #if EFI_UNIT_TEST
88 33 printf("Configured YES %s\n", name());
89 #endif
90 33 return true;
91 }
92
93 LinearFunc m_func;
94 FunctionalSensor m_sens;
95 };
96
97 struct RedundantPair {
98 public:
99 3 RedundantPair(FuncSensPair& pri, FuncSensPair& sec, SensorType outputType)
100 3 : m_pri(pri)
101 3 , m_sec(sec)
102 3 , m_redund(outputType, m_pri.type(), m_sec.type())
103 {
104 3 }
105
106 55 void init(bool isFordTps, RedundantFordTps* fordTps, float secondaryMaximum, const TpsConfig& primary, const TpsConfig& secondary, bool allowIdenticalSensors = false) {
107 55 bool hasFirst = m_pri.init(primary);
108
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 22 times.
2/2
✓ Decision 'true' taken 31 times.
✓ Decision 'false' taken 22 times.
53 if (!hasFirst) {
109 // no input if we have no first channel
110 31 return;
111 }
112
113
1/2
✓ Branch 0 taken 22 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 22 times.
✗ Decision 'false' not taken.
22 if (!allowIdenticalSensors) {
114 // Check that the primary and secondary aren't too close together - if so, the user may have done
115 // an unsafe thing where they wired a single sensor to both inputs. Don't do that!
116
3/4
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 11 times.
✓ Branch 5 taken 11 times.
22 bool hasBothSensors = isAdcChannelValid(primary.channel) && isAdcChannelValid(secondary.channel);
117 22 bool tooCloseClosed = std::abs(primary.closed - secondary.closed) < 0.2f;
118 22 bool tooCloseOpen = std::abs(primary.open - secondary.open) < 0.2f;
119
120
3/6
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 11 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 11 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 22 times.
22 if (hasBothSensors && tooCloseClosed && tooCloseOpen) {
121 firmwareError(ObdCode::OBD_TPS_Configuration, "Configuration for redundant pair %s/%s are too similar - did you wire one sensor to both inputs...?", m_pri.name(), m_sec.name());
122 return;
123 }
124 }
125
126 22 bool hasSecond = m_sec.init(secondary);
127
128
3/6
✓ Branch 1 taken 22 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 22 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 22 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 22 times.
22 if (engineConfiguration->etbSplit <= 0 || engineConfiguration->etbSplit > MAX_TPS_PPS_DISCREPANCY) {
129 engineConfiguration->etbSplit = MAX_TPS_PPS_DISCREPANCY;
130 }
131
132
3/4
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 18 times.
✓ Branch 2 taken 4 times.
✗ Branch 3 not taken.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 18 times.
22 if (isFordTps && fordTps) {
133 // we have a secondary
134 4 fordTps->configure(engineConfiguration->etbSplit, secondaryMaximum);
135 4 fordTps->Register();
136 } else {
137 // not ford TPS
138 18 m_redund.configure(engineConfiguration->etbSplit, !hasSecond);
139 #if EFI_UNIT_TEST
140 18 printf("init m_redund.Register() %s\n", getSensorType(m_redund.type()));
141 #endif
142 18 m_redund.Register();
143 }
144 }
145
146 6 void deinit(bool isFordTps, RedundantFordTps* fordTps) {
147 6 m_pri.deinit();
148 6 m_sec.deinit();
149
150
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 6 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 6 times.
6 if (isFordTps && fordTps) {
151 fordTps->unregister();
152 } else {
153 6 m_redund.unregister();
154 }
155
156 6 }
157
158 // technical debt: oop violation: this method is specific to PPS usage
159 522954 void updateUnfilteredRawValues() {
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522954 times.
522954 engine->outputChannels.rawRawPpsPrimary = m_pri.adc == nullptr ? 0 : m_pri.adc->sensorVolts;
161
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 522954 times.
522954 engine->outputChannels.rawRawPpsSecondary = m_sec.adc == nullptr ? 0 : m_sec.adc->sensorVolts;
162 522954 }
163
164 private:
165 FuncSensPair& m_pri;
166 FuncSensPair& m_sec;
167
168 RedundantSensor m_redund;
169 };
170
171 static FuncSensPair tps1p(TPS_TS_CONVERSION, SensorType::Tps1Primary);
172 static FuncSensPair tps1s(TPS_TS_CONVERSION, SensorType::Tps1Secondary);
173 static FuncSensPair tps2p(TPS_TS_CONVERSION, SensorType::Tps2Primary);
174 static FuncSensPair tps2s(TPS_TS_CONVERSION, SensorType::Tps2Secondary);
175
176 // Used in case of "normal", non-Ford ETB TPS
177 static RedundantPair analogTps1(tps1p, tps1s, SensorType::Tps1);
178 static RedundantPair tps2(tps2p, tps2s, SensorType::Tps2);
179
180 #if EFI_SENT_SUPPORT
181 SentTps sentTps;
182 #endif
183
184 // Used only in case of weird Ford-style ETB TPS
185 static RedundantFordTps fordTps1(SensorType::Tps1, SensorType::Tps1Primary, SensorType::Tps1Secondary);
186 static RedundantFordTps fordTps2(SensorType::Tps2, SensorType::Tps2Primary, SensorType::Tps2Secondary);
187 static RedundantFordTps fordPps(SensorType::AcceleratorPedalUnfiltered, SensorType::AcceleratorPedalPrimary, SensorType::AcceleratorPedalSecondary);
188
189 // Pedal sensors and redundancy
190 static FuncSensPair pedalPrimary(1, SensorType::AcceleratorPedalPrimary);
191 static FuncSensPair pedalSecondary(1, SensorType::AcceleratorPedalSecondary);
192 static RedundantPair pedal(pedalPrimary, pedalSecondary, SensorType::AcceleratorPedalUnfiltered);
193
194 522954 void updateUnfilteredRawPedal() {
195 522954 pedal.updateUnfilteredRawValues();
196 522954 }
197
198 // This sensor indicates the driver's throttle intent - Pedal if we have one, TPS if not.
199 static ProxySensor driverIntent(SensorType::DriverThrottleIntent);
200 static ProxySensor ppsFilterSensor(SensorType::AcceleratorPedal);
201
202 // These sensors are TPS-like, so handle them in here too
203 static FuncSensPair wastegate(1, SensorType::WastegatePosition);
204 static FuncSensPair idlePos(PACK_MULT_VOLTAGE, SensorType::IdlePosition);
205
206 20 void initTps() {
207
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 20 times.
20 criticalAssertVoid(engineConfiguration != nullptr, "null engineConfiguration");
208 20 percent_t minTpsPps = engineConfiguration->tpsErrorDetectionTooLow;
209 20 percent_t maxTpsPps = engineConfiguration->tpsErrorDetectionTooHigh;
210
211
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 20 times.
✗ Decision 'false' not taken.
20 if (!engineConfiguration->consumeObdSensors) {
212 20 bool isFordTps = engineConfiguration->useFordRedundantTps;
213 20 bool isFordPps = engineConfiguration->useFordRedundantPps;
214
215 20 float tpsSecondaryMaximum = engineConfiguration->tpsSecondaryMaximum;
216
1/2
✓ Branch 0 taken 20 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 20 times.
✗ Decision 'false' not taken.
20 if (tpsSecondaryMaximum < 20) {
217 // don't allow <20% split point
218 20 tpsSecondaryMaximum = 20;
219 }
220
221
222 #if EFI_SENT_SUPPORT
223
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 19 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 19 times.
20 if (isDigitalTps1()) {
224 1 sentTps.Register();
225 } else
226 #endif
227 {
228 40 analogTps1.init(isFordTps, &fordTps1, tpsSecondaryMaximum,
229
1/1
✓ Branch 1 taken 17 times.
19 { engineConfiguration->tps1_1AdcChannel, (float)engineConfiguration->tpsMin, (float)engineConfiguration->tpsMax, minTpsPps, maxTpsPps },
230 19 { engineConfiguration->tps1_2AdcChannel, (float)engineConfiguration->tps1SecondaryMin, (float)engineConfiguration->tps1SecondaryMax, minTpsPps, maxTpsPps }
231 );
232 }
233
234 36 tps2.init(isFordTps, &fordTps2, tpsSecondaryMaximum,
235
1/1
✓ Branch 1 taken 18 times.
18 { engineConfiguration->tps2_1AdcChannel, (float)engineConfiguration->tps2Min, (float)engineConfiguration->tps2Max, minTpsPps, maxTpsPps },
236 18 { engineConfiguration->tps2_2AdcChannel, (float)engineConfiguration->tps2SecondaryMin, (float)engineConfiguration->tps2SecondaryMax, minTpsPps, maxTpsPps }
237 );
238
239 18 float ppsSecondaryMaximum = engineConfiguration->ppsSecondaryMaximum;
240
1/2
✓ Branch 0 taken 18 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 18 times.
✗ Decision 'false' not taken.
18 if (ppsSecondaryMaximum < 20) {
241 // don't allow <20% split point
242 18 ppsSecondaryMaximum = 20;
243 }
244
245 // Pedal sensors
246 18 pedal.init(isFordPps, &fordPps, ppsSecondaryMaximum,
247
1/1
✓ Branch 1 taken 18 times.
18 { engineConfiguration->throttlePedalPositionAdcChannel, engineConfiguration->throttlePedalUpVoltage, engineConfiguration->throttlePedalWOTVoltage, minTpsPps, maxTpsPps },
248 18 { engineConfiguration->throttlePedalPositionSecondAdcChannel, engineConfiguration->throttlePedalSecondaryUpVoltage, engineConfiguration->throttlePedalSecondaryWOTVoltage, minTpsPps, maxTpsPps },
249 18 engineConfiguration->allowIdenticalPps
250 );
251 18 ppsFilterSensor.setProxiedSensor(SensorType::AcceleratorPedalUnfiltered);
252 18 ppsFilterSensor.setConverter([](SensorResult arg) {
253
2/2
✓ Branch 1 taken 205 times.
✓ Branch 2 taken 2 times.
2/2
✓ Decision 'true' taken 205 times.
✓ Decision 'false' taken 2 times.
207 if (!arg) {
254 205 return arg;
255 }
256 static ExpAverage ppsExpAverage;
257 2 ppsExpAverage.setSmoothingFactor(engineConfiguration->ppsExpAverageAlpha);
258
1/1
✓ Branch 2 taken 2 times.
2 SensorResult result = ppsExpAverage.initOrAverage(arg.Value);
259 2 return result;
260 });
261 18 ppsFilterSensor.Register();
262
263 // TPS-like stuff that isn't actually a TPS
264
1/1
✓ Branch 2 taken 18 times.
18 wastegate.init({ engineConfiguration->wastegatePositionSensor, engineConfiguration->wastegatePositionClosedVoltage, engineConfiguration->wastegatePositionOpenedVoltage, minTpsPps, maxTpsPps });
265
1/1
✓ Branch 2 taken 18 times.
18 idlePos.init({ engineConfiguration->idlePositionChannel, (float)engineConfiguration->idlePositionMin, (float)engineConfiguration->idlePositionMax, minTpsPps, maxTpsPps });
266 }
267
268 // Route the pedal or TPS to driverIntent as appropriate
269
2/2
✓ Branch 1 taken 8 times.
✓ Branch 2 taken 10 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 10 times.
18 if (isAdcChannelValid(engineConfiguration->throttlePedalPositionAdcChannel)) {
270 8 driverIntent.setProxiedSensor(SensorType::AcceleratorPedal);
271 } else {
272 10 driverIntent.setProxiedSensor(SensorType::Tps1);
273 }
274
275 18 driverIntent.Register();
276 }
277
278 2 void deinitTps() {
279 2 bool isFordTps = activeConfiguration.useFordRedundantTps;
280 2 bool isFordPps = activeConfiguration.useFordRedundantPps;
281
282 2 analogTps1.deinit(isFordTps, &fordTps1);
283 2 tps2.deinit(isFordTps, &fordTps2);
284 2 pedal.deinit(isFordPps, &fordPps);
285
286 #if EFI_SENT_SUPPORT
287 2 sentTps.unregister();
288 #endif
289
290 2 wastegate.deinit();
291 2 idlePos.deinit();
292 2 }
293