GCC Code Coverage Report


Directory: ./
File: firmware/controllers/algo/launch_control.cpp
Date: 2025-11-16 14:52:24
Coverage Exec Excl Total
Lines: 95.4% 103 0 108
Functions: 100.0% 16 0 16
Branches: 76.9% 60 0 78
Decisions: 86.7% 13 - 15

Line Branch Decision Exec Source
1 /*
2 * @file launch_control.cpp
3 *
4 * @date 10. sep. 2019
5 * Author: Ola Ruud
6 * Rework: Patryk Chmura
7 */
8
9 #include "pch.h"
10
11 #if EFI_LAUNCH_CONTROL
12 #include "boost_control.h"
13 #include "launch_control.h"
14 #include "engine_state.h"
15 #include "tinymt32.h" // TL,DR: basic implementation of 'random'
16 #include "gppwm_channel_reader.h"
17
18 /**
19 * We can have active condition from switch or from clutch.
20 * In case we are dependent on VSS we just return true.
21 */
22
1/1
✓ Decision 'true' taken 727 times.
727 bool LaunchControlBase::isInsideSwitchCondition() {
23 727 isSwitchActivated = engineConfiguration->launchActivationMode == SWITCH_INPUT_LAUNCH;
24 727 isClutchActivated = engineConfiguration->launchActivationMode == CLUTCH_INPUT_LAUNCH;
25 727 isBrakePedalActivated = engineConfiguration->launchActivationMode == STOP_INPUT_LAUNCH;
26
27
2/2
✓ Branch 0 taken 65 times.
✓ Branch 1 taken 662 times.
2/2
✓ Decision 'true' taken 65 times.
✓ Decision 'false' taken 662 times.
727 if (isSwitchActivated) {
28 #if !EFI_SIMULATOR
29
2/2
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 63 times.
2/2
✓ Decision 'true' taken 2 times.
✓ Decision 'false' taken 63 times.
65 if (isBrainPinValid(engineConfiguration->launchActivatePin)) {
30 2 launchActivatePinState = efiReadPin(engineConfiguration->launchActivatePin, engineConfiguration->launchActivatePinMode);
31 }
32 #endif // EFI_PROD_CODE
33 65 return launchActivatePinState;
34
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 658 times.
2/2
✓ Decision 'true' taken 4 times.
✓ Decision 'false' taken 658 times.
662 } else if (isClutchActivated) {
35 4 return getClutchDownState();
36
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 658 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 658 times.
658 } else if (isBrakePedalActivated) {
37 return getBrakePedalState();
38
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 658 times.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 658 times.
658 } else if (engineConfiguration->launchActivationMode == LUA_LAUNCH) {
39 return luaLaunchState;
40 } else {
41 // ALWAYS_ACTIVE_LAUNCH
42 658 return true;
43 }
44 }
45
46 /**
47 * Returns True when Vehicle speed ALLOWS launch control
48 */
49 725 bool LaunchControlBase::isInsideSpeedCondition() const {
50
2/2
✓ Branch 0 taken 51 times.
✓ Branch 1 taken 674 times.
2/2
✓ Decision 'true' taken 51 times.
✓ Decision 'false' taken 674 times.
725 if (engineConfiguration->launchSpeedThreshold == 0) {
51 51 return true; // allow launch, speed does not matter
52 }
53
54 674 int speed = Sensor::getOrZero(SensorType::VehicleSpeed);
55
56 674 return engineConfiguration->launchSpeedThreshold > speed;
57 }
58
59 /**
60 * Returns false if TPS is invalid or TPS > preset threshold
61 */
62 723 bool LaunchControlBase::isInsideTpsCondition() const {
63 723 auto tps = Sensor::get(SensorType::DriverThrottleIntent);
64
65 // Disallow launch without valid TPS
66
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 659 times.
2/2
✓ Decision 'true' taken 64 times.
✓ Decision 'false' taken 659 times.
723 if (!tps.Valid) {
67 64 return false;
68 }
69
70 // todo: should this be 'launchTpsThreshold <= tps.Value' so that nicely calibrated TPS of zero does not prevent launch?
71 659 return engineConfiguration->launchTpsThreshold < tps.Value;
72 }
73
74 726 LaunchCondition LaunchControlBase::calculateRPMLaunchCondition(const float rpm) {
75
2/2
✓ Branch 0 taken 69 times.
✓ Branch 1 taken 657 times.
726 if ((engineConfiguration->launchActivationMode == SWITCH_INPUT_LAUNCH)
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 69 times.
69 && (engineConfiguration->torqueReductionActivationMode == LAUNCH_BUTTON)
77 && engineConfiguration->torqueReductionEnabled
78 && (engineConfiguration->torqueReductionArmingRpm <= rpm)
79 ) {
80 // We need perform Shift Torque Reduction stuff (see
81 // https://github.com/rusefi/rusefi/issues/5608#issuecomment-2391500472 and
82 // https://github.com/rusefi/rusefi/issues/5608#issuecomment-2391772899 for details)
83 return LaunchCondition::NotMet;
84 }
85
86 726 const int launchRpm = engineConfiguration->launchRpm;
87 726 const int preLaunchRpm = launchRpm - engineConfiguration->launchRpmWindow;
88
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 696 times.
726 if (rpm < preLaunchRpm) {
89 30 return LaunchCondition::NotMet;
90
2/2
✓ Branch 0 taken 641 times.
✓ Branch 1 taken 55 times.
696 } else if (launchRpm <= rpm) {
91 641 return LaunchCondition::Launch;
92 } else {
93 55 return LaunchCondition::PreLaunch;
94 }
95 }
96
97 720 LaunchCondition LaunchControlBase::calculateLaunchCondition(const float rpm) {
98 720 const LaunchCondition currentRpmLaunchCondition = calculateRPMLaunchCondition(rpm);
99 720 activateSwitchCondition = isInsideSwitchCondition();
100 720 rpmLaunchCondition = (currentRpmLaunchCondition == LaunchCondition::Launch);
101 720 rpmPreLaunchCondition = (currentRpmLaunchCondition == LaunchCondition::PreLaunch);
102 720 speedCondition = isInsideSpeedCondition();
103 720 tpsCondition = isInsideTpsCondition();
104
105
5/6
✓ Branch 0 taken 718 times.
✓ Branch 1 taken 2 times.
✓ Branch 2 taken 655 times.
✓ Branch 3 taken 63 times.
✓ Branch 4 taken 655 times.
✗ Branch 5 not taken.
720 if(speedCondition && activateSwitchCondition && tpsCondition) {
106 655 return currentRpmLaunchCondition;
107 } else {
108 65 return LaunchCondition::NotMet;
109 }
110 }
111
112 691 LaunchControlBase::LaunchControlBase() {
113 691 launchActivatePinState = false;
114 691 isPreLaunchCondition = false;
115 691 isLaunchCondition = false;
116 691 }
117
118 61327 float LaunchControlBase::getFuelCoefficient() const {
119
3/4
✓ Branch 0 taken 608 times.
✓ Branch 1 taken 60719 times.
✓ Branch 2 taken 608 times.
✗ Branch 3 not taken.
61327 return 1 + (isLaunchCondition && engineConfiguration->launchControlEnabled ? engineConfiguration->launchFuelAdderPercent / 100.0 : 0);
120 }
121
122 90840 void LaunchControlBase::update() {
123
2/2
✓ Branch 0 taken 90123 times.
✓ Branch 1 taken 717 times.
90840 if (!engineConfiguration->launchControlEnabled) {
124 90123 return;
125 }
126
127 717 const float rpm = Sensor::getOrZero(SensorType::Rpm);
128 717 const LaunchCondition launchCondition = calculateLaunchCondition(rpm);
129 717 isLaunchCondition = (launchCondition == LaunchCondition::Launch);
130 717 isPreLaunchCondition = (launchCondition == LaunchCondition::PreLaunch);
131
132 //and still recalculate in case user changed the values
133 717 retardThresholdRpm = engineConfiguration->launchRpm;
134
135 717 sparkSkipRatio = calculateSparkSkipRatio(rpm);
136 }
137
138 181730 bool LaunchControlBase::isLaunchRpmRetardCondition() const {
139
5/6
✓ Branch 0 taken 1230 times.
✓ Branch 1 taken 180500 times.
✓ Branch 2 taken 1230 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 1220 times.
✓ Branch 6 taken 10 times.
181730 return isLaunchCondition && engineConfiguration->launchControlEnabled && (retardThresholdRpm < Sensor::getOrZero(SensorType::Rpm));
140 }
141
142 90867 bool LaunchControlBase::isLaunchSparkRpmRetardCondition() const {
143
3/4
✓ Branch 1 taken 611 times.
✓ Branch 2 taken 90256 times.
✓ Branch 3 taken 611 times.
✗ Branch 4 not taken.
90867 return isLaunchRpmRetardCondition() && engineConfiguration->launchSparkCutEnable;
144 }
145
146 90863 bool LaunchControlBase::isLaunchFuelRpmRetardCondition() const {
147
3/4
✓ Branch 1 taken 609 times.
✓ Branch 2 taken 90254 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 609 times.
90863 return isLaunchRpmRetardCondition() && engineConfiguration->launchFuelCutEnable;
148 }
149
150 717 float LaunchControlBase::calculateSparkSkipRatio(const float rpm) const {
151 717 float result = 0.0f;
152
3/4
✓ Branch 0 taken 717 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 703 times.
✓ Branch 3 taken 14 times.
717 if (engineConfiguration->launchControlEnabled && engineConfiguration->launchSparkCutEnable) {
153
2/2
✓ Branch 0 taken 617 times.
✓ Branch 1 taken 86 times.
703 if (isLaunchCondition) {
154 617 result = 1.0f;
155
2/2
✓ Branch 0 taken 17 times.
✓ Branch 1 taken 69 times.
86 } else if (isPreLaunchCondition) {
156 17 const int launchRpm = engineConfiguration->launchRpm;
157 17 const int sparkSkipStartRpm = launchRpm - engineConfiguration->launchRpmWindow;
158
1/2
✓ Branch 0 taken 17 times.
✗ Branch 1 not taken.
17 if (sparkSkipStartRpm <= rpm) {
159 17 const float initialIgnitionCutRatio = engineConfiguration->initialIgnitionCutPercent / 100.0f;
160 17 const int sparkSkipEndRpm = launchRpm - engineConfiguration->launchCorrectionsEndRpm;
161 17 const float finalIgnitionCutRatio = engineConfiguration->finalIgnitionCutPercentBeforeLaunch / 100.0f;
162 17 result = interpolateClamped(sparkSkipStartRpm, initialIgnitionCutRatio, sparkSkipEndRpm, finalIgnitionCutRatio, rpm);
163 }
164 }
165 }
166 717 return result;
167 }
168
169 1369 SoftSparkLimiter::SoftSparkLimiter(const bool p_allowHardCut)
170 1369 : allowHardCut(p_allowHardCut) {
171 #if EFI_UNIT_TEST
172 1369 initLaunchControl();
173 #endif // EFI_UNIT_TEST
174 1369 }
175
176 162646 void SoftSparkLimiter::updateTargetSkipRatio(
177 const float luaSparkSkip,
178 const float tractionControlSparkSkip,
179 const float launchOrShiftTorqueReductionControllerSparkSkipRatio
180 ) {
181 162646 targetSkipRatio = luaSparkSkip;
182
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 162644 times.
162646 if (engineConfiguration->useHardSkipInTraction) {
183
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 1 time.
2 if (allowHardCut) {
184 1 targetSkipRatio += tractionControlSparkSkip;
185 }
186
2/2
✓ Branch 0 taken 81321 times.
✓ Branch 1 taken 81323 times.
162644 } else if (!allowHardCut) {
187 81321 targetSkipRatio += tractionControlSparkSkip;
188 }
189
190
2/2
✓ Branch 0 taken 81324 times.
✓ Branch 1 taken 81322 times.
162646 if (allowHardCut) {
191 /*
192 * We are applying launch controller spark skip ratio only for hard skip limiter (see
193 * https://github.com/rusefi/rusefi/issues/6566#issuecomment-2153149902).
194 */
195 81324 targetSkipRatio += launchOrShiftTorqueReductionControllerSparkSkipRatio;
196 }
197 162646 }
198
199 static tinymt32_t tinymt;
200
201 20226 bool SoftSparkLimiter::shouldSkip() {
202
3/6
✓ Branch 0 taken 1001 times.
✓ Branch 1 taken 19225 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1001 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
20226 if (targetSkipRatio == 0 || (!allowHardCut && wasJustSkipped)) {
203 19225 wasJustSkipped = false;
204 19225 return false;
205 }
206
207 1001 float random = tinymt32_generate_float(&tinymt);
208
1/2
✓ Branch 0 taken 1001 times.
✗ Branch 1 not taken.
1001 wasJustSkipped = random < (allowHardCut ? 1 : 2) * targetSkipRatio;
209 1001 return wasJustSkipped;
210 }
211
212 1960 void initLaunchControl() {
213 1960 tinymt32_init(&tinymt, 1345135);
214 1960 }
215
216 #endif /* EFI_LAUNCH_CONTROL */
217