GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/airmass/speed_density_airmass.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 93.2% 55 0 59
Functions: 100.0% 6 0 6
Branches: 91.4% 32 0 35
Decisions: 87.5% 21 - 24

Line Branch Decision Exec Source
1 #include "pch.h"
2 #include "speed_density_airmass.h"
3 #include "accel_enrichment.h"
4
5
6 1 AirmassResult SpeedDensityAirmass::getAirmass(float rpm, bool postState) {
7 1 ScopePerf perf(PE::GetSpeedDensityFuel);
8
9
1/1
✓ Branch 1 taken 1 time.
1 auto map = getMap(rpm, postState);
10
11
1/1
✓ Branch 1 taken 1 time.
2 return getAirmass(rpm, map, postState);
12 }
13
14 1086 AirmassResult SpeedDensityAirmass::getAirmass(float rpm, float map, bool postState) {
15 /**
16 * most of the values are pre-calculated for performance reasons
17 */
18 1086 float tChargeK = engine->engineState.sd.tChargeK;
19
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1086 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1086 times.
1086 if (std::isnan(tChargeK)) {
20 warning(ObdCode::CUSTOM_ERR_TCHARGE_NOT_READY2, "tChargeK not ready"); // this would happen before we have CLT reading for example
21 return {};
22 }
23
24 1086 float ve = getVe(rpm, map, postState);
25
26 1086 float airMass = getAirmassImpl(ve, map, tChargeK);
27
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1086 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1086 times.
1086 if (std::isnan(airMass)) {
28 warning(ObdCode::CUSTOM_ERR_6685, "NaN airMass");
29 return {};
30 }
31 #if EFI_PRINTF_FUEL_DETAILS
32 1086 printf("getSpeedDensityAirmass map=%.2f\n", map);
33 #endif /*EFI_PRINTF_FUEL_DETAILS */
34
35 return {
36 airMass,
37 map, // AFR/VE table Y axis
38 1086 };
39 }
40
41 1085 float SpeedDensityAirmass::getAirflow(float rpm, float map, bool postState) {
42 1085 auto airmassResult = getAirmass(rpm, map, postState);
43
44 1085 float massPerCycle = airmassResult.CylinderAirmass * engineConfiguration->cylindersCount;
45
46
2/2
✓ Branch 0 taken 1083 times.
✓ Branch 1 taken 2 times.
2/2
✓ Decision 'true' taken 1083 times.
✓ Decision 'false' taken 2 times.
1085 if (!engineConfiguration->twoStroke) {
47 // 4 stroke engines only do a half cycle per rev
48 1083 massPerCycle = massPerCycle / 2;
49 }
50
51 // g/s
52 1085 return massPerCycle * rpm / 60;
53 }
54
55 9 float SpeedDensityAirmass::getPredictiveMap(float rpm, bool postState, float mapSensor) {
56 18 float blendDuration = interpolate2d(rpm, config->predictiveMapBlendDurationBins,
57 9 config->predictiveMapBlendDurationValues);
58
59 9 float elapsedTime = m_predictionTimer.getElapsedSeconds();
60
61
4/4
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 3 times.
✓ Branch 2 taken 1 time.
✓ Branch 3 taken 5 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 8 times.
9 if (m_isMapPredictionActive && elapsedTime >= blendDuration) {
62 // prediction phase is over
63 1 m_isMapPredictionActive = false;
64 }
65
66 9 float effectiveMap = 0;
67
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 5 times.
✓ Decision 'false' taken 4 times.
9 if (m_isMapPredictionActive) {
68 5 float blendFactor = elapsedTime / blendDuration;
69 // Linearly interpolate between the initial predicted and real values
70 5 effectiveMap = m_initialPredictedMap + (m_initialRealMap - m_initialPredictedMap) * blendFactor;
71
72
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 4 times.
5 if (mapSensor >= effectiveMap) {
73 1 m_isMapPredictionActive = false;
74 }
75 } else {
76
2/2
✓ Branch 3 taken 2 times.
✓ Branch 4 taken 2 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 2 times.
4 if (engine->module<TpsAccelEnrichment>()->isAccelEventTriggered()) {
77 2 float predictedMap = logAndGetFallback(rpm, postState);
78
79
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
1/2
✓ Decision 'true' taken 2 times.
✗ Decision 'false' not taken.
2 if (predictedMap > mapSensor) {
80 2 m_isMapPredictionActive = true;
81 2 engine->module<TpsAccelEnrichment>()->m_timeSinceAccel.reset();
82 2 m_predictionTimer.reset();
83 2 m_initialPredictedMap = predictedMap;
84 2 m_initialRealMap = mapSensor;
85 2 effectiveMap = predictedMap;
86 }
87 }
88 }
89 9 engine->outputChannels.isMapPredictionActive = m_isMapPredictionActive;
90
91
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 6 times.
2/2
✓ Decision 'true' taken 3 times.
✓ Decision 'false' taken 6 times.
9 if (!m_isMapPredictionActive) {
92 3 effectiveMap = mapSensor;
93 }
94
95 #if EFI_TUNER_STUDIO
96
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
2/2
✓ Decision 'true' taken 1 time.
✓ Decision 'false' taken 8 times.
9 if (postState) {
97 1 engine->outputChannels.effectiveMap = effectiveMap;
98 }
99 #endif // EFI_TUNER_STUDIO
100
101 9 return effectiveMap;
102 }
103
104 6 float SpeedDensityAirmass::logAndGetFallback(float rpm, bool postState) const {
105 6 float fallbackMap = m_mapEstimationTable->getValue(rpm, Sensor::getOrZero(SensorType::Tps1));
106 #if EFI_TUNER_STUDIO
107
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 4 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 4 times.
6 if (postState) {
108 2 engine->outputChannels.fallbackMap = fallbackMap;
109 }
110 #endif // EFI_TUNER_STUDIO
111 6 return fallbackMap;
112 }
113
114 13 float SpeedDensityAirmass::getMap(float rpm, bool postState) {
115
1/1
✓ Branch 2 taken 13 times.
13 auto mapSensor = Sensor::get(SensorType::Map);
116
6/6
✓ Branch 1 taken 11 times.
✓ Branch 2 taken 2 times.
✓ Branch 3 taken 9 times.
✓ Branch 4 taken 2 times.
✓ Branch 5 taken 9 times.
✓ Branch 6 taken 4 times.
2/2
✓ Decision 'true' taken 9 times.
✓ Decision 'false' taken 4 times.
13 if (mapSensor && engineConfiguration->accelEnrichmentMode == AE_MODE_PREDICTIVE_MAP) {
117
1/1
✓ Branch 1 taken 9 times.
9 return getPredictiveMap(rpm, postState, mapSensor.Value);
118 }
119
1/1
✓ Branch 1 taken 4 times.
4 return mapSensor.value_or(logAndGetFallback(rpm, postState));
120 }
121