12#define MAX_ADJ (0.25f)
14#define SAVE_AFTER_HITS 1000
42 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
48 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
49 for (
size_t loadIndex = 0; loadIndex < VE_LOAD_COUNT; loadIndex++) {
50 for (
size_t rpmIndex = 0; rpmIndex < VE_RPM_COUNT; rpmIndex++) {
51 trims[bank][loadIndex][rpmIndex] = PERCENT_DIV * (loadIndex + rpmIndex * 0.1);
63 for (
size_t loadIndex = 0; loadIndex < VE_LOAD_COUNT; loadIndex++) {
64 for (
size_t rpmIndex = 0; rpmIndex < VE_RPM_COUNT; rpmIndex++) {
68 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
69 k += 1.0f +
trims[bank][loadIndex][rpmIndex];
71 k = k / FT_BANK_COUNT;
84 ltftLoadPending =
false;
96 return clampF(0, PERCENT_DIV * cfg.
maxAdd, MAX_ADJ);
101 return clampF(-MAX_ADJ, -PERCENT_DIV * cfg.
maxRemove, 0);
108 if ((!cfg.
enabled) || (ltftSavePending) || (ltftLoadPending) ||
110 ltftLearning =
false;
120 if ((abs(x.Frac) > 0.5) ||
121 (abs(y.Frac) > 0.5)) {
124 ltftLearning =
false;
128 bool adjusted =
false;
132 float weight = 1.0 - hypotf(x.Frac, y.Frac) / hypotf(0.5, 0.5);
135 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
136 float lambdaCorrection = clResult.
banks[bank] - 1.0;
139 if (std::abs(lambdaCorrection) < PERCENT_DIV * cfg.
deadband) {
144 float trim = m_state->trims[bank][x.Idx][y.Idx];
147 float newTrim =
trim + k * (lambdaCorrection -
trim);
153 newTrim = clampF(getMinAdjustment(cfg), newTrim, getMaxAdjustment(cfg));
156 ltftAccummulatedCorrection[bank] += newTrim -
trim;
159 m_state->trims[bank][x.Idx][y.Idx] = newTrim;
164 ltftLearning = adjusted;
167 showUpdateToUser =
true;
183 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
184 ltftCorrection[bank] = 1.0f;
186 ltftCorrecting =
false;
197 if ((abs(x.Frac) > 0.5) ||
198 (abs(y.Frac) > 0.5)) {
207 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
208 ltftCorrection[bank] = 1.0f + interpolate3d(
209 m_state->trims[bank],
216 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
217 result.
banks[bank] = ltftCorrection[bank];
220 ltftCorrecting =
true;
228 ltftLoadPending =
false;
233 ltftSavePending =
true;
240 ltftSavePending =
false;
250 for (
size_t bank = 0; bank < FT_BANK_COUNT; bank++) {
251 ltftAccummulatedCorrection[bank] = 0.0;
256 m_state->applyToVe();
259 veNeedRefresh =
true;
264 veNeedRefresh =
false;
272 if (ltftPageRefreshFlag) {
273 ltftPageRefreshFlag =
false;
274 showUpdateToUser =
false;
275 pageRefreshTimer.reset();
278 ltftPageRefreshFlag = showUpdateToUser && pageRefreshTimer.hasElapsedSec(1);
283 m_state->fillRandom();
288 if ((ltftLoadPending) &&
289#
if EFI_SHAFT_POSITION_INPUT
293 efiPrintf(
"LTFT: failed to load calibrations");
295 ltftLoadPending =
false;
296 ltftLoadError =
true;
static bool call_board_override(std::optional< FuncType > board_override, Args &&... args)
RpmCalculator rpmCalculator
constexpr auto & module()
void onSlowCallback() override
bool needsDelayedShutoff() override
float getMinAdjustment(const ltft_s &cfg) const
void init(LtftState *state)
float getMaxAdjustment(const ltft_s &cfg) const
void learn(ClosedLoopFuelResult clResult, float rpm, float fuelLoad)
ClosedLoopFuelResult getTrims(float rpm, float fuelLoad)
float getIntegratorGain(const ltft_s &cfg, ft_region_e region) const
float getSecondsSinceEngineStart(efitick_t nowNt) const
static EngineAccessor engine
static constexpr persistent_config_s * config
static constexpr engine_configuration_s * engineConfiguration
bool settingsLtftRequestWriteToFlash()
constexpr float integrator_dt
std::optional< setup_custom_board_overrides_type > custom_board_LtftTrimToVeApply
static LtftState ltftState
size_t ltftGetTsPageSize()
void applyLongTermFuelTrimToVe()
void resetLongTermFuelTrim()
void devPokeLongTermFuelTrim()
LtftState * ltftGetState()
state("state", SensorCategory.SENSOR_INPUTS, FieldType.INT8, 1878, 1.0, -1.0, -1.0, "")
ltftCntDeadband("LTFT learning: in deadband", SensorCategory.SENSOR_INPUTS, FieldType.INT, 2024, 1.0, 0.0, 10000.0, "cnt")
ltftCntMiss("LTFT learning: miss", SensorCategory.SENSOR_INPUTS, FieldType.INT, 2020, 1.0, 0.0, 10000.0, "cnt")
ltftCntHit("LTFT learning: hits", SensorCategory.SENSOR_INPUTS, FieldType.INT, 2016, 1.0, 0.0, 10000.0, "cnt")
trim("ETB: trim", SensorCategory.SENSOR_INPUTS, FieldType.INT, 1840, 1.0, -100.0, 100.0, "%")
bool storageReqestReadID(StorageItemId id)
StorageStatus storageRead(StorageItemId id, uint8_t *ptr, size_t size)
StorageStatus storageWrite(StorageItemId id, const uint8_t *ptr, size_t size)
float banks[FT_BANK_COUNT]
float trims[FT_BANK_COUNT][VE_LOAD_COUNT][VE_RPM_COUNT]
scaled_channel< uint8_t, 10, 1 > maxRemove
scaled_channel< uint8_t, 10, 1 > deadband
scaled_channel< uint16_t, 1, 1 > timeConstant[STFT_CELL_COUNT]
scaled_channel< uint8_t, 10, 1 > maxAdd
uint16_t veLoadBins[VE_LOAD_COUNT]
scaled_channel< uint16_t, 10, 1 > veTable[VE_LOAD_COUNT][VE_RPM_COUNT]
uint16_t veRpmBins[VE_RPM_COUNT]
stft_state_e stftCorrectionState
constexpr void setTable(TElement(&dest)[N][M], const VElement value)