/* * tein_detection.c * * Created on: May 17, 2025 * Author: herli */ #include #include "tein_detection.h" #include "injection.h" #include "timeouts.h" #include #include #define MAX_MINIMA 1 #define VARIATION_TOL 5 // tighter margin since index range is smaller typedef struct { int16_t index; float dv; // 1st derivative float d2v; // 2nd derivative } MinimaPoint; uint8_t dma_buffer_tein[DMA_BUFFER_SIZE_TEIN]; static int16_t signal[DMA_BUFFER_SIZE_TEIN]; // Centered signal static float smooth[DMA_BUFFER_SIZE_TEIN]; // 3-point moving average static float dv_dx[DMA_BUFFER_SIZE_TEIN]; // 1st derivative static float d2v_dx2[DMA_BUFFER_SIZE_TEIN]; // 2nd derivative static MinimaPoint last_minima[MAX_MINIMA]; // For extrapolation float LPF_Beta = 0.01; // 0<ß<1 float LPF_Beta_acc = 0.1; float T_ein_in; float T_ein_filtered = 800; float T_ein = TEIN_FAULT; uint8_t T_ein_status = 0; int CompensateTein = 0; uint32_t TeinProcessedTime; uint32_t lastTeinProcessedTime; int ProcessTein; uint32_t diff_ConvCallback; volatile uint8_t hasTeinDetEnded = 1; volatile uint8_t hasTeinDetEndedFlag = 0; void smooth_signal() { for (int i = 1; i < DMA_BUFFER_SIZE_TEIN - 1; i++) { smooth[i] = (signal[i - 1] + signal[i] + signal[i + 1]) / 3.0f; } smooth[0] = smooth[1]; smooth[DMA_BUFFER_SIZE_TEIN - 1] = smooth[DMA_BUFFER_SIZE_TEIN - 2]; } void compute_derivatives() { for (int i = 1; i < DMA_BUFFER_SIZE_TEIN - 1; i++) { dv_dx[i] = (smooth[i + 1] - smooth[i - 1]) / 2.0f; d2v_dx2[i] = smooth[i + 1] - 2.0f * smooth[i] + smooth[i - 1]; } dv_dx[0] = dv_dx[1]; dv_dx[DMA_BUFFER_SIZE_TEIN - 1] = dv_dx[DMA_BUFFER_SIZE_TEIN - 2]; d2v_dx2[0] = d2v_dx2[1]; d2v_dx2[DMA_BUFFER_SIZE_TEIN - 1] = d2v_dx2[DMA_BUFFER_SIZE_TEIN - 2]; } int detect_minima(MinimaPoint* output, uint16_t PeakEndIndex) { int count = 0; for (int i = PeakEndIndex; i < DMA_BUFFER_SIZE_TEIN - 1 && count < MAX_MINIMA; i++) { if (dv_dx[i - 1] < 0 && dv_dx[i] >= 0 && d2v_dx2[i] > 0) { output[count].index = (int16_t)i; output[count].dv = dv_dx[i]; output[count].d2v = d2v_dx2[i]; count++; } } return count; } int fill_missing_minima(MinimaPoint* found, int count, MinimaPoint* filled, uint16_t cutoffIndex) { int lostFirst = 0; int d1 = last_minima[1].index - last_minima[0].index; int d2 = last_minima[2].index - last_minima[1].index; MinimaPoint valid[4]; int valid_count = 0; for (int i = 0; i < count; ++i) { if (found[i].index <= cutoffIndex) { valid[valid_count++] = found[i]; } } if (valid_count >= 3) { // Copy first three by default filled[0] = valid[0]; filled[1] = valid[1]; filled[2] = valid[2]; // Check if spacing suggests first point is missing // Compare spacing between found[1] and found[2] against historical d1/d2 int spacing1 = valid[1].index - valid[0].index; int spacing2 = valid[2].index - valid[1].index; // If spacing2 matches d1 and spacing1 matches d2, it's likely a shift if (abs(spacing1 - d2) < VARIATION_TOL ) { //&& abs(spacing2 - d1) > VARIATION_TOL //si el espaciado es del 2 al 3, es decir // Shift: detected[0] is actually 2nd, detected[1] is 3rd, detected[2] is old 4th filled[2] = valid[1]; // 3rd ← was 2nd filled[1] = valid[0]; // 2nd ← was 1st filled[0].index = valid[0].index - d1; // Predict new 1st lostFirst = 1; } } if (valid_count == 2) { int spacing = valid[1].index - valid[0].index; if (abs(spacing - d1) < VARIATION_TOL) { //si tengo primero y segundo // found[0] = 1st, found[1] = 2nd → extrapolate 3rd filled[0] = valid[0]; filled[1] = valid[1]; filled[2].index = valid[1].index + d2; } else if (abs(valid[0].index - last_minima[1].index) < VARIATION_TOL && abs(valid[1].index - last_minima[2].index) < VARIATION_TOL) { //si tengo segundo y tercero // Possibly captured 2nd and 3rd → predict missing 1st filled[2] = valid[1]; filled[1] = valid[0]; filled[0].index = valid[0].index - d1; lostFirst = 1; } else { //si tengo primero y tercero (imposible) // Too uncertain → fallback to last known for (int i = 0; i < MAX_MINIMA; ++i) { filled[i] = last_minima[i]; } lostFirst = 1; } } else if (valid_count == 1) { // si solo encontré un punto, lo tomamos como que es el primero /*filled[1] = found[0]; filled[0].index = filled[1].index - d1; filled[2].index = filled[1].index + d2; lostFirst = 1;*/ filled[0] = valid[0]; filled[1].index = filled[0].index + d1; filled[2].index = filled[1].index + d2; } else if (valid_count == 0) { // como el anterior for (int i = 0; i < MAX_MINIMA; i++) { filled[i] = last_minima[i]; } lostFirst = 1; } return lostFirst; } extern IC_TEIN; void ProcessAdcSignal(uint32_t IC_INJ, uint16_t T_peak, uint16_t T_hold) { // Center signal for (int i = 0; i < DMA_BUFFER_SIZE_TEIN; i++) { signal[i] = (int16_t)dma_buffer_tein[i] - ADC_MID; } uint8_t PeakEndIndex = T_peak / (samplePeriod - 1); smooth_signal(); compute_derivatives(); MinimaPoint detected[MAX_MINIMA]; int found = detect_minima(detected, PeakEndIndex); if(!found || !T_hold){ //T_ein = 950; return; } /*MinimaPoint final[MAX_MINIMA]; uint16_t cutoff_index = T_on / samplePeriod; int lostFirst = fill_missing_minima(detected, found, final, cutoff_index); for (int i = 0; i < MAX_MINIMA; i++) { last_minima[i] = final[i]; }*/ // final[0..2].index now holds the local minima int lostFirst = 0; uint8_t TeinIndex = detected[0].index; // first minimum uint16_t timeConvCallback = (DMA_BUFFER_SIZE_TEIN - TeinIndex) * samplePeriod; // in microseconds diff_ConvCallback = TeinProcessedTime >= IC_INJ ? TeinProcessedTime-IC_INJ : TeinProcessedTime-(IC_INJ-0xffffffff); //diff_ConvCallback = TeinProcessedTime- lastTeinProcessedTime; // Convert timer ticks to microseconds float T_ConvCallback = diff_ConvCallback / 16.0; // us T_ein_in = T_ConvCallback - timeConvCallback; if(T_ein_in < T_peak+40){ /*for (int i = 0; i < MAX_MINIMA; i++) { last_minima[i].index = 0; }*/ return; } float Tein_diff = T_ein_filtered - T_ein_in; float abs_Tein_diff = Tein_diff < 0 ? -Tein_diff : Tein_diff; //take abs if(!lostFirst){ T_ein_filtered -= abs_Tein_diff < 40 ? LPF_Beta * Tein_diff : LPF_Beta_acc * Tein_diff; //T_ein_filtered -= abs_Tein_diff < 40 ? LPF_Beta * Tein_diff : 0; //if(abs_Tein_diff < 40){ //T_ein_filtered -= LPF_Beta * Tein_diff; //} //T_ein = T_ein_filtered - 15; T_ein = 1.01843 * T_ein_filtered - 10.918; CompensateTein = T_ein_filtered > 1700 ? 0 : 1; Timeout_ResetByIndex(10, TIM16->CNT); // Reset Tein timeout T_ein_status = CompensateTein; //CompensateTein = (T_ein_filtered < 400 && CompensateTein) ? 0 : 1; /*if(!CompensateTein){ T_ein = 950; }*/ } /*else{ CompensateTein = 0; }*/ if(!T_ein_filtered){ T_ein_filtered = T_ein_in; } //T_ein = CompensateTein ? 1.0549 * T_ein_filtered - 79.5975 : 950 ; //T_ein = 950; } extern TIM_HandleTypeDef htim1; uint8_t timer1started; //uint8_t pending_start_tein_adc; void TEIN_DET_Init(){ if(!timer1started){ HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_3); // no hace falta activarlo todas las veces; timer1started = 1; StartSampling(); } } void TEIN_DET_DeInit(){ T_ein = TEIN_FAULT; } void TEIN_DET_Service(){ //if() if(ProcessTein){//ProcessTein && startedEngine ProcessAdcSignal(IC_INJ, T_peak, T_hold); ProcessTein = 0; if(!T_hold){ Timeout_StopByIndex(10); // Reset Tein timeout } TW_DEFER_TEIN_VAL(); } } void TEIN_STATUS_ONFAULT(){ T_ein = TEIN_FAULT; T_ein_status = 0; }