GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/fuel/fuel_computer.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 80.4% 37 0 46
Functions: 100.0% 5 0 5
Branches: 41.2% 7 0 17
Decisions: 41.2% 7 - 17

Line Branch Decision Exec Source
1 #include "pch.h"
2
3 #include "engine_configuration.h"
4 #include "sensor.h"
5 #include "error_handling.h"
6 #include "efi_interpolation.h"
7 #include "table_helper.h"
8 #include "fuel_math.h"
9 #include "fuel_computer.h"
10
11 #if EFI_ENGINE_CONTROL
12
13 1126 mass_t FuelComputerBase::getCycleFuel(mass_t airmass, float rpm, float load) {
14 1126 load = getTargetLambdaLoadAxis(load);
15
16 1126 float stoich = getStoichiometricRatio();
17 1126 float lambda = getTargetLambda(rpm, load);
18 1126 float afr = stoich * lambda;
19
20 1126 afrTableYAxis = load;
21 1126 targetLambda = lambda;
22 1126 targetAFR = afr;
23 1126 stoichiometricRatio = stoich;
24
25 1126 return airmass / afr;
26 }
27
28 1131 float FuelComputer::getStoichiometricRatio() const {
29 1131 float primary = engineConfiguration->stoichRatioPrimary;
30
31
32
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1131 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1131 times.
1131 if (primary < 5) {
33 // Config compatibility: this field may be zero on ECUs with very old defaults
34 criticalError("Please set stoichRatioPrimary");
35 return 0;
36 }
37
38 // Without an ethanol/flex sensor, return primary configured stoich ratio
39
2/2
✓ Branch 1 taken 1126 times.
✓ Branch 2 taken 5 times.
2/2
✓ Decision 'true' taken 1126 times.
✓ Decision 'false' taken 5 times.
1131 if (!Sensor::hasSensor(SensorType::FuelEthanolPercent)) {
40 1126 return primary;
41 }
42
43 5 float secondary = engineConfiguration->stoichRatioSecondary;
44
45
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 5 times.
5 if (secondary < 5) {
46 // Config compatibility: this field may be zero on ECUs with very old defaults
47 criticalError("Please set stoichRatioSecondary");
48 return 0;
49 }
50
51 5 auto flex = Sensor::get(SensorType::FuelEthanolPercent);
52
53 // TODO: what do do if flex sensor fails?
54
55 // Linear interpolate between primary and secondary stoich ratios
56 5 return interpolateClamped(0, primary, 100, secondary, flex.Value);
57 }
58
59
60 1125 float FuelComputer::getTargetLambda(float rpm, float load) const {
61 2250 float target = interpolate3d(
62 1125 config->lambdaTable,
63 1125 config->lambdaLoadBins, load,
64 1125 config->lambdaRpmBins, rpm
65 );
66
67 // Add any blends if configured
68
2/2
✓ Branch 1 taken 2250 times.
✓ Branch 2 taken 1125 times.
2/2
✓ Decision 'true' taken 2250 times.
✓ Decision 'false' taken 1125 times.
3375 for (size_t i = 0; i < efi::size(config->targetAfrBlends); i++) {
69 2250 auto result = calculateBlend(config->targetAfrBlends[i], rpm, load);
70
71 2250 engine->outputChannels.targetAfrBlendParameter[i] = result.BlendParameter;
72 2250 engine->outputChannels.targetAfrBlendBias[i] = result.Bias;
73 2250 engine->outputChannels.targetAfrBlendOutput[i] = result.Value;
74 2250 engine->outputChannels.targetAfrBlendYAxis[i] = result.TableYAxis;
75
76 2250 target += result.Value;
77 }
78
79 1125 return target;
80 }
81
82 1125 float FuelComputer::getTargetLambdaLoadAxis(float defaultLoad) const {
83 1125 return getLoadOverride(defaultLoad, engineConfiguration->afrOverrideMode);
84 }
85
86 2250 float IFuelComputer::getLoadOverride(float defaultLoad, load_override_e overrideMode) const {
87
1/6
✓ Branch 0 taken 2250 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
2250 switch(overrideMode) {
88
1/1
✓ Decision 'true' taken 2250 times.
2250 case AFR_None: return defaultLoad;
89 // MAP default to 200kpa - failed MAP goes rich
90 case AFR_MAP: return Sensor::get(SensorType::Map).value_or(200);
91 // TPS/pedal default to 100% - failed TPS goes rich
92 case AFR_Tps: return Sensor::get(SensorType::Tps1).value_or(100);
93 case AFR_AccPedal: return Sensor::get(SensorType::AcceleratorPedal).value_or(100);
94 case AFR_CylFilling: return normalizedCylinderFilling;
95 default: return 0;
96 }
97 }
98
99 #endif // EFI_ENGINE_CONTROL
100