Files
hpsg5-controller_v2-stm32g4/Core/Src/tein_detection.c
2026-03-30 18:36:35 +02:00

268 lines
7.9 KiB
C

/*
* tein_detection.c
*
* Created on: May 17, 2025
* Author: herli
*/
#include <id.h>
#include "tein_detection.h"
#include "injection.h"
#include "timeouts.h"
#include <stdint.h>
#include <stdlib.h>
#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;
}