GCC Code Coverage Report


Directory: ./
File: firmware/controllers/actuators/ac_control.cpp
Date: 2025-10-03 00:57:22
Coverage Exec Excl Total
Lines: 93.9% 46 0 49
Functions: 100.0% 3 0 3
Branches: 76.7% 33 0 43
Decisions: 86.4% 19 - 22

Line Branch Decision Exec Source
1 #include "pch.h"
2
3 #include "ac_control.h"
4 #include "deadband.h"
5 #include "max_limit_with_hysteresis.h"
6
7 namespace {
8 // Deadbands to prevent rapid switching on/off of AC
9 Deadband<200> maxRpmDeadband;
10 Deadband<5> maxCltDeadband;
11 Deadband<5> maxTpsDeadband;
12 Deadband<AcController::PRESSURE_DEADBAND_WIDTH> minPressureDeadband;
13 MaxLimitWithHysteresis acPressureEnableHysteresis;
14 }
15
16 1085 bool AcController::getAcState() {
17
1/1
✓ Branch 1 taken 1085 times.
1085 auto rpm = Sensor::getOrZero(SensorType::Rpm);
18
19 1085 engineTooSlow = rpm < 500;
20
21
2/2
✓ Branch 0 taken 921 times.
✓ Branch 1 taken 164 times.
2/2
✓ Decision 'true' taken 921 times.
✓ Decision 'false' taken 164 times.
1085 if (engineTooSlow) {
22 921 return false;
23 }
24
25 164 auto maxRpm = engineConfiguration->maxAcRpm;
26
3/4
✓ Branch 1 taken 164 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 48 times.
✓ Branch 6 taken 116 times.
164 engineTooFast = maxRpm != 0 && maxRpmDeadband.gt(rpm, maxRpm);
27
2/2
✓ Branch 0 taken 48 times.
✓ Branch 1 taken 116 times.
2/2
✓ Decision 'true' taken 48 times.
✓ Decision 'false' taken 116 times.
164 if (engineTooFast) {
28 48 return false;
29 }
30
31
1/1
✓ Branch 2 taken 116 times.
116 auto clt = Sensor::get(SensorType::Clt);
32
33 116 noClt = !clt;
34 // No AC with failed CLT
35
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 116 times.
116 if (noClt) {
36 return false;
37 }
38
39 // Engine too hot, disable
40 116 auto maxClt = engineConfiguration->maxAcClt;
41
2/4
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 116 times.
116 engineTooHot = (maxClt != 0) && maxCltDeadband.gt(clt.Value, maxClt);
42
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 116 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 116 times.
116 if (engineTooHot) {
43 return false;
44 }
45
46 // TPS too high, disable
47 116 auto maxTps = engineConfiguration->maxAcTps;
48
4/5
✓ Branch 0 taken 116 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 116 times.
✓ Branch 6 taken 51 times.
✓ Branch 7 taken 65 times.
116 tpsTooHigh = maxTps != 0 && maxTpsDeadband.gt(Sensor::getOrZero(SensorType::Tps1), maxTps);
49
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 65 times.
2/2
✓ Decision 'true' taken 51 times.
✓ Decision 'false' taken 65 times.
116 if (tpsTooHigh) {
50 51 return false;
51 }
52
53
1/1
✓ Branch 1 taken 65 times.
65 const auto acPressure= Sensor::get(SensorType::AcPressure);
54
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 38 times.
2/2
✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 38 times.
65 if (acPressure.Valid) {
55 27 const auto minAcPressure = static_cast<float>(engineConfiguration->minAcPressure);
56 27 acPressureTooLow = minPressureDeadband.lt(acPressure.Value, minAcPressure);
57
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 21 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 21 times.
27 if (acPressureTooLow) {
58 6 return false;
59 }
60
61 21 const auto maxAcPressure = static_cast<float>(engineConfiguration->maxAcPressure);
62 63 acPressureTooHigh = acPressureEnableHysteresis.checkIfLimitIsExceeded(
63
1/1
✓ Branch 1 taken 21 times.
21 acPressure.Value,
64 maxAcPressure,
65 21 engineConfiguration->acPressureEnableHyst
66 );
67
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 15 times.
2/2
✓ Decision 'true' taken 6 times.
✓ Decision 'false' taken 15 times.
21 if (acPressureTooHigh) {
68 6 return false;
69 }
70 }
71
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 53 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 53 times.
53 if (isDisabledByLua) {
73 return false;
74 }
75
76 // All conditions allow AC, simply pass thru switch
77 53 return acButtonState;
78 }
79
80 1085 void AcController::onSlowCallback() {
81 1085 bool isEnabled = getAcState();
82
83 1085 m_acEnabled = isEnabled;
84
85
2/2
✓ Branch 0 taken 1070 times.
✓ Branch 1 taken 15 times.
2/2
✓ Decision 'true' taken 1070 times.
✓ Decision 'false' taken 15 times.
1085 if (!isEnabled) {
86 // reset the timer if AC is off
87 1070 m_timeSinceNoAc.reset();
88 }
89
90 1085 float acDelay = engineConfiguration->acDelay;
91
2/2
✓ Branch 0 taken 27 times.
✓ Branch 1 taken 1058 times.
2/2
✓ Decision 'true' taken 27 times.
✓ Decision 'false' taken 1058 times.
1085 if (acDelay == 0) {
92 // Without delay configured, enable immediately
93 27 acCompressorState = isEnabled;
94 } else {
95
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 1058 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
1058 acCompressorState = isEnabled && m_timeSinceNoAc.hasElapsedSec(acDelay);
96 }
97
98 1085 enginePins.acRelay.setValue(acCompressorState);
99 1085 }
100
101 2197 bool AcController::isAcEnabled() const {
102 2197 return m_acEnabled;
103 }
104