
 /******************************************************************************
*
* @file    ah_counter.c
* @author  ECS, Falko Jahn
* @version V1.0.0
* @date    2020-05-01
* @brief
*
******************************************************************************/

//	--- INCLUDES -----------------------------------------------------------------
#include "main.h"
#include "math.h"
#include "sysdata.h"
#include "ah_counter.h"
#include "wh_counter.h"
#include "eeprom.h"
//	--- EXTERNE VARIABLEN --------------------------------------------------------

//	--- LOKALE DEFINES - bitte hier dokumentieren --------------------------------

//	--- LOKALE TYPE DEFS - bitte hier dokumentieren-------------------------------

//	--- DEFINITIONEN GLOBALER VARIABLEN - Bitte in Header dokumentieren ----------

//	--- LOKALE VARIABLEN - bitte hier dokumentieren ------------------------------
int startMeasurement = 0;
//	--- LOKALE FUNKTIONS PROTOTYPEN ----------------------------------------------
int getSocAhRated(void);
int getSocAhAuto(void);

//int64_t mAs_AutoMode;


void AH_COUNTER_Init(void)
{
  sys_data.s.values.mAs_AutoMode =   (int32_t)-sys_data.s.parameter.cellCapacity * 3600;;
}


//	--- LOKALE FUNKTIONEN - bitte hier dokumentieren -----------------------------
int getSocAhRated(void)
{
    int64_t cellCapacitySeconds =  (int64_t)sys_data.s.parameter.cellCapacity * 60 * 60; // Umrechnung mAh zu mAs
	return (100000 * sys_data.s.values.mAsCounter)  / cellCapacitySeconds;
}


int getSocAhAuto(void)
{

	const int64_t _100mPercent = 100000LL;


	int64_t mAh_AutoMode = sys_data.s.values.mAh_AutoMode < 0 ? -sys_data.s.values.mAh_AutoMode : 0;
	int64_t tmp = 0LL;
	if (sys_data.s.values.detectedCapacity <= 0)
        {
          tmp = _100mPercent - (mAh_AutoMode * _100mPercent) / (int64_t)sys_data.s.parameter.cellCapacity;
        }
        else
        {
          tmp = _100mPercent - (mAh_AutoMode * _100mPercent) / (int64_t)sys_data.s.values.detectedCapacity;
	}

	if (tmp > _100mPercent) tmp = _100mPercent;
	else if (tmp <= 0) tmp = 0LL;
	return tmp;
 }


//	--- GLOBALE FUNKTIONEN - bitte in Header dokumentieren------------------------

void AH_COUNTER_Exec(void)
{
  double iBatDivIbatNenn = 0;
  double current = 0;
  double peukert = 0;
  double calcPow = 0;
  double cef = 0;
  double soc = 0;
  int64_t maxCurrentForBatteryFullDetection = 0;
  static int16_t batteryFullCounter = 0;
  static uint64_t totalDischarge = 0;
  static uint64_t totalCharge = 0;

  int64_t cellCapacitySeconds =  (int64_t)sys_data.s.parameter.cellCapacity * 60 * 60; // Umrechnung mAh zu mAs


  if (totalDischarge == 0) totalDischarge = sys_data.s.values.dischargeTotalAh * 3600000;
  if (totalCharge == 0) totalCharge = sys_data.s.values.chargeTotalAh * 3600000;



  // bei Strom größer 0 -> Ladestrom CEF rechnen
  if(sys_data.s.values.batteryCurrent >= 0)
  {
	  //99 --> 99% --> 0.99
	  cef =  sys_data.s.parameter.cef / 100.0;
	  sys_data.s.values.batteryCurrentCorrected = sys_data.s.values.batteryCurrent * cef;
  }
  else
  {	  // bei Strom kleiner 0 peukert rechnen
	  //int32_t ratedCurrent = sys_data.s.parameter.cellRatedCurrent * 1000;
	  int32_t ratedCurrent = sys_data.s.parameter.cellCapacity / sys_data.s.parameter.cellRatedDischargeTime;

	  if (sys_data.s.values.batteryCurrent < -ratedCurrent) //ACHTUNG mit Minus das vorzeichen gedreht!
	  {
		  current = sys_data.s.values.batteryCurrent;
		  iBatDivIbatNenn = current / ratedCurrent;
		  iBatDivIbatNenn = -iBatDivIbatNenn;
		  peukert = (sys_data.s.parameter.peukert / 100.0);
		  calcPow = pow(iBatDivIbatNenn , peukert - 1.0);
		  sys_data.s.values.batteryCurrentCorrected = (current * calcPow);
	  }
	  else sys_data.s.values.batteryCurrentCorrected = sys_data.s.values.batteryCurrent;
  }

  sys_data.s.values.batteryCurrentCorrected -= (int32_t)sys_data.s.parameter.extraDischargeStrom_mA;

  // Counting negative current
  if (sys_data.s.values.batteryCurrent < 0)
  {
	  totalDischarge += -sys_data.s.values.batteryCurrent;
	  sys_data.s.values.dischargeTotalAh = totalDischarge / 3600000; //Umrechnung von mAs auf Ah


	  sys_data.s.values.fullCyclesCnt = (uint16_t) ((sys_data.s.values.dischargeTotalAh * 1000) / sys_data.s.parameter.cellCapacity);
  }
  else
  {
	  totalCharge += sys_data.s.values.batteryCurrent;
	  sys_data.s.values.chargeTotalAh = totalCharge / 3600000; //Umrechnung von mAs auf Ah
  }



  // Aufsummieren
  sys_data.s.values.mAsCounter += sys_data.s.values.batteryCurrentCorrected;
  sys_data.s.values.mAs_AutoMode += (int64_t)sys_data.s.values.batteryCurrentCorrected;
  sys_data.s.values.mAh_AutoMode = sys_data.s.values.mAs_AutoMode / 3600LL;

  // Begrenzen, Batterie darf nicht über 100% gehen
  if (sys_data.s.values.mAsCounter > cellCapacitySeconds) sys_data.s.values.mAsCounter = cellCapacitySeconds;

  if (sys_data.s.values.mAs_AutoMode > 0)
  {
	sys_data.s.values.mAs_AutoMode = 0;
  }

  //Prüfe Battery Voll Bedinungen
  maxCurrentForBatteryFullDetection = sys_data.s.parameter.cellCapacity * sys_data.s.parameter.iBatFull / 100.0;

  if (sys_data.s.values.batteryVoltage > sys_data.s.parameter.uBatFull && sys_data.s.values.batteryCurrent <  maxCurrentForBatteryFullDetection)
  {
    batteryFullCounter++;
  }
  else
  {
    batteryFullCounter = 0;
  }

  if (batteryFullCounter > sys_data.s.parameter.tBatFull)
  {
    sys_data.s.values.mAsCounter = cellCapacitySeconds;
	sys_data.s.values.mAs_AutoMode = 0;
	// Here we can set Wh to max
	WH_COUNTER_SetToMax();

	//und wir starten eine neue Battery Kapazitäts und Energiemessung
	startMeasurement = 1;
  }

  sys_data.s.values.mAhCounter = sys_data.s.values.mAsCounter / 3600LL;

  static uint16_t cnt;
  if (sys_data.s.parameter.batteryEmptyDetectionMode == 0)
  {
    if (sys_data.s.values.batteryVoltage < sys_data.s.values.uBatEmptyTempComp && sys_data.s.values.batteryVoltage > 1000) // Verhindert das beim abziehen der Sense ein Batt Empty erkannt wird
    {
  	  cnt++;
  	  if ((cnt >= 10) && (startMeasurement == 1)) // 5 Sekunden fest
  	  {
  		  cnt = 10; //sys_data.s.parameter.tBatFull;

  		  if ((sys_data.s.values.lastTimeVbatFull >= 3600U) && (sys_data.s.values.lastTimeVbatFull <= 200U * 3600U)) 	// This line prevents from very high discharge-currents to be used to estimate battery capacity
  		  {
  			  // This line is not so important anymore, because we do not allow mAh_AutoMode to be greater than zero
  			  sys_data.s.values.detectedCapacity = sys_data.s.values.mAh_AutoMode >= 0 ? sys_data.s.values.mAh_AutoMode : -sys_data.s.values.mAh_AutoMode;
  			  WH_COUNTER_SetDetectedEnergy();
  			  startMeasurement = 0;			  
  		  }
  		  sys_data.s.values.lastTimeVbatEmpty = 0U;
  	  }
    }
    else 
    {
      cnt = 0;
    }
  }
  else
  {
	// Neuer Modus. Spannungsmessung wird ignoriert. Erkannt wird Batt Leer mit LVP Signal von LiPro
	// OVP darf nicht ausgehen, sonst handelt es sich um ein Temperaturabschaltung oder ein andere Fehler
	// 1000mV als Schwelle um sicher vor rauschen um den Nullpunkt zu seinzu sein
    if ((sys_data.s.values.ovp_sense > 1000) && (sys_data.s.values.lvp_sense < 1000)) 
    {
  	  cnt++;
  	  if ((cnt >= 10) && (startMeasurement == 1)) // 5 Sekunden fest
  	  {
  		  cnt = 10; //sys_data.s.parameter.tBatFull;

  		  if ((sys_data.s.values.lastTimeVbatFull >= 3600U) && (sys_data.s.values.lastTimeVbatFull <= 240U * 3600U)) 	// This line prevents from very high discharge-currents to be used to estimate battery capacity
  		  {
  			  // This line is not so important anymore, because we do not allow mAh_AutoMode to be greater than zero
  			  sys_data.s.values.detectedCapacity = sys_data.s.values.mAh_AutoMode >= 0 ? sys_data.s.values.mAh_AutoMode : -sys_data.s.values.mAh_AutoMode;
  			  WH_COUNTER_SetDetectedEnergy();
  			  startMeasurement = 0;			  
  		  }
  		  sys_data.s.values.lastTimeVbatEmpty = 0U;
  	  }
    }
    else 
    {
      cnt = 0;
    }
  }


  switch (sys_data.s.parameter.socCalcMode)
  {
	  case SOC_CALC_MODE_AH_RATED:		sys_data.s.values.soc = getSocAhRated();			  break;
	  case SOC_CALC_MODE_AH_AUTO:		sys_data.s.values.soc = getSocAhAuto();				  break;
	  case SOC_CALC_MODE_WH_RATED:		sys_data.s.values.soc = WH_COUNTER_GetSoCManual();	  break;
	  case SOC_CALC_MODE_WH_AUTO:		sys_data.s.values.soc = WH_COUNTER_GetSoCAuto();	  break;
	  case SOC_CALC_MODE_WH_AUTO_TEMP:	sys_data.s.values.soc = WH_COUNTER_GetSoCAutoTemp();  break;
	  default: sys_data.s.values.soc = 0;
  }
}