/**
  ******************************************************************************
  * @file    tsl_acq.c
  * @author  MCD Application Team
  * @version V2.2.0
  * @date    01-february-2016
  * @brief   This file contains all functions to manage the acquisition.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; COPYRIGHT 2014 STMicroelectronics</center></h2>
  *
  * Licensed under MCD-ST Liberty SW License Agreement V2, (the "License");
  * You may not use this file except in compliance with the License.
  * You may obtain a copy of the License at:
  *
  *        http://www.st.com/software_license_agreement_liberty_v2
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include "tsl_acq.h"
#include "tsl_globals.h"

/* Private typedefs ----------------------------------------------------------*/
/* Private defines -----------------------------------------------------------*/
/* Private macros ------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private functions prototype -----------------------------------------------*/

/**
  * @brief Read all channels measurement of a Bank, calculate Delta
  * @param[in]  idx_bk  Index of the Bank to access
  * @param[in]  mfilter Pointer to the Measure filter function
  * @param[in]  dfilter Pointer to the Delta filter function
  * @retval Status
  */
TSL_Status_enum_T TSL_acq_BankGetResult(TSL_tIndex_T idx_bk, TSL_pFuncMeasFilter_T mfilter, TSL_pFuncDeltaFilter_T dfilter)
{
  TSL_Status_enum_T retval = TSL_STATUS_OK;
  TSL_tIndex_T idx_ch;
  TSL_tIndexDest_T idx_dest;
  TSL_tMeas_T old_meas, new_meas;
  TSL_tDelta_T new_delta;
  CONST TSL_Bank_T *bank = &(TSL_Globals.Bank_Array[idx_bk]);
  CONST TSL_ChannelDest_T *pchDest = bank->p_chDest;
  CONST TSL_ChannelSrc_T *pchSrc = bank->p_chSrc;

  if (idx_bk >= TSLPRM_TOTAL_BANKS)
  {
    return TSL_STATUS_ERROR;
  }

  // For all channels in the bank copy the measure + calculate delta and store them.
  for (idx_ch = 0; idx_ch < bank->NbChannels; idx_ch++)
  {

    // Get the Destination Index of the current channel
    idx_dest = pchDest->IdxDest;

    if (bank->p_chData[idx_dest].Flags.ObjStatus == TSL_OBJ_STATUS_ON)
    {

      // Initialize flag to inform the Object of that a new data is ready
      bank->p_chData[idx_dest].Flags.DataReady = TSL_DATA_READY;

      // Get the new measure (the access is different between acquisitions)
      new_meas = TSL_acq_GetMeas(pchSrc->IdxSrc);

      // Store last measure for the filter below
#if TSLPRM_USE_MEAS > 0
      old_meas = bank->p_chData[idx_dest].Meas;
#else
      old_meas = new_meas;
#endif

      // Store the new measure
#if TSLPRM_USE_MEAS > 0
      bank->p_chData[idx_dest].Meas = new_meas;
#endif

      // Check acquisition value min/max and set acquisition status flag
      if (new_meas < TSL_Params.AcqMin)
      {
        bank->p_chData[idx_dest].Flags.AcqStatus = TSL_ACQ_STATUS_ERROR_MIN;
        bank->p_chData[idx_dest].Delta = 0;
        retval = TSL_STATUS_ERROR;
      }
      else
      {
        if (new_meas > TSL_Params.AcqMax)
        {
          bank->p_chData[idx_dest].Flags.AcqStatus = TSL_ACQ_STATUS_ERROR_MAX;
          bank->p_chData[idx_dest].Delta = 0;
          retval = TSL_STATUS_ERROR;
        }
        else // The measure is OK
        {
          if (TSL_acq_UseFilter(&bank->p_chData[idx_dest]))
          {
            // Apply Measure filter if it exists
            if (mfilter)
            {
              new_meas = mfilter(old_meas, new_meas);
              // Store the measure (optional - used for debug purpose)
#if TSLPRM_USE_MEAS > 0
              bank->p_chData[idx_dest].Meas = new_meas;
#endif
            }

            // Calculate the new Delta
            new_delta = TSL_acq_ComputeDelta(bank->p_chData[idx_dest].Ref, new_meas);

            // Check Noise (TSL_ACQ_STATUS_OK if no Noise or if Noise detection is not supported)
            bank->p_chData[idx_dest].Flags.AcqStatus = TSL_acq_CheckNoise();

            // Apply Delta filter if it exists
            if (dfilter)
            {
              bank->p_chData[idx_dest].Delta = dfilter(new_delta);
            }
            else
            {
              bank->p_chData[idx_dest].Delta = new_delta;
            }
          }
          else
          {
            // Calculate the new Delta
            bank->p_chData[idx_dest].Delta = TSL_acq_ComputeDelta(bank->p_chData[idx_dest].Ref, new_meas);

            // Check Noise (TSL_ACQ_STATUS_OK if no Noise or if Noise detection is not supported)
            bank->p_chData[idx_dest].Flags.AcqStatus = TSL_acq_CheckNoise();
          }
        }
      }
    }

    // Next channel
    pchDest++;
    pchSrc++;

  }

  return retval;
}


/**
  * @brief Calibrate a Bank
  * @param[in] idx_bk  Index of the Bank to access
  * @retval Status
  */
TSL_Status_enum_T TSL_acq_BankCalibrate(TSL_tIndex_T idx_bk)
{
  TSL_Status_enum_T retval;
  TSL_Status_enum_T acq_status;
  TSL_tIndex_T idx_ch;
  TSL_tIndexDest_T idx_dest;
  TSL_tMeas_T new_meas;
  static TSL_tIndex_T calibration_ongoing = 0;
  static TSL_tNb_T calibration_done = 0;
  static TSL_tNb_T div;
  CONST TSL_Bank_T *bank;
  CONST  TSL_ChannelDest_T *pchDest; // Pointer to the current channel
  CONST TSL_ChannelSrc_T *pchSrc; // Pointer to the current channel

  if (idx_bk >= TSLPRM_TOTAL_BANKS)
  {
    return TSL_STATUS_ERROR;
  }

  bank = &(TSL_Globals.Bank_Array[idx_bk]);

  if (calibration_ongoing == 0)
  {
    switch (TSL_Params.NbCalibSamples)
    {
      case 4:
        div = 2;
        break;
      case 16:
        div = 4;
        break;
      default:
        TSL_Params.NbCalibSamples =  8;
        div = 3;
        break;
    }
    // Clear data for all channels of the bank
    TSL_acq_BankClearData(idx_bk);
    // Configure bank
    if (TSL_acq_BankConfig(idx_bk) == TSL_STATUS_OK)
    {
      // Start acquisition
      TSL_acq_BankStartAcq();
      calibration_ongoing = 1; // Calibration started
      calibration_done = TSL_Params.NbCalibSamples;
      retval = TSL_STATUS_BUSY;
    }
    else
    {
      // Stop calibration
      // Clear data for all channels of the bank
      TSL_acq_BankClearData(idx_bk);
      calibration_ongoing = 0;
      retval = TSL_STATUS_ERROR;
    }

  }
  else // Calibration is on-going
  {
    // Check End of Acquisition
    acq_status = TSL_acq_BankWaitEOC();
    if (acq_status == TSL_STATUS_OK)
    {

      // Get the first channel of the bank
      pchDest = bank->p_chDest;
      pchSrc = bank->p_chSrc;

      // Get new measurement for all channels of the bank
      for (idx_ch = 0; idx_ch < bank->NbChannels; idx_ch++)
      {

        // Get index of the current channel
        idx_dest = pchDest->IdxDest;

        // Get the new measure (the access is different between acquisitions)
        new_meas = TSL_acq_GetMeas(pchSrc->IdxSrc);

        // Check min/max and set status flag
        if ((new_meas < TSL_Params.AcqMin) || (new_meas > TSL_Params.AcqMax))
        {
          // Stop calibration
          // Clear data for all channels of the bank
          TSL_acq_BankClearData(idx_bk);
          calibration_ongoing = 0;
          return TSL_STATUS_ERROR;
        }
        else
        {
          // Add the measure
          bank->p_chData[idx_dest].Ref += new_meas;
        }

        // Next channel
        pchDest++;
        pchSrc++;
      }

      // Check that we have all the needed measurements
      calibration_done--;
      if (calibration_done == 0)
      {

        // Get the first channel of the bank
        pchDest = bank->p_chDest;

        // Calculate the Reference for all channels of the bank
        for (idx_ch = 0; idx_ch < bank->NbChannels; idx_ch++)
        {
          // Get index of the current channel
          idx_dest = pchDest->IdxDest;
          // Divide the Reference by the number of samples
          bank->p_chData[idx_dest].Ref >>= div;
          // Next channel
          pchDest++;
        }

        // End
        calibration_ongoing = 0;
        retval = TSL_STATUS_OK;
      }
      else // Restart a new measurement on the bank
      {
        TSL_acq_BankStartAcq();
        retval = TSL_STATUS_BUSY;
      }
    }
    else
      if (acq_status == TSL_STATUS_ERROR)
      {
        // Stop calibration
        // Clear data for all channels of the bank
        TSL_acq_BankClearData(idx_bk);
        calibration_ongoing = 0;
        retval = TSL_STATUS_ERROR;
      }
      else
      {
        retval = TSL_STATUS_BUSY;
      }
  }

  return retval;
}


/**
  * @brief Clear Reference and Delta on all channels of a Bank
  * @param[in] idx_bk  Index of the Bank to access
  * @retval None
  */
void TSL_acq_BankClearData(TSL_tIndex_T idx_bk)
{
  TSL_tIndex_T idx_ch;
  TSL_tIndexDest_T idx_Dest;
  CONST TSL_Bank_T *bank = &(TSL_Globals.Bank_Array[idx_bk]);
  CONST TSL_ChannelDest_T *pchDest = bank->p_chDest;

  if (idx_bk >= TSLPRM_TOTAL_BANKS)
  {
    return;
  }

  // For all channels of the bank
  for (idx_ch = 0; idx_ch < bank->NbChannels; idx_ch++)
  {
    idx_Dest = pchDest->IdxDest;
    bank->p_chData[idx_Dest].Ref = 0;
    bank->p_chData[idx_Dest].Delta = 0;
    pchDest++; // Next channel
  }
}

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
