GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/fuel/dfco.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 92.3% 48 0 52
Functions: 100.0% 5 0 5
Branches: 82.3% 51 0 62
Decisions: 80.0% 16 - 20

Line Branch Decision Exec Source
1 // Deceleration Fuel Cut-off
2
3 #include "pch.h"
4
5 #include "dfco.h"
6 #include "closed_loop_fuel.h"
7
8 1120 bool DfcoController::getState() const {
9
2/2
✓ Branch 0 taken 1103 times.
✓ Branch 1 taken 17 times.
2/2
✓ Decision 'true' taken 1103 times.
✓ Decision 'false' taken 17 times.
1120 if (!engineConfiguration->coastingFuelCutEnabled) {
10 1103 return false;
11 }
12
13
2/3
✓ Branch 1 taken 17 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 17 times.
17 if (checkIfTuningVeNow()) {
14 return false;
15 }
16
17
1/1
✓ Branch 2 taken 17 times.
17 const auto tps = Sensor::get(SensorType::DriverThrottleIntent);
18
1/1
✓ Branch 2 taken 17 times.
17 const auto clt = Sensor::get(SensorType::Clt);
19
1/1
✓ Branch 2 taken 17 times.
17 const auto map = Sensor::get(SensorType::Map);
20
21 // If some sensor is broken, inhibit DFCO
22
3/6
✓ Branch 1 taken 17 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 17 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 17 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 17 times.
17 if (!tps || !clt) {
23 return false;
24 }
25
26 // MAP sensor is optional, only inhibit if the sensor is present but broken
27
1/1
✓ Branch 1 taken 17 times.
17 bool hasMap = Sensor::hasSensor(SensorType::Map);
28
3/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 17 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 17 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 17 times.
17 if (hasMap && !map) {
29 return false;
30 }
31
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 17 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 17 times.
17 if (engine->engineState.lua.disableDecelerationFuelCutOff) {
32 // Lua might have reasons to disable
33 return false;
34 }
35
36
1/1
✓ Branch 1 taken 17 times.
17 float rpm = Sensor::getOrZero(SensorType::Rpm);
37
1/1
✓ Branch 1 taken 17 times.
17 float vss = Sensor::getOrZero(SensorType::VehicleSpeed);
38
39
3/5
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 4 taken 17 times.
✓ Branch 6 taken 17 times.
✗ Branch 7 not taken.
17 bool mapActivate = !hasMap || !m_mapHysteresis.test(map.value_or(0), engineConfiguration->coastingFuelCutMap + 1, engineConfiguration->coastingFuelCutMap - 1);
40 17 bool tpsActivate = tps.Value < engineConfiguration->coastingFuelCutTps;
41 17 bool cltActivate = clt.Value > engineConfiguration->coastingFuelCutClt;
42 // True if throttle, MAP, and CLT are all acceptable for DFCO to occur
43
5/6
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 14 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 13 times.
✓ Branch 5 taken 1 time.
17 bool dfcoAllowed = mapActivate && tpsActivate && cltActivate;
44
45 17 bool rpmActivate = (rpm > engineConfiguration->coastingFuelCutRpmHigh);
46 17 bool rpmDeactivate = (rpm < engineConfiguration->coastingFuelCutRpmLow);
47
48 // greater than or equal so that it works if both config params are set to 0
49 17 bool vssActivate = (vss >= engineConfiguration->coastingFuelCutVssHigh);
50 17 bool vssDeactivate = (vss < engineConfiguration->coastingFuelCutVssLow);
51
52 // RPM is high enough, VSS high enough, and DFCO allowed
53
6/6
✓ Branch 0 taken 13 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 10 times.
✓ Branch 3 taken 3 times.
✓ Branch 4 taken 7 times.
✓ Branch 5 taken 3 times.
2/2
✓ Decision 'true' taken 7 times.
✓ Decision 'false' taken 10 times.
17 if (dfcoAllowed && rpmActivate && vssActivate) {
54 7 return true;
55 }
56
57 // RPM too low, VSS too low, or DFCO not allowed
58
6/6
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 4 times.
✓ Branch 2 taken 5 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 1 time.
✓ Branch 5 taken 4 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 4 times.
10 if (!dfcoAllowed || rpmDeactivate || vssDeactivate) {
59 6 return false;
60 }
61
62 // No conditions hit, no change to state (provides hysteresis)
63 4 return m_isDfco;
64 }
65
66 1120 void DfcoController::update() {
67 // Run state machine
68 1120 bool newState = getState();
69
70 // If fuel is cut, reset the timer
71
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 1112 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 1112 times.
1120 if (newState) {
72 8 m_timeSinceCut.reset();
73 } else {
74 // If fuel is not cut, reset the not-cut timer
75 1112 m_timeSinceNoCut.reset();
76 }
77
78 1120 m_isDfco = newState;
79 1120 }
80
81 524079 bool DfcoController::cutFuel() const {
82 524079 float cutDelay = engineConfiguration->dfcoDelay;
83
84 // 0 delay means cut immediately, aka timer has always expired
85
4/4
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 524074 times.
✓ Branch 3 taken 1 time.
✓ Branch 4 taken 4 times.
524079 bool hasBeenDelay = (cutDelay == 0) || m_timeSinceNoCut.hasElapsedSec(cutDelay);
86
87
4/4
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 524071 times.
✓ Branch 2 taken 6 times.
✓ Branch 3 taken 2 times.
524079 return m_isDfco && hasBeenDelay;
88 }
89
90 1 float DfcoController::getTimeSinceCut() const {
91 1 return m_timeSinceCut.getElapsedSeconds();
92 }
93
94 961 float DfcoController::getTimingRetard() const {
95 961 float cutTiming = clampF(0, engineConfiguration->dfcoRetardDeg, 30);
96
97
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 953 times.
2/2
✓ Decision 'true' taken 8 times.
✓ Decision 'false' taken 953 times.
961 if (m_isDfco) {
98 // While cut, always retard timing
99 8 return cutTiming;
100 } else {
101 953 float timeSinceCut = m_timeSinceCut.getElapsedSeconds();
102 953 float rampInTime = engineConfiguration->dfcoRetardRampInTime;
103
104
2/2
✓ Branch 0 taken 946 times.
✓ Branch 1 taken 7 times.
2/2
✓ Decision 'true' taken 946 times.
✓ Decision 'false' taken 7 times.
953 if (timeSinceCut > rampInTime) {
105 // Normal operation, no retard
106 946 return 0;
107 } else {
108 7 return interpolateClamped(0, cutTiming, 0.5, 0, timeSinceCut);
109 }
110 }
111 }
112