rusEFI
The most advanced open source ECU
Loading...
Searching...
No Matches
Public Member Functions | Data Fields | Private Member Functions | Private Attributes
LongTermIdleTrim Class Reference

#include <closed_loop_idle.h>

Inheritance diagram for LongTermIdleTrim:
Inheritance graph
[legend]
Collaboration diagram for LongTermIdleTrim:
Collaboration graph
[legend]

Public Member Functions

 LongTermIdleTrim ()
 
float getLtitFactor (float rpm, float clt) const
 
void update (float rpm, float clt, bool acActive, bool fan1Active, bool fan2Active, float idleIntegral)
 
void smoothLtitTable (float intensity)
 
void onIgnitionStateChanged (bool ignitionOn)
 
void checkIfShouldSave ()
 
virtual void loadLtitFromConfig ()
 
bool hasValidData () const
 

Data Fields

bool updatedLtit = false
 
- Data Fields inherited from closed_loop_idle_s
bool isStableIdle: 1 {}
 
bool unusedBit_1_1: 1 {}
 
bool unusedBit_1_2: 1 {}
 
bool unusedBit_1_3: 1 {}
 
bool unusedBit_1_4: 1 {}
 
bool unusedBit_1_5: 1 {}
 
bool unusedBit_1_6: 1 {}
 
bool unusedBit_1_7: 1 {}
 
bool unusedBit_1_8: 1 {}
 
bool unusedBit_1_9: 1 {}
 
bool unusedBit_1_10: 1 {}
 
bool unusedBit_1_11: 1 {}
 
bool unusedBit_1_12: 1 {}
 
bool unusedBit_1_13: 1 {}
 
bool unusedBit_1_14: 1 {}
 
bool unusedBit_1_15: 1 {}
 
bool unusedBit_1_16: 1 {}
 
bool unusedBit_1_17: 1 {}
 
bool unusedBit_1_18: 1 {}
 
bool unusedBit_1_19: 1 {}
 
bool unusedBit_1_20: 1 {}
 
bool unusedBit_1_21: 1 {}
 
bool unusedBit_1_22: 1 {}
 
bool unusedBit_1_23: 1 {}
 
bool unusedBit_1_24: 1 {}
 
bool unusedBit_1_25: 1 {}
 
bool unusedBit_1_26: 1 {}
 
bool unusedBit_1_27: 1 {}
 
bool unusedBit_1_28: 1 {}
 
bool unusedBit_1_29: 1 {}
 
bool unusedBit_1_30: 1 {}
 
bool unusedBit_1_31: 1 {}
 
float emaError = (float)0
 

Private Member Functions

bool isValidConditionsForLearning (float idleIntegral) const
 
void initializeTableWithDefaults ()
 

Private Attributes

float ltitTableHelper [LTIT_TABLE_SIZE]
 
bool ltitTableInitialized = false
 
Timer m_updateTimer
 
Timer m_stableIdleTimer
 
Timer m_ignitionOffTimer
 
bool m_ignitionState = false
 
bool m_pendingSave = false
 

Detailed Description

Definition at line 14 of file closed_loop_idle.h.

Constructor & Destructor Documentation

◆ LongTermIdleTrim()

LongTermIdleTrim::LongTermIdleTrim ( )

Definition at line 12 of file closed_loop_idle.cpp.

12 {
14 emaError = 0.0f; //TODO: unused?
16 m_pendingSave = false;
17}
Here is the call graph for this function:

Member Function Documentation

◆ checkIfShouldSave()

void LongTermIdleTrim::checkIfShouldSave ( )

Definition at line 229 of file closed_loop_idle.cpp.

229 {
230 // Handle delayed save after ignition off
232 float saveDelaySeconds = engineConfiguration->ltitIgnitionOffSaveDelay;
233 if (saveDelaySeconds <= 0) {
234 //TODO: this is part of setDefaultEngineConfiguration?
235 saveDelaySeconds = 5.0f; // Default 5 seconds
236 }
237
238 if (m_ignitionOffTimer.hasElapsedSec(saveDelaySeconds)) {
239 // TODO: copyArray?
240 // Save to flash memory
241 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
242 // Convert float to autoscaled uint16_t
243 config->ltitTable[i] = static_cast<uint16_t>(ltitTableHelper[i]);
244 }
245
246#if EFI_PROD_CODE
247 //TODO: we need to use requestBurn here?
249#endif // EFI_PROD_CODE
250 m_pendingSave = false;
251 }
252 }
253}
float ltitTableHelper[LTIT_TABLE_SIZE]
constexpr int LTIT_TABLE_SIZE
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
void setNeedToWriteConfiguration()
scaled_channel< uint16_t, 10, 1 > ltitTable[CLT_IDLE_TABLE_CLT_SIZE]

Referenced by IdleController::onFastCallback().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getLtitFactor()

float LongTermIdleTrim::getLtitFactor ( float  rpm,
float  clt 
) const

Definition at line 67 of file closed_loop_idle.cpp.

67 {
68 //TODO: rpm unused?
69 UNUSED(rpm);
70
72 return 1.0f; // No correction if not initialized
73 }
74
75 // Use 2D interpolation based only on CLT (temperature)
76 return interpolate2d(clt, config->cltIdleCorrBins, ltitTableHelper) * 0.01f;
77}
static CCM_OPTIONAL FunctionalSensor clt(SensorType::Clt, MS2NT(10))
UNUSED(samplingTimeSeconds)
Here is the call graph for this function:

◆ hasValidData()

bool LongTermIdleTrim::hasValidData ( ) const

Definition at line 27 of file closed_loop_idle.cpp.

27 {
28 // More robust validation - check for reasonable range and distribution
29 int validCount = 0;
30 float totalValue = 0.0f;
31
32 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
33 float value = static_cast<float>(config->ltitTable[i]);
34
35 // Check if value is in reasonable range (50% to 150%)
36 if (value >= 50.0f && value <= 150.0f) {
37 validCount++;
38 totalValue += value;
39 }
40 }
41
42 // Require at least half the table to be valid and reasonable average
43 if (validCount < (LTIT_TABLE_SIZE / 2)) {
44 return false;
45 }
46
47 float avgValue = totalValue / validCount;
48 return (avgValue >= 80.0f && avgValue <= 120.0f); // Reasonable average range
49}

Referenced by loadLtitFromConfig().

Here is the caller graph for this function:

◆ initializeTableWithDefaults()

void LongTermIdleTrim::initializeTableWithDefaults ( )
private

Definition at line 19 of file closed_loop_idle.cpp.

19 {
20 // Initialize with 100% (1.0 multiplier) as default
21 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
22 ltitTableHelper[i] = 100.0f;
23 }
24}

Referenced by loadLtitFromConfig(), and LongTermIdleTrim().

Here is the caller graph for this function:

◆ isValidConditionsForLearning()

bool LongTermIdleTrim::isValidConditionsForLearning ( float  idleIntegral) const
private

Definition at line 79 of file closed_loop_idle.cpp.

79 {
80 float minThreshold = engineConfiguration->ltitIntegratorThreshold;
81 if (std::abs(idleIntegral) < minThreshold) {
82 return false; // Integrator too low - PID not working hard enough
83 }
84
85 // Upper limit to avoid extreme conditions
86 if (std::abs(idleIntegral) > 25.0f) {
87 return false; // Integrator too high - unstable conditions
88 }
89
90 // Check if enough time has passed since ignition on
92 return false;
93 }
94
95 // Must be in stable idle
96 if (!isStableIdle) {
97 return false;
98 }
99
100 return true;
101}

Referenced by update().

Here is the caller graph for this function:

◆ loadLtitFromConfig()

void LongTermIdleTrim::loadLtitFromConfig ( )
virtual

Definition at line 51 of file closed_loop_idle.cpp.

51 {
52 if (hasValidData()) {
53 // Convert autoscaled uint16_t to float
54 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
55 ltitTableHelper[i] = static_cast<float>(config->ltitTable[i]);
56 }
57
59 } else {
60 //TODO: this is part of setDefaultEngineConfiguration?
61 // Initialize with defaults if no valid data
64 }
65}
bool hasValidData() const

Referenced by IdleController::init(), and update().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ onIgnitionStateChanged()

void LongTermIdleTrim::onIgnitionStateChanged ( bool  ignitionOn)

Definition at line 211 of file closed_loop_idle.cpp.

211 {
212 m_ignitionState = ignitionOn;
213
214 if (ignitionOn) {
215 // Reset timers when ignition turns on
216 m_updateTimer.reset();
217 m_stableIdleTimer.reset();
218 isStableIdle = false;
219 m_pendingSave = false;
220 } else if (updatedLtit) {
221 // Schedule save after ignition off
222 // TODO: maybe move all this to EngineModule & use needsDelayedShutoff?
223 m_pendingSave = true;
224 m_ignitionOffTimer.reset();
225 updatedLtit = false;
226 }
227}

Referenced by IdleController::onIgnitionStateChanged().

Here is the caller graph for this function:

◆ smoothLtitTable()

void LongTermIdleTrim::smoothLtitTable ( float  intensity)

Definition at line 256 of file closed_loop_idle.cpp.

256 {
257 if (!engineConfiguration->ltitEnabled || intensity <= 0.0f || intensity > 100.0f) {
258 return; // Invalid intensity or LTIT disabled
259 }
260
261 // Normalize intensity to 0.0-1.0 range
262 float normalizedIntensity = intensity / 100.0f;
263
264 // Apply 1D smoothing for the temperature-based curve
265 float temp[LTIT_TABLE_SIZE];
266
267 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
268 float sum = ltitTableHelper[i];
269 int count = 1;
270
271 // Add values from adjacent cells if they exist
272 if (i > 0) {
273 sum += ltitTableHelper[i-1];
274 count++;
275 }
276 if (i < LTIT_TABLE_SIZE-1) {
277 sum += ltitTableHelper[i+1];
278 count++;
279 }
280
281 // Calculate the average of the cell and its neighbors
282 float avg = sum / count;
283
284 // Apply weighted average based on intensity
285 temp[i] = ltitTableHelper[i] * (1.0f - normalizedIntensity) + avg * normalizedIntensity;
286 }
287
288 // Copy back the smoothed values
289 for (int i = 0; i < LTIT_TABLE_SIZE; i++) {
290 ltitTableHelper[i] = temp[i];
291 }
292
293 // Mark for saving
294 m_pendingSave = true;
295}
uint16_t count
Definition tunerstudio.h:1

◆ update()

void LongTermIdleTrim::update ( float  rpm,
float  clt,
bool  acActive,
bool  fan1Active,
bool  fan2Active,
float  idleIntegral 
)

Definition at line 103 of file closed_loop_idle.cpp.

103 {
104 //TODO: acActive unused, fan1Active, fan2Active, check comment about isValidConditionsForLearning
105 UNUSED(acActive); UNUSED(fan1Active); UNUSED(fan2Active);
106
108 return;
109 }
110
111 // Try to load data periodically until successful
114 return;
115 }
116
117 // Check ignition delay
119 m_stableIdleTimer.reset();
120 isStableIdle = false;
121 return;
122 }
123
124 auto& idleController = engine->engineModules.get<IdleController>();
125 auto currentPhase = idleController->getCurrentPhase();
126
127 // LTIT should only learn during Phase::Idling
128 if (currentPhase != IIdleController::Phase::Idling) {
129 m_stableIdleTimer.reset();
130 isStableIdle = false;
131 return;
132 }
133
134 // Check if we're in idle RPM range
135 float targetRpm = idleController->getTargetRpm(clt).ClosedLoopTarget;
136 float rpmDelta = std::abs(rpm - targetRpm);
137 bool isIdleRpm = rpmDelta < engineConfiguration->ltitStableRpmThreshold;
138
139 // Check stability
140 if (!isIdleRpm) {
141 m_stableIdleTimer.reset();
142 isStableIdle = false;
143 return;
144 }
145
146 // Check if stable for minimum time
147 // TODO: set ltitStableTime default value? also move to autoscale 0.1 like other configs with seconds?
149 isStableIdle = true;
150 }
151
152 if (!isStableIdle) {
153 return;
154 }
155
156 if (!idleController->isIdleClosedLoop) {
157 return;
158 }
159
160 // Main table learning (now allowed even with AC/Fan active)
161 // Validate conditions
162 if (!isValidConditionsForLearning(idleIntegral)) {
163 return;
164 }
165
166 // Check minimum update interval (fixed slowCallback period: 50ms)
167 if (!m_updateTimer.hasElapsedSec(1.0f)) {
168 return;
169 }
170 m_updateTimer.reset();
171
172 //TODO: docs?, weird use of non-public interpolation.h, we are trying to the get bin index for X temp?
173 // also ltitTableHelper scale will depend on clt idle bins?
174 // Use proper bin finding with getBin function for CLT only
175 auto cltBin = priv::getBin(clt, config->cltIdleCorrBins);
176
177 // Apply correction rate in %/s (percentage per second)
178 // Using fixed slowCallback delta time (50ms = 0.05s) for consistent behavior
179 const float deltaTime = 0.05f; // SLOW_CALLBACK_PERIOD_MS / 1000.0f
180 float correctionPerSecond = idleIntegral * engineConfiguration->ltitCorrectionRate * 0.01f; // Convert % to decimal
181 float correction = correctionPerSecond * deltaTime; // Apply time-based correction
182 float alpha = engineConfiguration->ltitEmaAlpha / 255.0f;
183
184 // Primary cell (largest weight)
185 float newValue = ltitTableHelper[cltBin.Idx] * (1.0f + correction);
186 newValue = alpha * newValue + (1.0f - alpha) * ltitTableHelper[cltBin.Idx];
187
188 // Apply clamping
190 float clampMax = engineConfiguration->ltitClampMax > 0 ? engineConfiguration->ltitClampMax : 250.0f;
191 ltitTableHelper[cltBin.Idx] = clampF(clampMin, newValue, clampMax);
192
193 // Apply to adjacent cells with reduced weight (for better interpolation)
194 float adjWeight = 0.3f; // 30% weight for adjacent cells
195 for (int di = -1; di <= 1; di++) {
196 if (di == 0) continue; // Skip primary cell
197
198 int adjI = cltBin.Idx + di;
199
200 if (adjI >= 0 && adjI < LTIT_TABLE_SIZE) {
201 float adjCorrection = correction * adjWeight;
202 float adjNewValue = ltitTableHelper[adjI] * (1.0f + adjCorrection);
203 adjNewValue = alpha * adjNewValue + (1.0f - alpha) * ltitTableHelper[adjI];
204 ltitTableHelper[adjI] = clampF(clampMin, adjNewValue, clampMax);
205 }
206 }
207
208 updatedLtit = true;
209}
type_list< Mockable< InjectorModelPrimary >, Mockable< InjectorModelSecondary >,#if EFI_IDLE_CONTROL Mockable< IdleController >,#endif TriggerScheduler,#if EFI_HPFP &&EFI_ENGINE_CONTROL Mockable< HpfpController >,#endif #if EFI_ENGINE_CONTROL Mockable< ThrottleModel >,#endif #if EFI_ALTERNATOR_CONTROL AlternatorController,#endif MainRelayController, Mockable< IgnitionController >, Mockable< AcController >, PrimeController, DfcoController,#if EFI_HD_ACR HarleyAcr,#endif Mockable< WallFuelController >, KnockController, SensorChecker,#if EFI_ENGINE_CONTROL Mockable< LimpManager >,#endif #if EFI_VVT_PID VvtController1, VvtController2, VvtController3, VvtController4,#endif #if EFI_BOOST_CONTROL BoostController,#endif TpsAccelEnrichment,#if EFI_LAUNCH_CONTROL NitrousController,#endif #if EFI_LTFT_CONTROL LongTermFuelTrim,#endif ShortTermFuelTrim,#include "modules_list_generated.h" EngineModule > engineModules
Definition engine.h:194
Phase getCurrentPhase() const override
Definition idle_thread.h:96
virtual void loadLtitFromConfig()
bool isValidConditionsForLearning(float idleIntegral) const
static EngineAccessor engine
Definition engine.h:413

Referenced by IdleController::updateLtit().

Here is the call graph for this function:
Here is the caller graph for this function:

Field Documentation

◆ ltitTableHelper

float LongTermIdleTrim::ltitTableHelper[LTIT_TABLE_SIZE]
private

◆ ltitTableInitialized

bool LongTermIdleTrim::ltitTableInitialized = false
private

Definition at line 33 of file closed_loop_idle.h.

Referenced by getLtitFactor(), loadLtitFromConfig(), LongTermIdleTrim(), and update().

◆ m_ignitionOffTimer

Timer LongTermIdleTrim::m_ignitionOffTimer
private

Definition at line 38 of file closed_loop_idle.h.

Referenced by checkIfShouldSave(), and onIgnitionStateChanged().

◆ m_ignitionState

bool LongTermIdleTrim::m_ignitionState = false
private

Definition at line 40 of file closed_loop_idle.h.

Referenced by checkIfShouldSave(), and onIgnitionStateChanged().

◆ m_pendingSave

bool LongTermIdleTrim::m_pendingSave = false
private

◆ m_stableIdleTimer

Timer LongTermIdleTrim::m_stableIdleTimer
private

Definition at line 37 of file closed_loop_idle.h.

Referenced by onIgnitionStateChanged(), and update().

◆ m_updateTimer

Timer LongTermIdleTrim::m_updateTimer
private

Definition at line 36 of file closed_loop_idle.h.

Referenced by isValidConditionsForLearning(), onIgnitionStateChanged(), and update().

◆ updatedLtit

bool LongTermIdleTrim::updatedLtit = false

Definition at line 22 of file closed_loop_idle.h.

Referenced by onIgnitionStateChanged(), and update().


The documentation for this class was generated from the following files: