/*
 * toothedwheel.c
 *
 *  Created on: Aug 4, 2025
 *      Author: herli
 */

#include "toothed_wheel.h"
#include "timeouts.h"
#include "injection.h"
#include "FBKW.h"
#include "can_port.h"
#include "timing_manager.h"
#include "tein_detection.h"


volatile uint8_t currentTooth = 0;

volatile uint32_t IC_RPM_Val1 = 0;
volatile uint32_t IC_RPM_Val2 = 0;
volatile uint32_t IC_MT = 0;
volatile uint32_t IC_EOI = 0;
volatile uint32_t IC_TEIN = 0;
volatile uint32_t IC_NOM_BIP = 0;

volatile uint32_t prev_Difference = 0xFFFFFFFF;
volatile uint32_t Tooth_Difference = 0;

volatile uint8_t TW_current_max_teeth = 0;
volatile uint8_t TW_RPM_SENSOR_STATE = 0;

volatile float RPM;

volatile float MT_frequency = 0;
volatile float MT_RPM = 0;
//volatile float next_MT_RPM = 0;

volatile float last_MT_RPM = 0;

float actual_phi1;


volatile float TEETH_frequency = 0;
volatile float TEETH_RPM = 0;
volatile float last_TEETHRPM = 0;
volatile uint8_t SYNCOUT_clear = 0;

volatile uint32_t RPM_Difference;

volatile uint8_t startedEngine = 0;
volatile uint8_t count_CKP = 0;


extern float MEPI;

uint8_t real_boi_pending = 0;
uint8_t real_eoi_pending = 0;
uint8_t phi1_eval_pending = 0;

uint8_t hasCapturedTeeth = 0;

uint8_t real_eoi_updated = 0;
uint8_t real_bip_updated = 0;
uint8_t eoi_eval_pending = 0;

void TW_RESET_SENSOR(){
	TW_RPM_SENSOR_STATE = 0;
	TW_current_max_teeth = 0;
	prev_Difference = 0xFFFFFFFF;
	hasCapturedTeeth = 0;
}
void TW_EVAL_MAX_TEETH(){
	TW_RPM_SENSOR_STATE = (TW_current_max_teeth == TW_PERCYL_TEETH) ? 1 : 0 ;
}
uint32_t sensorfail;
extern uint8_t startup_finished;
volatile uint8_t isMT = 0;
uint8_t SYNCOUT_TEETH = 0;
volatile float accel_rpm = 0;
// en un timeout tendria que resetear el prevdiff, y hascapturedteet.
void TW_TEETH_CAPTURE(){
	if(hasCapturedTeeth){
		Tooth_Difference = IC_RPM_Val2 >= IC_RPM_Val1 ? IC_RPM_Val2-IC_RPM_Val1 : IC_RPM_Val2-(IC_RPM_Val1-0xffffffff);
	}
	if(Tooth_Difference > prev_Difference * TW_MT_THRESHOLD && TEETH_RPM > 2 && !isInjecting){
		//esto es para que la realentizacion no nos afecte y podamos bajar el treshold
		isMT = 1;
		//si tiene TEETH RPM y no está inyectando.. es MT 100%
		//si no hay algun diente capturado antes: esta claro que no es missing tooth
	}else{
		//el original, ademas checkea si esta en el ultimo diente y sabe que viene el missing tooth.
		//Sigue leyendo rpm pero da error de sensor y no inyecta... habra que ver si es necesario implementarlo.
		isMT = 0;
	}

	if(isMT){

		RPM_Difference = IC_RPM_Val2 >= IC_MT ? IC_RPM_Val2-IC_MT : IC_RPM_Val2-(IC_MT-0xffffffff);
		IC_MT = IC_RPM_Val2;

		TW_current_max_teeth = currentTooth;
		TW_RPM_SENSOR_STATE = (TW_current_max_teeth == TW_PERCYL_TEETH) ? 1 : 0 ;

		currentTooth = 0;

		INJ_MT_TASK();

		if(!TW_current_max_teeth){
			/*correction_beta = 0.0;
			correction_eoi = 0.0;*/
			//quitao para q no de error
			//Timeout_ResetByIndex(4, TIM16->CNT);  // Reset captor defect timeout
		}
		if(!TW_RPM_SENSOR_STATE){
			ClearEdgeBuffer();
			//TODO ENABLE CLEAREDGEBUFFER
			//TW_RESET_SENSOR();
			//TW_Reset_Pending_Tasks();
			//IC_MT = 0;
			sensorfail++;
			hasCapturedTeeth = 0;
			TEETH_RPM = 0;
			return;
		}

		FBKW_RESET_FEEDBACK();

		last_MT_RPM = MT_RPM;
		MT_frequency = refClock/RPM_Difference;
		MT_RPM = fclamp(60.0 * MT_frequency / CYLINDERS, MIN_RPM, 4000);
		RPM=MT_RPM;

		TM_UPDATE_ACCELERATION(last_MT_RPM, MT_RPM, RPM_Difference);//next_MT_RPM =

		MakeEdgeBufferOld();
		UpdateEdgeBuffer(IC_RPM_Val2, MT_RPM, 0, isInjecting);


		TW_PREPARE_BOI_ACCELCORRECTION();
		if(!startup_finished && RPM > 200){
			can_port_send_msg_def(&MSG_ID_EMPF3); // TX from template (+ symbols if any)
	        Timeout_ResetByIndex(18, TIM16->CNT);  // re-arm for another 40ms
		}
		INJ_PREPARE_ONCE();
		//TW_CALCULATE_INSTANT_EOI_CORRECTION();
		real_bip_updated = 0;
		real_eoi_updated = 0;
		if(currentTooth == SYNCOUT_TEETH){
			if(SYNC_Pulse_IsSyncoutMode()){
				SYNCOUT_clear = 1;
				SYNC_Pulse_GPIO_set(1);
			}
		}
	}else{ //normal teeth
		currentTooth++;

		if(!hasInjected && TW_RPM_SENSOR_STATE){ //TODO
			EvaluateInjection();
			/*if(currentTooth > 13){
				SYNCOUT_clear = 0;

			}*/
		}
		if(currentTooth == SYNCOUT_TEETH){
			if(SYNC_Pulse_IsSyncoutMode()){
				SYNCOUT_clear = 1;
				SYNC_Pulse_GPIO_set(1);
			}
		}
		if(currentTooth == SYNCOUT_TEETH + 1 && SYNCOUT_clear && SYNC_Pulse_IsSyncoutMode()){
			SYNC_Pulse_GPIO_set(0);
			SYNCOUT_clear = 0;
		}

		if(hasCapturedTeeth){
			TEETH_frequency = refClock/Tooth_Difference;
			TEETH_RPM = fclamp(60.0 * TEETH_frequency / TW_THEETHS, 1, 4000);
		}
		//UpdateEdgeBuffer(IC_RPM_Val2, TEETH_RPM, currentTooth);

		if(TW_RPM_SENSOR_STATE){

			if(!hasInjected){
				if(startedEngine && ME >  0.031){ //hay que ver, igual hace que resetee el BOI
					//TW_CALCULATE_INSTANT_BOI_CORRECTION(); //deactivated for now

				}
				//EvaluateInjection();


				INJ_EVAL_EOI_COMPENSATION();
			}
			INJ_EVAL_END();

			UpdateEdgeBuffer(IC_RPM_Val2, TEETH_RPM, currentTooth, isInjecting);

			if(!hasInjected){
				INJ_PREPARE_BIP(currentTooth);
				if(TW_RPM_SENSOR_STATE){ //TODO
					EvaluateInjection();
				}
				TW_CALCULATE_INSTANT_BOI_CORRECTION(); //deactivated for now
			}

		}


		if(currentTooth == TW_PERCYL_TEETH){
			if(RPM > 200) {
				can_port_send_msg_def(&MSG_ID_EMPF1);
			}

			if (s_empf2_pending) {
				can_port_send_msg_def(&MSG_ID_EMPF2);       // packs from EMPF2_WORDS[]
				s_empf2_pending = 0;
				Timeout_StopByIndex(17);               // stop the 60ms gate
			}
			if(HAL_GPIO_ReadPin(SHUT_OFF_GPIO_Port, SHUT_OFF_Pin) != GPIO_PIN_RESET){  	//si esta en alto

			}else{
				Timeout_ResetByIndex(11, TIM16->CNT);  // Reset turnoff timeout
			}
		}
		if(currentTooth == TW_PERCYL_TEETH - 1){
			INJ_END();
		}


		hasCapturedTeeth = 1;

		if(hasInjectionEndedFlag == 1){
			hasInjectionEndedFlag++;
		}else if(hasInjectionEndedFlag > 1){
			hasInjectionEnded = 1;

			real_boi_pending = 1;
			real_eoi_pending = 1;
		}
		if(hasTeinDetEndedFlag == 1){
			hasTeinDetEndedFlag = 0;
			hasTeinDetEnded = 1;
		}

		/*}else if(hasInjectionEndedFlag > 1){
			hasTeinDetEnded = 1;
		}*/

	}
	prev_Difference = Tooth_Difference;
	IC_RPM_Val1 = IC_RPM_Val2;

	if(TEETH_RPM > 15){
		Timeout_ResetByIndex(3, TIM16->CNT);  // Reset RPM timeout
	}
	if(RPM > TW_STARTED_RPM){
		//startedEngine = 1;
		Timeout_StartIfStopped(19, TIM16->CNT);
	}

}

uint8_t ckp_process_pending = 0;
uint8_t teethCKP;

void TW_CKP_CAPTURE(){
	if(TW_RPM_SENSOR_STATE){
		count_CKP++;

		ckp_process_pending=1;

		//meter timeout cuando !count_ckp (significa que solo recibe un pulso);
		CKP_PULSE_AVAILABLE = 1;
		Timeout_ResetByIndex(13, TIM16->CNT);  // Reset CKP timeout
	}
}
float fb_1;
float fb_2;
uint8_t fb_lock;
float new_fb = 0.0;
float lpf_fb = 1.0;//0.4
uint8_t feedbackNew = 1;
uint8_t valid = 0;

float ckp_rpm = 0.0;
float fbkw_offset= 0;
uint8_t process_CKP_tooth = 24;

void TW_CALC_FBKW_FEEDBACK(){
	if(!TW_RPM_SENSOR_STATE){
		return;
	}
	int index = -1;
	uint32_t IC_CKP_Real = IC_CKP2 - FBKW_FEEDBACK_IC_DT * 16;
	for(uint8_t i = 1; i < TW_PERCYL_TEETH; i++){
		if(edgeBuf[i].ic < IC_CKP_Real && IC_CKP_Real < edgeBuf[i+1].ic){
			index = i;
			break;
		}
	}
	if(index < 0){
		if(edgeBuf[TW_PERCYL_TEETH].ic < IC_CKP_Real && IC_CKP_Real < edgeBuf[0].ic){//probar si lo que le pasa es que es el ultimo
			index = TW_PERCYL_TEETH;
		}else{
			return;
		}
	}
	float eff_ckp_zero = FBKW_GET_CKP_OFFSET();
	uint8_t index0 = eff_ckp_zero / TW_TOOTH_ALPHA - 1;
	process_CKP_tooth = get_ckp_process_tooth();

	process_CKP_tooth = process_CKP_tooth > TW_PERCYL_TEETH ? TW_PERCYL_TEETH : process_CKP_tooth;

	teethCKP = index;

	uint8_t nteeths = TW_PERCYL_TEETH - index0;
	uint32_t diffCKP_e = IC_CKP_Real >= edgeBuf[index0-1].ic ? IC_CKP_Real - edgeBuf[index0-1].ic :  IC_CKP_Real - (-edgeBuf[index0-1].ic - 0xffffffff);

	/*float b_a = 1.0*FBKW_FEEDBACK_ZERO / TW_TOOTH_ALPHA - index0; //remaining angle
	float rpm_a = edgeBuf[index0].rpm;
	float rpm_2 = edgeBuf[index+1].rpm;
	float rpm_b = (rpm_2 - rpm_a)/TW_TOOTH_ALPHA * b_a + rpm_a;*/

	float avgrpm = 0;
	for(int i = index0; i < TW_PERCYL_TEETH + 1; i ++){ //poner security por overfkiw
		avgrpm += edgeBuf[i].rpm;
	}
	avgrpm = avgrpm / nteeths;
	//checkear si restando el tiempo de adquisicion al ic_ckp2

	uint32_t diffCKP_1 = IC_CKP_Real >= edgeBuf[index].ic ? IC_CKP_Real - edgeBuf[index].ic :  IC_CKP_Real - (-edgeBuf[index].ic - 0xffffffff);
	uint32_t diffCKP_2 = edgeBuf[index+1].ic >= IC_CKP_Real  ? edgeBuf[index+1].ic - IC_CKP_Real : edgeBuf[index+1].ic - (-IC_CKP_Real - 0xffffffff);

	uint8_t toNext = diffCKP_1 > diffCKP_2 ? 1 : 0;
	//uint32_t diffCKP = toNext ? diffCKP_2 : diffCKP_1;
	uint32_t diffCKP = IC_CKP_Real >= edgeBuf[index0].ic ? IC_CKP_Real - edgeBuf[index0].ic :  IC_CKP_Real - (-edgeBuf[index0].ic - 0xffffffff);


	//uint32_t diffCKP = IC_CKP2 >= IC_MT ? IC_CKP2 - IC_MT :  IC_CKP2 - (IC_MT - 0xffffffff);
	float diffbot = 0;
	float difftop = 0;
	if(safetySHUTOFF){
		if(RPM < 150){
			Timeout_StopByIndex(12);  // Reset CKP timeout
		}
	}else{
		if(count_CKP > 1){
			if(feedbackNew){
				uint8_t now = process_CKP_tooth;
				uint32_t P_seg = edgeBuf[now].ic - edgeBuf[index0].ic;   // wrap-safe by uint32 modular arithmetic
				uint32_t T_lag = edgeBuf[now].ic - IC_CKP_Real;          // same

				float fraction_before_engine_pulse = 1 - (1.0f*T_lag / P_seg);
				float angle_from_arm_deg = fraction_before_engine_pulse * (now - index0) * TW_TOOTH_ALPHA;

				fb_2 = index0 * TW_TOOTH_ALPHA + angle_from_arm_deg;
			}else{
				fb_2 = avgrpm * 6.0 * diffCKP_e / refClock + (index0-1) * TW_TOOTH_ALPHA;
			}

			fb_2 += - eff_ckp_zero;

			if(fb_2 > FBKW_FEEDBACK_MIN && fb_2 < FBKW_FEEDBACK_MAX){ //si esta entre margenes
				new_fb = fb_2;
				fb_lock = 0;
			}else{
				diffbot = FBKW_FEEDBACK_MIN - fb_2;
				difftop = fb_2 - FBKW_FEEDBACK_MAX;
				valid = 1;
			}

		}else{
			//feedbackfirst = RPM * 6.0 * diffCKP / refClock;
			if(feedbackNew){
				uint8_t now = process_CKP_tooth;
				uint32_t P_seg = edgeBuf[now].ic - edgeBuf[index0].ic;   // wrap-safe by uint32 modular arithmetic
				uint32_t T_lag = edgeBuf[now].ic - IC_CKP_Real;          // same

				float fraction_before_engine_pulse = 1 - (1.0f*T_lag / P_seg);
				float angle_from_arm_deg = fraction_before_engine_pulse * (now - index0) * TW_TOOTH_ALPHA;

				fb_1 = index0 * TW_TOOTH_ALPHA + angle_from_arm_deg;
			}else{
				//fb_1 = (toNext ? -1 : 1) * edgeBuf[index + toNext].rpm * 6.0 * diffCKP / refClock + (teethCKP + toNext) * TW_TOOTH_ALPHA - dFi;
				fb_1 = avgrpm * 6.0 * diffCKP_e / refClock + (index0-1) * TW_TOOTH_ALPHA;
			}

			fb_1 += - eff_ckp_zero;

			if(fb_1 > FBKW_FEEDBACK_MIN && fb_1 < FBKW_FEEDBACK_MAX){
				new_fb = fb_1;
				fb_lock = 0;
				valid = 1;
			}else{
				diffbot = FBKW_FEEDBACK_MIN - fb_1;
				difftop = fb_1 - FBKW_FEEDBACK_MAX;
			}

		}

	}

	if(!valid && count_CKP > 1){ //si no encontro ningun otro nuevo valor dentro de rango, checkear boundaries
		//estas diferencias son positivas cuando hay algun punto fuera.
		if(difftop > 0 && diffbot > 0){
			fb_lock = (difftop > diffbot) > 0 ? 2 : 1;
		}else if(difftop > 0){
			fb_lock = 2;
		}else if(diffbot > 0){
			fb_lock = 1;
		}
	}
	if(fb_lock){ //si se ha encontrado bloqueo, set the value now
		if(fb_lock > 1){
			new_fb = FBKW_FEEDBACK_MAX;
		}else{
			new_fb = FBKW_FEEDBACK_MIN;
		}
		valid = 0;
	}else{
		Timeout_ResetByIndex(12, TIM16->CNT);  // Reset CKP timeout
		Timeout_ResetByIndex(13, TIM16->CNT);
		//si estan fuera de bounds da error.
	}
	FBKW_CKP_ISR();

	FBKW_FEEDBACK -= lpf_fb*(FBKW_FEEDBACK - new_fb - fbkw_offset);

	ckp_process_pending=0;
}

void FBKW_RESET_FEEDBACK(){
	//fb_1 = 0;
	//fb_2 = 0;
	//valid = 0;
	count_CKP = 0;

	uint8_t index0 = FBKW_FEEDBACK_ZERO / TW_TOOTH_ALPHA-1;
	uint8_t nteeths = TW_PERCYL_TEETH - index0;
	uint32_t diff_rpm_to_end = edgeBuf[TW_PERCYL_TEETH].ic >= edgeBuf[index0].ic ? edgeBuf[TW_PERCYL_TEETH].ic - edgeBuf[index0].ic :  edgeBuf[TW_PERCYL_TEETH].ic - (-edgeBuf[index0].ic - 0xffffffff);
	ckp_rpm = 60.0 * refClock / diff_rpm_to_end * (nteeths)/TW_THEETHS;
}



float real_Beta = 0.0;
extern float dPHI1_bip_nom;

void TW_CALC_REAL_BETA(){
	uint8_t index = 0;
	for(uint8_t i = 1; i < TW_PERCYL_TEETH; i++){
		if(edgeBuf[i].ic < IC_INJ && IC_INJ < edgeBuf[i+1].ic){
			index = i;
			break;
		}
	}
	if(!index){
		return;
	}
	uint32_t Inj_Difference = edgeBuf[index + 1].ic >= IC_INJ ? edgeBuf[index+1].ic-IC_INJ : edgeBuf[index+1].ic-(IC_INJ-0xffffffff);
	//uint32_t Inj_Difference = IC_INJ >= edgeBuf[index].ic ? IC_INJ-edgeBuf[index].ic : IC_INJ-(edgeBuf[index].ic-0xffffffff);

	//uint32_t Tooth_Difference = edgeBuf[index+1].ic >= edgeBuf[index].ic ? edgeBuf[index+1].ic-edgeBuf[index].ic : edgeBuf[index+1].ic-(edgeBuf[index].ic-0xffffffff);

	real_Beta = (index+1)*TW_TOOTH_ALPHA - 6.0*edgeBuf[index+1].rpm*Inj_Difference/refClock;
	//real_Beta = index*TW_TOOTH_ALPHA + 6.0*edgeBuf[index].rpm*Inj_Difference/refClock;

	/*float rpm_1 = edgeBuf[index].rpm;
	float rpm_2 = edgeBuf[index+1].rpm;
	float rpm_b = (rpm_2 - rpm_1)/(Tooth_Difference) * Inj_Difference + rpm_1;

	real_Beta = index*TW_TOOTH_ALPHA + 6.0*rpm_b*Inj_Difference/refClock;*/

	real_boi_pending = 0;
}

float correction_beta = 0.0;
volatile float  s_boi_corr_deg = 0.0f;
float correction_boi_accel = 0.0;
float correction_boi_accel_2 = 0.0;

uint8_t boi_eval_pending = 0;
void TW_DEFER_BOI_EVAL(uint32_t ic){
	IC_INJ = ic;
	boi_eval_pending = 1;
}
uint8_t par = 0;
uint8_t filter = 4;

void TW_CALC_INJ_BOI(){
	if(TW_RPM_SENSOR_STATE){
		TW_UPDATE_BOI_COMP();
		TW_UPDATE_INJ_DELAY();
	}
	correction_beta = s_boi_corr_deg + correction_boi_accel_2;
	boi_eval_pending = 0;
}
extern uint32_t T_delay;
extern uint32_t T_delayFI;
float val;

void TW_UPDATE_INJ_DELAY(){
	uint32_t t = TM_TIME_FROM_NEAREST_TOOTH(IC_INJ, 1);

	if(t - (T_delayFI - T_delay) < 100){
		T_delay = t - (T_delayFI - T_delay);
	}else{
		T_delay = 20;
	}

	float inj_rpm = TM_GET_RPM_FROM_NEAREST_TOOTH(IC_INJ, 0);
	INJ_UPDATE_BOI_MARGIN(inj_rpm);
}
// Tunables (per update/event)
static float s_boi_Kp = 0.15f;      // proportional gain
static float s_boi_Ki = 0.00f;      // integral gain (per event)
static float s_boi_max_step_deg = 0.4f; // max per-update change

// State
static float s_boi_I = 0.0f;        // integral accumulator
static float max_boi = 1.8;
void TW_UPDATE_BOI_COMP(void)
{
    float err = PHI1 + dFi - actual_phi1;// + correction_boi_accel_2

    // reject outliers, also clear integral so we don't carry stale windup
    if (err > 4.0f || err < -4.0f) {
    	s_boi_I = 0.0f; return;
    }

    // Unsaturated control
    float u_unsat = s_boi_Kp * err + s_boi_I;

    // Saturate to per-step limit
    float delta = u_unsat;
    if (delta >  s_boi_max_step_deg) delta =  s_boi_max_step_deg;
    if (delta < -s_boi_max_step_deg) delta = -s_boi_max_step_deg;

    // Simple anti-windup: only integrate when not saturated
    if (delta == u_unsat) {
        s_boi_I += s_boi_Ki * err;
        // optional clamp for the integral itself (keeps it reasonable)
        /*float I_lim = s_boi_max_step_deg;
        if (s_boi_I >  I_lim) s_boi_I =  I_lim;
        if (s_boi_I < -I_lim) s_boi_I = -I_lim;*/
    }

    // Apply correction
    s_boi_corr_deg += delta;
    if (s_boi_corr_deg >  max_boi) s_boi_corr_deg =  max_boi;
	if (s_boi_corr_deg < -max_boi) s_boi_corr_deg = -max_boi;
}


//static float accel_m = 0.23; //160 //comparing to last probamos con 5 en el coche si se reduce el error intentar dejarlo fino
//float accel_m_2=4; //antes

//ahora
static float accel_m = 0.35;
float accel_m_2=0;


float lastrpmteeth = 0.0;
float accel= 0;

float drpm_=0;
//float acc = 0;
//float lpf_accboi = 0.1;
uint8_t roundingmode;
extern float last_accel;
int tooth = 0;
void TW_CALCULATE_INSTANT_BOI_CORRECTION(){

	//uint8_t lookuptooth = currentTooth < TW_PERCYL_TEETH ? currentTooth + 1 : 0;
	//uint8_t dtooth = currentTooth >= lookuptooth ? currentTooth + 3 - lookuptooth : lookuptooth + TW_PERCYL_TEETH + 2 - (currentTooth);
	uint8_t dtooth = 30;
	//drpm_ = (TEETH_RPM - edgeBuf[lookuptooth].rpm)/(TEETH_RPM*dtooth);
	/*if(MT_RPM > lastrpmteeth && MT_RPM > TEETH_RPM){
		drpm_ = (TEETH_RPM - MT_RPM)/(TEETH_RPM*currentTooth);
	}else if(MT_RPM < lastrpmteeth && MT_RPM < TEETH_RPM){
		drpm_ = (TEETH_RPM - MT_RPM)/(TEETH_RPM*currentTooth);
	}else{
		//drpm_ = (TEETH_RPM - lastrpmteeth)/(TEETH_RPM*dtooth);
	}*/
	drpm_ = (TEETH_RPM - lastrpmteeth)/(dtooth);
	/*if(drpm_ * last_accel < 20 ){
		drpm_ = 0;
	}*/
	float drpm_2 = (MT_RPM - last_MT_RPM) / MT_RPM;
	//acc -= lpf_accboi*(acc-drpm_);
	//float drpm_ = (TEETH_RPM - lastrpmteeth)/TEETH_RPM;

	float comp = drpm_ * accel_m + drpm_2 * accel_m_2;
	//float comp = last_accel * accel_m;

	//float comp = accel_m * TM_GET_LOCAL_ACC_TO_ANGLE(PHI1 + dFi, roundingmode);
	//float comp = edgeBuf[currentTooth].accel * accel_m;

	//correction_boi_accel_2 = (comp > 0.5 || comp < -0.5) ? 0 : comp ;
	correction_boi_accel_2 = fclamp(comp, -1, 1);
	/*if(correction_boi_accel_2 < -0.4){
		tooth = currentTooth;
		correction_boi_accel_2 = 0;
	}*/
	correction_beta = s_boi_corr_deg + correction_boi_accel_2;
}
void TW_PREPARE_BOI_ACCELCORRECTION(){
	uint8_t index = 0;
	for(uint8_t i = 1; i < TW_PERCYL_TEETH; i++){
		if(edgeBuf[i].ic < IC_INJ && IC_INJ < edgeBuf[i+1].ic){
			index = i;
			break;
		}
	}
	if(!index){
		return;
	}
	lastrpmteeth = edgeBuf[index - 1].rpm;
}

float real_eoi = 0.0;
float correction_eoi = 0.0;
static volatile float  s_eoi_corr_deg = 0.0f;

void TW_DEFER_EOI_EVAL(uint32_t ic){
	IC_EOI = ic;
	eoi_eval_pending = 1;
}
uint8_t inj_rpm_pending = 0;

void TW_CALC_REAL_EOI(){

	real_eoi = TM_INTEGRATE_ANGLE_FROM_NEAREST_TOOTH(IC_EOI, 0); //friggin backwards integration

	real_eoi_pending = 0;
	real_eoi_updated = 1;
	inj_rpm_pending = 1;

}


//float drpm_eoi = 0.0;
//float correction_eoi_accel = 0.0;
void TW_CALCULATE_INSTANT_EOI_CORRECTION(){
	//float drpm_ = (TEETH_RPM - lastrpmteeth)/TEETH_RPM;
	float valid_accel = last_accel;//fclamp(last_accel, -100, 50);
	if(RPM < MIN_RPM){
		return;
	}
	//float comp = fclamp(-valid_accel * eoi_acc_K / RPM, -10, 10);
	//correction_eoi_accel += lpf_eoi_acc_k*(comp-correction_eoi_accel);

	/*if(last_accel < 5 && last_accel > -5){
		//correction_eoi_accel = 0;
		//correction_eoi_accel += lpf_eoi_acc_k*(0-correction_eoi_accel);
	}else{
		correction_eoi_accel += lpf_eoi_acc_k*(comp-correction_eoi_accel);
	}*/
	//correction_eoi_accel = (comp > 2 || comp < -2) ? 0 : comp ;
	//correction_eoi_accel = eoi_acc_K*correction_boi_accel_2;
	//correction_eoi = correction_eoi_accel + s_eoi_corr_deg;
}

static inline float angle_diff_deg(float a, float b)
{
    float d = a - b;
    while (d >  180.0f) d -= 360.0f;
    while (d < -180.0f) d += 360.0f;
    return d;
}
static float s_eoi_Kp = 0.45f;      // proportional gain
static float s_eoi_Ki = 0.00f;      // integral gain (per event)
static float s_eoi_max_step_deg = 0.4f; // max per-update change

// State
static float s_eoi_I = 0.0f;        // integral accumulator

extern float correction_eoi_accel;
float eoi_clamp = 1.67;//sacado del osciloscopio
float eoi_kslow = 0.45;

float cummulative_eoi_error = 0.0;
void TW_UPDATE_EOI_COMP(void)
{
    float target = INJ_GET_TARGET_EOI();

    float err = angle_diff_deg(target, real_eoi);  // wrap-aware
    /*if(!T_ein_status){
    	err = angle_diff_deg(target, TM_INTEGRATE_ANGLE_FROM_NEAREST_TOOTH(IC_EOI + 16*PH_PEAK_DEF,1));
    }*/
    // reject outliers, also clear integral so we don't carry stale windup
    if (err > 30.0f || err < -30.0f) {
    	s_boi_I = 0.0f; return;
    }

    //estas dos lineas son nuevas, la idea es que se suma el nuevo error y se calcula en base a eso
    err += cummulative_eoi_error;
    err /= filter;

    // Unsaturated control
    /*if(err < -0.15 ){
    	err *= eoi_kslow;
    }*/
    float u_unsat = ((err < -0.15) ? eoi_kslow : s_eoi_Kp) * err + s_eoi_I;

    // Saturate to per-step limit
    float delta = u_unsat;
    if (delta >  s_eoi_max_step_deg) delta =  s_eoi_max_step_deg;
    if (delta < -s_eoi_max_step_deg) delta = -s_eoi_max_step_deg;

    // Simple anti-windup: only integrate when not saturated
    if (delta == u_unsat) {
        s_eoi_I += s_eoi_Ki * err;
        // optional clamp for the integral itself (keeps it reasonable)
        /*float I_lim = s_boi_max_step_deg;
        if (s_boi_I >  I_lim) s_boi_I =  I_lim;
        if (s_boi_I < -I_lim) s_boi_I = -I_lim;*/
    }

    // Apply correction
    s_eoi_corr_deg += delta;
    s_eoi_corr_deg = fclamp (s_eoi_corr_deg, -eoi_clamp, eoi_clamp);

    cummulative_eoi_error = 0;
}
void TW_UPDATE_EOI_ERROR(void)
{
    float target = INJ_GET_TARGET_EOI();

    float err = angle_diff_deg(target, real_eoi);  // wrap-aware

    // reject outliers, also clear integral so we don't carry stale windup
    if (err > 30.0f || err < -30.0f) {
    	return;
    }
    cummulative_eoi_error += err;
}
void TW_CALC_INJ_EOI(){
	if(TW_RPM_SENSOR_STATE && !blankInj){
		if(!(par % filter)){
			TW_UPDATE_EOI_COMP(); //igual haciendo promedios se subsana algo

		}else{//este else es nuevo
			TW_UPDATE_EOI_ERROR();
		}
		par++;
		TM_UPDATE_END_DELAY_CORRECTION();
	}
	correction_eoi = s_eoi_corr_deg;
	eoi_eval_pending = 0;
}


extern float T_ein;
extern float dPHI1_bip;

void TW_DEFER_TEIN_VAL(){
	IC_NOM_BIP = IC_TEIN - TEIN_NOMINAL * 16; //16 prescaler
	hasTeinDetEndedFlag = 1;
	phi1_eval_pending = 1;
}

extern float dPHI1_bip_nom_2;
extern float dPHI1_bip_2;
void TW_CALC_BIP_ANGLE(){
	IC_TEIN = IC_INJ + T_ein*16 - TEIN_READING_OFFSET*16; //16 prescaler

	uint8_t index = 0;
	for(uint8_t i = 1; i < TW_PERCYL_TEETH; i++){
		if(edgeBuf[i].ic < IC_TEIN && IC_TEIN <= edgeBuf[i+1].ic){ //si lo llamo antes del siguiente diente nunca llega
			index = i;
			break;
		}
	}
	if(!index){
		return;
	}
	float newphi1 = TM_INTEGRATE_ANGLE_FROM_NEAREST_TOOTH(IC_TEIN, 0); //maybe it gets calculated fromtooth behing
	if(newphi1 > 1){
		actual_phi1 = newphi1;
		phi1_eval_pending = 0;
		real_bip_updated = 1;
	}
	/*if(actual_phi1 < 1){
		index = 0;
	}*/
	//+TM_INTEGRATE_ANGLE_FROM_NEAREST_TOOTH(IC_TEIN, 0)
	//actual_phi1 = TM_GET_REAL_BIP_ANGLE(IC_TEIN);

}

float fclamp(float value, float min, float max) {
    const float t = value < min ? min : value;
    return t > max ? max : t;
}
void TW_Reset_Pending_Tasks(){
	boi_eval_pending = 0;
	eoi_eval_pending = 0;
	phi1_eval_pending = 0;
	ckp_process_pending = 0;
}

void TW_Service(){

	if(real_boi_pending){
		TW_CALC_REAL_BETA();
	}
	if(real_eoi_pending){
		TW_CALC_REAL_EOI();
	}
	if(boi_eval_pending && hasInjectionEnded){ //&& startedEngine
		TW_CALC_INJ_BOI();
	}
	///antes estaba en vez de real eoi, con eoi eval, o sea apenas recibia eoi, pero sin actualizarlo??
	if(eoi_eval_pending && hasInjectionEnded & real_eoi_updated){//&& startedEngine
		TW_CALC_INJ_EOI();
	}

	if(phi1_eval_pending && hasTeinDetEnded){
		TW_CALC_BIP_ANGLE();
	}

	if(real_eoi_updated && real_bip_updated && inj_rpm_pending){
		TM_UPDATE_INJECTION_RPM_AND_ALPHA(real_eoi, actual_phi1, IC_EOI, IC_TEIN);
		inj_rpm_pending = 0;
	}
	if(ckp_process_pending && (currentTooth == process_CKP_tooth)){ // esto es para la 504042, estaria bien revisar el resto...
		TW_CALC_FBKW_FEEDBACK();
	}

}
