/*! *********************************************************************************
* Copyright 2016-2017 NXP
* All rights reserved.
*
* \file
*
* SPDX-License-Identifier: BSD-3-Clause
********************************************************************************** */

#include "fsl_device_registers.h"
#include "fsl_common.h"
#include "fsl_xcvr.h"
#include "fsl_xcvr_trim.h"
#include "dbg_ram_capture.h"
#include "math.h"

/*******************************************************************************
 * Definitions
 ******************************************************************************/


/*******************************************************************************
 * Prototypes
 ******************************************************************************/
void DC_Measure_short(IQ_t chan, DAC_SWEEP_STEP2_t dcoc_init_val);
float calc_dcoc_dac_step(GAIN_CALC_TBL_ENTRY2_T * meas_ptr, GAIN_CALC_TBL_ENTRY2_T * baseline_meas_ptr );

#if defined(__GNUC__)
/* Define the prototype for roundf() so it links properly */
float roundf( float arg );
#endif

/*******************************************************************************
 * Variables
 ******************************************************************************/
static const int8_t TsettleCal = 10;
static GAIN_CALC_TBL_ENTRY2_T measurement_tbl2[NUM_I_Q_CHAN][NUM_SWEEP_STEP_ENTRIES2]; 
static const int8_t sweep_step_values2[NUM_SWEEP_STEP_ENTRIES2] = 
{ 
    0, /* Baseline entry is first and not used in this table */
  -16,
  +16,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  -4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4,
  +4
};


/*******************************************************************************
 * Macros
 ******************************************************************************/
#define ISIGN(x) !((uint16_t)x & 0x8000)
#define ABS(x) ((x) > 0 ? (x) : -(x))

/*******************************************************************************
 * Code
 ******************************************************************************/
/*! *********************************************************************************
 * \brief  This function performs a trim of the BBA DCOC DAC on the DUT
 *
 * \return status - 1 if passed, 0 if failed.
 *
 * \ingroup PublicAPIs
 *
 * \details
 *   Requires the RX to be warmed up before this function is called.
 *   
 ***********************************************************************************/
uint8_t rx_bba_dcoc_dac_trim_shortIQ(void)
{
    uint8_t i;
    float temp_mi = 0;
    float temp_mq = 0;
    float temp_pi = 0;
    float temp_pq = 0;
    float temp_step = 0;
    uint8_t bbf_dacinit_i, bbf_dacinit_q;

    uint32_t dcoc_init_reg_value_dcgain = 0x80802020UL; /* Used in 2nd & 3rd Generation DCOC Trims only. */
    uint32_t bbf_dcoc_step;
    uint32_t bbf_dcoc_step_rcp;
    TZAdcocstep_t tza_dcoc_step[11];
    uint8_t status = 0;
    
    /* Save register values. */  
    uint32_t dcoc_ctrl_0_stack;
    uint32_t dcoc_ctrl_1_stack;
    uint32_t agc_ctrl_1_stack;
    uint32_t rx_dig_ctrl_stack;
    uint32_t dcoc_cal_gain_state;

    XcvrCalDelay(1000);
    dcoc_ctrl_0_stack = XCVR_RX_DIG->DCOC_CTRL_0; /* Save state of DCOC_CTRL_0 for later restore. */
    dcoc_ctrl_1_stack = XCVR_RX_DIG->DCOC_CTRL_1; /* Save state of DCOC_CTRL_1 for later restore. */
    rx_dig_ctrl_stack =  XCVR_RX_DIG->RX_DIG_CTRL; /* Save state of RX_DIG_CTRL for later restore. */
    agc_ctrl_1_stack = XCVR_RX_DIG->AGC_CTRL_1; /* Save state of RX_DIG_CTRL for later restore. */
    dcoc_cal_gain_state = XCVR_RX_DIG->DCOC_CAL_GAIN; /* Save state of DCOC_CAL_GAIN for later restore. */

    /* Ensure AGC, DCOC and RX_DIG_CTRL is in correct mode. */
    XCVR_RX_DIG->RX_DIG_CTRL = (XCVR_RX_DIG->RX_DIG_CTRL & ~XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK) | XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN(0); /* Turn OFF AGC */

    XCVR_RX_DIG->AGC_CTRL_1 = (XCVR_RX_DIG->AGC_CTRL_1 & ~XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN_MASK) | XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) ; /* Set LNA Manual Gain */
    XCVR_RX_DIG->AGC_CTRL_1 = (XCVR_RX_DIG->AGC_CTRL_1 & ~XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN_MASK) | XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) ; /* Set BBA Manual Gain */

    XCVR_RX_DIG->RX_DIG_CTRL = (XCVR_RX_DIG->RX_DIG_CTRL & ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN_MASK) | XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN(0); /* Enable HW DC Calibration -- Disable for SW-DCOC */
    XCVR_RX_DIG->DCOC_CTRL_0 = (XCVR_RX_DIG->DCOC_CTRL_0 & ~XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN_MASK) | XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN(1); /* Enable  Manual DCOC */
    /* DCOC_CTRL_0 @ 4005_C02C -- Define default DCOC DAC settings in manual mode. */
    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(0x20) | XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(0x20) |  XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(0x80) | XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(0x80);
    /* Set DCOC Tracking State. */
    XCVR_RX_DIG->DCOC_CTRL_0 = (XCVR_RX_DIG->DCOC_CTRL_0 & ~XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC_MASK) | XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(0); /* Disables DCOC Tracking when set to 0 */
    /* Apply Manual Gain. */
    XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0x02) | XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(0x00) ;
    XcvrCalDelay(TsettleCal);

    dcoc_init_reg_value_dcgain = XCVR_RX_DIG->DCOC_DAC_INIT; /* Capture DC null setting. */

    bbf_dacinit_i = (dcoc_init_reg_value_dcgain & 0x000000FFU);
    bbf_dacinit_q = (dcoc_init_reg_value_dcgain & 0x0000FF00U) >> 8;

    DC_Measure_short(I_CHANNEL, NOMINAL2);
    DC_Measure_short(Q_CHANNEL, NOMINAL2);

    /* SWEEP Q CHANNEL */
    /* BBF NEG STEP */
    XCVR_RX_DIG->DCOC_DAC_INIT = (XCVR_RX_DIG->DCOC_DAC_INIT & ~XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q_MASK) | XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q-16U);
    XcvrCalDelay(TsettleCal);
    DC_Measure_short(Q_CHANNEL, BBF_NEG);

    /* BBF POS STEP */
    XCVR_RX_DIG->DCOC_DAC_INIT = (XCVR_RX_DIG->DCOC_DAC_INIT & ~XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q_MASK) | XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q+16U);
    XcvrCalDelay(TsettleCal);
    DC_Measure_short(Q_CHANNEL, BBF_POS);

    XCVR_RX_DIG->DCOC_DAC_INIT = dcoc_init_reg_value_dcgain; /* Return DAC setting to initial. */
    XcvrCalDelay(TsettleCal);  

    /* SWEEP I CHANNEL */
    /* BBF NEG STEP */
    XCVR_RX_DIG->DCOC_DAC_INIT = (XCVR_RX_DIG->DCOC_DAC_INIT & ~XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I_MASK) | XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i-16U); 
    XcvrCalDelay(TsettleCal);
    DC_Measure_short(I_CHANNEL, BBF_NEG);
    /* BBF POS STEP  */
    XCVR_RX_DIG->DCOC_DAC_INIT = (XCVR_RX_DIG->DCOC_DAC_INIT & ~XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I_MASK) | XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i+16U); 
    XcvrCalDelay(TsettleCal);
    DC_Measure_short(I_CHANNEL, BBF_POS);

    XCVR_RX_DIG->DCOC_DAC_INIT = dcoc_init_reg_value_dcgain; /* Return DACs to initial. */
    XcvrCalDelay(TsettleCal);

    /* Calculate BBF DCOC STEPS, RECIPROCALS */
    temp_mi = calc_dcoc_dac_step(&measurement_tbl2[I_CHANNEL][BBF_NEG], &measurement_tbl2[I_CHANNEL][NOMINAL2]);
    temp_mq = calc_dcoc_dac_step(&measurement_tbl2[Q_CHANNEL][BBF_NEG], &measurement_tbl2[Q_CHANNEL][NOMINAL2]);
    temp_pi = calc_dcoc_dac_step(&measurement_tbl2[I_CHANNEL][BBF_POS], &measurement_tbl2[I_CHANNEL][NOMINAL2]);
    temp_pq = calc_dcoc_dac_step(&measurement_tbl2[Q_CHANNEL][BBF_POS], &measurement_tbl2[Q_CHANNEL][NOMINAL2]);

    temp_step = (temp_mi+temp_pi+temp_mq+temp_pq)/(float)(4.0);

    bbf_dcoc_step = (uint32_t)roundf(temp_step*(float)(8.0));

    if ((bbf_dcoc_step>265U) & (bbf_dcoc_step<305U)) 
    {
        bbf_dcoc_step_rcp = (uint32_t)roundf((float)0x8000U / temp_step);

        /* Calculate TZA DCOC STEPS & RECIPROCALS and IQ_DC_GAIN_MISMATCH. */
        for (i = (uint32_t)TZA_STEP_N0; i <= (uint32_t)TZA_STEP_N10; i++) /* Relying on enumeration ordering. */
        {
            /* Calculate TZA DCOC STEPSIZE & its RECIPROCAL. */
            switch(i){
            case TZA_STEP_N0: 
                temp_step = (bbf_dcoc_step >> 3U) / 3.6F;
                break;
            case TZA_STEP_N1: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_01_init >> 16)/(xcvr_common_config.dcoc_tza_step_00_init >> 16); 
                break;
            case TZA_STEP_N2: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_02_init >> 16)/(xcvr_common_config.dcoc_tza_step_01_init >> 16); 
                break;
            case TZA_STEP_N3: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_03_init >> 16)/(xcvr_common_config.dcoc_tza_step_02_init >> 16); 
                break;
            case TZA_STEP_N4: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_04_init >> 16)/(xcvr_common_config.dcoc_tza_step_03_init >> 16); 
                break;
            case TZA_STEP_N5: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_05_init >> 16)/(xcvr_common_config.dcoc_tza_step_04_init >> 16); 
                break;
            case TZA_STEP_N6: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_06_init >> 16)/(xcvr_common_config.dcoc_tza_step_05_init >> 16);
                break;
            case TZA_STEP_N7: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_07_init >> 16)/(xcvr_common_config.dcoc_tza_step_06_init >> 16); 
                break;
            case TZA_STEP_N8: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_08_init >> 16)/(xcvr_common_config.dcoc_tza_step_07_init >> 16); 
                break;
            case TZA_STEP_N9: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_09_init >> 16)/(xcvr_common_config.dcoc_tza_step_08_init >> 16); 
                break;
            case TZA_STEP_N10: 
                temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_10_init >> 16)/(xcvr_common_config.dcoc_tza_step_09_init >> 16); 
                break;
            default:
                break;
            }

          tza_dcoc_step[i-(uint32_t)TZA_STEP_N0].dcoc_step = (uint32_t)roundf(temp_step*8);
          tza_dcoc_step[i-(uint32_t)TZA_STEP_N0].dcoc_step_rcp = (uint32_t)roundf((float)0x8000/temp_step);
        }

        /* Make the trims active. */
        XCVR_RX_DIG->DCOC_BBA_STEP = XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP(bbf_dcoc_step) | XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_RECIP(bbf_dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_0 = XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0(tza_dcoc_step[0].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_RCP_0(tza_dcoc_step[0].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_1 = XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_GAIN_1(tza_dcoc_step[1].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_RCP_1(tza_dcoc_step[1].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_2 = XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_GAIN_2(tza_dcoc_step[2].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_RCP_2(tza_dcoc_step[2].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_3 = XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_GAIN_3(tza_dcoc_step[3].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_RCP_3(tza_dcoc_step[3].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_4 = XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_GAIN_4(tza_dcoc_step[4].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_RCP_4(tza_dcoc_step[4].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_5 = XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_GAIN_5(tza_dcoc_step[5].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_RCP_5(tza_dcoc_step[5].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_6 = XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_GAIN_6(tza_dcoc_step[6].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_RCP_6(tza_dcoc_step[6].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_7 = XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_GAIN_7(tza_dcoc_step[7].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_RCP_7(tza_dcoc_step[7].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_8 = XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_GAIN_8(tza_dcoc_step[8].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_RCP_8(tza_dcoc_step[8].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_9 = XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_GAIN_9(tza_dcoc_step[9].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_RCP_9(tza_dcoc_step[9].dcoc_step_rcp) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_10 = XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_GAIN_10(tza_dcoc_step[10].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_RCP_10(tza_dcoc_step[10].dcoc_step_rcp) ;

        status = 1; /* Success */
    }
    else
    {
        status = 0; /* Failure */
    }

    /* Restore Registers. */
    XCVR_RX_DIG->DCOC_CTRL_0 = dcoc_ctrl_0_stack; /* Restore DCOC_CTRL_0 state to prior settings. */
    XCVR_RX_DIG->DCOC_CTRL_1 = dcoc_ctrl_1_stack; /* Restore DCOC_CTRL_1 state to prior settings. */
    XCVR_RX_DIG->RX_DIG_CTRL = rx_dig_ctrl_stack; /* Restore RX_DIG_CTRL state to prior settings. */
    XCVR_RX_DIG->DCOC_CAL_GAIN = dcoc_cal_gain_state; /* Restore DCOC_CAL_GAIN state to prior setting. */
    XCVR_RX_DIG->AGC_CTRL_1 = agc_ctrl_1_stack; /* Save state of RX_DIG_CTRL for later restore. */

    return status;
}

/*! *********************************************************************************
 * \brief  This function performs one point of the DC GAIN calibration process on the DUT
 *
 * \param[in] chan - whether the I or Q channel is being tested.
 * \param[in] stage - whether the BBF or TZA gain stage is being tested.
 * \param[in] dcoc_init_val - the value being set in the ***DCOC_INIT_* register by the parent.
 * \param[in] ext_measmt - the external measurement (in milliVolts) captured by the parent after the ***DCOC_INIT_* register was setup.
 *
 * \ingroup PublicAPIs
 *
 * \details
 *   Relies on a static array to store each point of data for later processing in ::DC_GainCalc().
 *   
 ***********************************************************************************/
void DC_Measure_short(IQ_t chan, DAC_SWEEP_STEP2_t dcoc_init_val)
{
    int16_t dc_meas_i = 0;
    int16_t dc_meas_q = 0;
    int16_t sum_dc_meas_i = 0;
    int16_t sum_dc_meas_q = 0;

    {
        int8_t i;
        const int8_t iterations = 1;
        sum_dc_meas_i = 0;
        sum_dc_meas_q = 0;
        
        for (i = 0; i < iterations; i++)
        {
            rx_dc_sample_average(&dc_meas_i, &dc_meas_q);
            sum_dc_meas_i = sum_dc_meas_i + dc_meas_i;
            sum_dc_meas_q = sum_dc_meas_q + dc_meas_q;
        }
        sum_dc_meas_i = sum_dc_meas_i / iterations;
        sum_dc_meas_q = sum_dc_meas_q / iterations;
    }

    measurement_tbl2[chan][dcoc_init_val].step_value = sweep_step_values2[dcoc_init_val];

    if (chan == I_CHANNEL)
    {
        measurement_tbl2[chan][dcoc_init_val].internal_measurement = dc_meas_i;
    }
    else
    {
        measurement_tbl2[chan][dcoc_init_val].internal_measurement = dc_meas_q;
    }
}  

/*! *********************************************************************************
 * \brief  This function calculates one point of DC DAC step based on digital samples of I or Q.
 *
 * \param[in] meas_ptr - pointer to the structure containing the measured data from internal measurement.
 * \param[in] baseline_meas_ptr - pointer to the structure containing the baseline measured data from internal measurement.
 *
 * \return result of the calculation, the measurement DCOC DAC step value for this measurement point.
 *   
 ***********************************************************************************/
float calc_dcoc_dac_step(GAIN_CALC_TBL_ENTRY2_T * meas_ptr, GAIN_CALC_TBL_ENTRY2_T * baseline_meas_ptr )
{
    static int16_t norm_dc_code;
    static float dc_step;

    /* Normalize internal measurement */
    norm_dc_code = meas_ptr->internal_measurement - baseline_meas_ptr->internal_measurement;
    dc_step = (float)(norm_dc_code) / (float)(meas_ptr->step_value);
    dc_step = (dc_step < 0)? -dc_step: dc_step;

    return dc_step;
}

/*! *********************************************************************************
 * \brief  Temporary delay function 
 *
 * \param[in] none.
 *
 * \return none.
 *
 * \details
 *
 ***********************************************************************************/
void XcvrCalDelay(uint32_t time)
{
    while(time * 32 > 0) /* Time delay is roughly in uSec. */
    {
        time--;
    }
}

/*! *********************************************************************************
 * \brief  This function calculates the average (DC value) based on a smaller set of digital samples of I and Q.
 *
 * \param[in] i_avg - pointer to the location for storing the calculated average for I channel samples.
 * \param[in] q_avg - pointer to the location for storing the calculated average for Q channel samples.
 *
 ***********************************************************************************/
void rx_dc_sample_average(int16_t * i_avg, int16_t * q_avg)
{
    static uint32_t samples[128]; /* 544*2*2 (entire packet ram1/2 size) */
    uint16_t i;
    uint32_t rx_sample;
    uint16_t * sample_ptr;
    uint32_t temp, end_of_rx_wu;
    uint32_t num_iq_samples;
    float avg_i = 0;
    float avg_q = 0;

    num_iq_samples = 128;

    /* Clear the entire allocated sample buffer */
    for (i = 0; i < num_iq_samples; i++)
    {
        samples[i]=0;
    }

    /* Assume this has been called *AFTER* RxWu has completed. */
    /* XCVR_ForceRxWu(); */

    /* Wait for TSM to reach the end of warmup (unless you want to capture some samples during DCOC cal phase) */
    temp = XCVR_TSM->END_OF_SEQ;
    end_of_rx_wu = (temp & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT;
    while ((( XCVR_MISC->XCVR_STATUS & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) >> XCVR_CTRL_XCVR_STATUS_TSM_COUNT_SHIFT ) != end_of_rx_wu) {};

    dbg_ram_init();
    /* Argument below is # of bytes, so *2 (I+Q) and *2 (2bytes/sample) */
#if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1)
    dbg_ram_start_capture(DBG_PAGE_RXDIGIQ, NO_START_TRIG, NO_STOP_TRIG);
    dbg_ram_wait_for_complete();
    dbg_ram_postproc_capture(DBG_PAGE_RXDIGIQ, num_iq_samples * 2 * 2, &samples[0]);
    dbg_ram_release();
#else
    (void)dbg_ram_capture(DBG_PAGE_RXDIGIQ, num_iq_samples * 2 * 2, &samples[0]);
#endif /* RADIO_IS_GEN_3P0 */

    /* Sign extend the IQ samples in place in the sample buffer. */    
    sample_ptr = (uint16_t *)(&samples[0]);
    for (i = 0; i < num_iq_samples * 2; i++)
    {
        rx_sample = *sample_ptr;
        rx_sample |= ((rx_sample & 0x800U) ? 0xF000U : 0x0U); /* Sign extend from 12 to 16 bits. */
        *sample_ptr = rx_sample;
        sample_ptr++;
    }
    
    sample_ptr = (uint16_t *)(&samples[0]);
    for (i = 0; i < num_iq_samples * 2; i += 2)
    {
        static int16_t i_value;
        static int16_t q_value;

        /* Average I & Q channels separately. */
        i_value = *(sample_ptr + i); /* Sign extend from 12 to 16 bits. */
        q_value = *(sample_ptr + i + 1); /* Sign extend from 12 to 16 bits. */
        avg_i += ((float)i_value - avg_i) / (float)(i + 1);  /* Rolling average I */
        avg_q += ((float)q_value - avg_q) / (float)(i + 1);  /* Rolling average Q */
    }
    XcvrCalDelay(10);
    *i_avg = (int16_t)avg_i;
    *q_avg = (int16_t)avg_q;
}

/*! *********************************************************************************
 * \brief  This function calculates the average (DC value) based on a larger set of digital samples of I and Q.
 *
 * \param[in] i_avg - pointer to the location for storing the calculated average for I channel samples.
 * \param[in] q_avg - pointer to the location for storing the calculated average for Q channel samples.
 *
 ***********************************************************************************/
void rx_dc_sample_average_long(int16_t * i_avg, int16_t * q_avg) 
{
    static uint32_t samples[512]; /* 544*2*2 (entire packet ram1/2 size) */
    uint16_t i;
    uint32_t rx_sample;
    uint16_t * sample_ptr;
    uint32_t temp, end_of_rx_wu;
    uint32_t num_iq_samples;
    float avg_i = 0;
    float avg_q = 0;

    num_iq_samples = 512;

    /* Clear the entire allocated sample buffer. */
    for (i = 0; i < num_iq_samples; i++)
    {
        samples[i]=0;
    }

    /* Assume this has been called *AFTER* RxWu has completed. */
    /* XCVR_ForceRxWu(); */

    /* Wait for TSM to reach the end of warmup (unless you want to capture some samples during DCOC cal phase). */
    temp = XCVR_TSM->END_OF_SEQ;
    end_of_rx_wu = (temp & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT;
    while ((( XCVR_MISC->XCVR_STATUS & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) >> XCVR_CTRL_XCVR_STATUS_TSM_COUNT_SHIFT ) != end_of_rx_wu) {};

    dbg_ram_init();
    /* Argument below is # of bytes, so *2 (I+Q) and *2 (2bytes/sample) */
#if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1)
    dbg_ram_start_capture(DBG_PAGE_RXDIGIQ, NO_START_TRIG, NO_STOP_TRIG);
    dbg_ram_wait_for_complete();
    dbg_ram_postproc_capture(DBG_PAGE_RXDIGIQ,num_iq_samples * 2 * 2, &samples[0]);
    dbg_ram_release();
#else
    (void)dbg_ram_capture(DBG_PAGE_RXDIGIQ, num_iq_samples * 2 * 2, &samples[0]);
#endif /* RADIO_IS_GEN_3P0 */

    /* Sign extend the IQ samples in place in the sample buffer. */

    sample_ptr = (uint16_t *)(&samples[0]);
    for (i = 0; i < num_iq_samples * 2; i++)
    {
        rx_sample = *sample_ptr;
        rx_sample |= ((rx_sample & 0x800U) ? 0xF000U : 0x0U); /* Sign extend from 12 to 16 bits. */
        *sample_ptr = rx_sample;
        sample_ptr++;
    }
    
    sample_ptr = (uint16_t *)(&samples[0]);
    for (i = 0; i < num_iq_samples * 2; i += 2)
    {
        static int16_t i_value;
        static int16_t q_value;

        /* Average I & Q channels separately. */
        i_value = *(sample_ptr + i); /* Sign extend from 12 to 16 bits */
        q_value = *(sample_ptr + i + 1); /* Sign extend from 12 to 16 bits */
        avg_i += ((float)i_value - avg_i) / (float)(i + 1); /* Rolling average I */
        avg_q += ((float)q_value - avg_q) / (float)(i + 1); /* Rolling average Q */
    }

    XcvrCalDelay(10);
    *i_avg = (int16_t)avg_i;
    *q_avg = (int16_t)avg_q;
}

/*! *********************************************************************************
 * rx_dc_est_average : Get DC EST values and return the Average
 ***********************************************************************************/
void rx_dc_est_average(int16_t * i_avg, int16_t * q_avg, uint16_t SampleNumber)
{
    float avg_i = 0;
    float avg_q = 0;
    uint16_t i = 0;
    static uint32_t dc_temp, temp;
    uint32_t end_of_rx_wu = 0;
    static int16_t dc_meas_i;
    static int16_t dc_meas_q;

     /* Wait for TSM to reach the end of warmup (unless you want to capture some samples during DCOC cal phase). */
    temp = XCVR_TSM->END_OF_SEQ;
    end_of_rx_wu = (temp & XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_MASK) >> XCVR_TSM_END_OF_SEQ_END_OF_RX_WU_SHIFT;
    while ((( XCVR_MISC->XCVR_STATUS & XCVR_CTRL_XCVR_STATUS_TSM_COUNT_MASK) >> XCVR_CTRL_XCVR_STATUS_TSM_COUNT_SHIFT ) != end_of_rx_wu) {};

    /* Read DCOC DC EST register. */
    for (i = 0; i < SampleNumber; i++)
    {
        dc_temp = XCVR_RX_DIG->DCOC_DC_EST;
        dc_meas_i = dc_temp & XCVR_RX_DIG_DCOC_DC_EST_DC_EST_I_MASK;
        temp = dc_meas_i;
        temp |= ((temp & 0x800U) ? 0xF000U : 0x0U); /* Sign extend from 12 to 16 bits. */
        dc_meas_i = temp;
        avg_i += (float) dc_meas_i;

        dc_meas_q = (dc_temp & XCVR_RX_DIG_DCOC_DC_EST_DC_EST_Q_MASK) >> XCVR_RX_DIG_DCOC_DC_EST_DC_EST_Q_SHIFT;
        temp = dc_meas_q;
        temp |= ((temp & 0x800U) ? 0xF000U : 0x0U); /* Sign extend from 12 to 16 bits. */
        dc_meas_q = temp;
        avg_q += (float) dc_meas_q;
    }

    avg_i /= (float) SampleNumber;
    avg_q /= (float) SampleNumber;

    *i_avg = (int16_t)avg_i;
    *q_avg = (int16_t)avg_q;
}

/*! *********************************************************************************
 * \brief  This function performs a trim of the BBA DCOC DAC on the DUT
 *
 * \return status - 1 if passed, 0 if failed.
 *
 * \ingroup PublicAPIs
 *
 * \details
 *   Requires the RX to be warmed up before this function is called.
 *   
 ***********************************************************************************/
uint8_t rx_bba_dcoc_dac_trim_DCest(void) 
{
    uint8_t i;
    float temp_mi = 0;
    float temp_mq = 0;
    float temp_pi = 0;
    float temp_pq = 0;
    float temp_step = 0;

    uint32_t bbf_dcoc_step;
    uint32_t bbf_dcoc_step_rcp;
    TZAdcocstep_t tza_dcoc_step[11];
    uint8_t status = 0;

    uint8_t bbf_dacinit_i, bbf_dacinit_q;
    uint8_t tza_dacinit_i, tza_dacinit_q;
    int16_t dc_meas_i;
    int16_t dc_meas_q;
    uint32_t dcoc_init_reg_value_dcgain = 0x80802020; /* Used in 2nd & 3rd Generation DCOC Trims only */
    uint32_t temp;

    uint32_t dcoc_ctrl_0_stack;
    uint32_t dcoc_ctrl_1_stack;
    uint32_t agc_ctrl_1_stack;
    uint32_t rx_dig_ctrl_stack;
    uint32_t dcoc_cal_gain_state;

    /* Save register */
    dcoc_ctrl_0_stack = XCVR_RX_DIG->DCOC_CTRL_0; /* Save state of DCOC_CTRL_0 for later restore */
    dcoc_ctrl_1_stack = XCVR_RX_DIG->DCOC_CTRL_1; /* Save state of DCOC_CTRL_1 for later restore */
    rx_dig_ctrl_stack =  XCVR_RX_DIG->RX_DIG_CTRL; /* Save state of RX_DIG_CTRL for later restore */
    agc_ctrl_1_stack = XCVR_RX_DIG->AGC_CTRL_1; /* Save state of RX_DIG_CTRL for later restore */
    dcoc_cal_gain_state = XCVR_RX_DIG->DCOC_CAL_GAIN; /* Save state of DCOC_CAL_GAIN for later restore */

    /* Register config */
    /* Ensure AGC, DCOC and RX_DIG_CTRL is in correct mode */
    temp = XCVR_RX_DIG->RX_DIG_CTRL;
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK; /* Turn OFF AGC */
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN_MASK; /* Disable for SW control of DCOC */
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN_MASK; /* Disable for SW control of DCOC */
    XCVR_RX_DIG->RX_DIG_CTRL = temp;

    XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | /* Enable LNA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | /* Enable BBA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0x0) | /* Set LNA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(0x0); /* Set BBA Manual Gain */

    /* DCOC_CTRL_0 @ 4005_C02C -- Define default DCOC DAC settings in manual mode */
    temp = XCVR_RX_DIG->DCOC_CTRL_0;
    temp |=  XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN(1); /* Enable  Manual DCOC */
    temp |=  XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1); /* Ensure DCOC Tracking is enabled */
    temp |=  XCVR_RX_DIG_DCOC_CTRL_0_DCOC_TRK_EST_OVR(1); /* Enable DC Estimator */
    temp |=  XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1); /* Ensure DC correction is enabled */
    temp &=~ XCVR_RX_DIG_DCOC_CTRL_0_TRACK_FROM_ZERO_MASK; /* Ensure track from zero is disabled during the estimation */
    XCVR_RX_DIG->DCOC_CTRL_0 = temp;

    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(0x20) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(0x20) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(0x80) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(0x80);

#if (RADIO_IS_GEN_3P0)
    /* Clear any prior DC compensation from tracking block */
    XCVR_RX_DIG->DCOC_DIG_MAN = 0x0U;
    force_rx_init_pulse();
    XcvrCalDelay(TsettleCal*4);
#endif

    /* Set default DCOC DAC INIT Value */
    dcoc_init_reg_value_dcgain = XCVR_RX_DIG->DCOC_DAC_INIT; /* Store DCOC DAC INIT values */
    bbf_dacinit_i = (dcoc_init_reg_value_dcgain & 0x000000FFU);
    bbf_dacinit_q = (dcoc_init_reg_value_dcgain & 0x0000FF00U)>>8;
    tza_dacinit_i = (dcoc_init_reg_value_dcgain & 0x00FF0000U)>>16;
    tza_dacinit_q = dcoc_init_reg_value_dcgain >> 24;

    XcvrCalDelay(TsettleCal * 4);
    rx_dc_est_average(&dc_meas_i, &dc_meas_q, 64);
    measurement_tbl2[I_CHANNEL][NOMINAL2].step_value = sweep_step_values2[NOMINAL2];
    measurement_tbl2[Q_CHANNEL][NOMINAL2].step_value = sweep_step_values2[NOMINAL2];
    measurement_tbl2[I_CHANNEL][NOMINAL2].internal_measurement = dc_meas_i;
    measurement_tbl2[Q_CHANNEL][NOMINAL2].internal_measurement = dc_meas_q;

    /* SWEEP I/Q CHANNEL */
    /* BBF NEG STEP */
    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i - 16) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q - 16) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(tza_dacinit_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(tza_dacinit_q);
    XcvrCalDelay(TsettleCal * 2);

    rx_dc_est_average(&dc_meas_i, &dc_meas_q, 64);
    measurement_tbl2[I_CHANNEL][BBF_NEG].step_value = -16;
    measurement_tbl2[Q_CHANNEL][BBF_NEG].step_value = -16;
    measurement_tbl2[I_CHANNEL][BBF_NEG].internal_measurement = dc_meas_i;
    measurement_tbl2[Q_CHANNEL][BBF_NEG].internal_measurement = dc_meas_q;


    /* BBF POS STEP */
    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(bbf_dacinit_i + 16) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(bbf_dacinit_q + 16) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(tza_dacinit_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(tza_dacinit_q);
    XcvrCalDelay(TsettleCal * 2);
    rx_dc_est_average(&dc_meas_i, &dc_meas_q, 64);
    measurement_tbl2[I_CHANNEL][BBF_POS].step_value = +16;
    measurement_tbl2[Q_CHANNEL][BBF_POS].step_value = +16;
    measurement_tbl2[I_CHANNEL][BBF_POS].internal_measurement = dc_meas_i;
    measurement_tbl2[Q_CHANNEL][BBF_POS].internal_measurement = dc_meas_q;

    XCVR_RX_DIG->DCOC_DAC_INIT = dcoc_init_reg_value_dcgain; /* Return DAC setting to initial */

    /* Calculate BBF DCOC STEPS, RECIPROCALS */
    temp_mi = calc_dcoc_dac_step(&measurement_tbl2[I_CHANNEL][BBF_NEG], &measurement_tbl2[I_CHANNEL][NOMINAL2]);
    temp_mq = calc_dcoc_dac_step(&measurement_tbl2[Q_CHANNEL][BBF_NEG], &measurement_tbl2[Q_CHANNEL][NOMINAL2]);
    temp_pi = calc_dcoc_dac_step(&measurement_tbl2[I_CHANNEL][BBF_POS], &measurement_tbl2[I_CHANNEL][NOMINAL2]);
    temp_pq = calc_dcoc_dac_step(&measurement_tbl2[Q_CHANNEL][BBF_POS], &measurement_tbl2[Q_CHANNEL][NOMINAL2]);

    temp_step = (temp_mi + temp_pi + temp_mq + temp_pq) / 4;
    bbf_dcoc_step = (uint32_t)roundf(temp_step * 8U);

    if ((bbf_dcoc_step>250) & (bbf_dcoc_step<305)) 
    {
        bbf_dcoc_step_rcp = (uint32_t)roundf((float)0x8000U / temp_step);

        /* Calculate TZA DCOC STEPS & RECIPROCALS and IQ_DC_GAIN_MISMATCH */
        for (i = TZA_STEP_N0; i <= TZA_STEP_N10; i++)
        {
            /* Calculate TZA DCOC STEPSIZE & its RECIPROCAL */
            switch(i){
                case TZA_STEP_N0:
                    temp_step = (float)bbf_dcoc_step;
                    temp_step =  temp_step/28.8F; /* implementing division by 8 then division by 3.6 (8*3.6=28.8) */
                    break;
                case TZA_STEP_N1:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_01_init >> 16) / (xcvr_common_config.dcoc_tza_step_00_init >> 16);
                    break;
                case TZA_STEP_N2:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_02_init >> 16) / (xcvr_common_config.dcoc_tza_step_01_init >> 16);
                    break;
                case TZA_STEP_N3:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_03_init >> 16) / (xcvr_common_config.dcoc_tza_step_02_init >> 16);
                    break;
                case TZA_STEP_N4:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_04_init >> 16) / (xcvr_common_config.dcoc_tza_step_03_init >> 16);
                    break;
                case TZA_STEP_N5:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_05_init >> 16) / (xcvr_common_config.dcoc_tza_step_04_init >> 16);
                    break;
                case TZA_STEP_N6:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_06_init >> 16) / (xcvr_common_config.dcoc_tza_step_05_init >> 16);
                    break;
                case TZA_STEP_N7:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_07_init >> 16) / (xcvr_common_config.dcoc_tza_step_06_init >> 16);
                    break;
                case TZA_STEP_N8:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_08_init >> 16) / (xcvr_common_config.dcoc_tza_step_07_init >> 16);
                    break;
                case TZA_STEP_N9:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_09_init >> 16) / (xcvr_common_config.dcoc_tza_step_08_init >> 16);
                    break;
                case TZA_STEP_N10:
                    temp_step = temp_step * (xcvr_common_config.dcoc_tza_step_10_init >> 16) / (xcvr_common_config.dcoc_tza_step_09_init >> 16);
                    break;
                default:
                    break;
            }

            tza_dcoc_step[i-TZA_STEP_N0].dcoc_step = (uint32_t)roundf(temp_step * 8);
            tza_dcoc_step[i-TZA_STEP_N0].dcoc_step_rcp = (uint32_t)roundf((float)0x8000 / temp_step);
        }

        /* Make the trims active */
        XCVR_RX_DIG->DCOC_BBA_STEP = XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP(bbf_dcoc_step) | XCVR_RX_DIG_DCOC_BBA_STEP_BBA_DCOC_STEP_RECIP(bbf_dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_0 = XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_GAIN_0(tza_dcoc_step[0].dcoc_step*10) | 
                                       XCVR_RX_DIG_DCOC_TZA_STEP_0_DCOC_TZA_STEP_RCP_0(tza_dcoc_step[0].dcoc_step_rcp/10) ;
        XCVR_RX_DIG->DCOC_TZA_STEP_1 = XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_GAIN_1(tza_dcoc_step[1].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_1_DCOC_TZA_STEP_RCP_1(tza_dcoc_step[1].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_2 = XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_GAIN_2(tza_dcoc_step[2].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_2_DCOC_TZA_STEP_RCP_2(tza_dcoc_step[2].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_3 = XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_GAIN_3(tza_dcoc_step[3].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_3_DCOC_TZA_STEP_RCP_3(tza_dcoc_step[3].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_4 = XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_GAIN_4(tza_dcoc_step[4].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_4_DCOC_TZA_STEP_RCP_4(tza_dcoc_step[4].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_5 = XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_GAIN_5(tza_dcoc_step[5].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_5_DCOC_TZA_STEP_RCP_5(tza_dcoc_step[5].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_6 = XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_GAIN_6(tza_dcoc_step[6].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_6_DCOC_TZA_STEP_RCP_6(tza_dcoc_step[6].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_7 = XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_GAIN_7(tza_dcoc_step[7].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_7_DCOC_TZA_STEP_RCP_7(tza_dcoc_step[7].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_8 = XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_GAIN_8(tza_dcoc_step[8].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_8_DCOC_TZA_STEP_RCP_8(tza_dcoc_step[8].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_9 = XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_GAIN_9(tza_dcoc_step[9].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_9_DCOC_TZA_STEP_RCP_9(tza_dcoc_step[9].dcoc_step_rcp);
        XCVR_RX_DIG->DCOC_TZA_STEP_10 = XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_GAIN_10(tza_dcoc_step[10].dcoc_step) | XCVR_RX_DIG_DCOC_TZA_STEP_10_DCOC_TZA_STEP_RCP_10(tza_dcoc_step[10].dcoc_step_rcp);

        status = 1; /* Success */
    }
    else
    {
        status = 0; /* Failure */
    }

    /* Restore Registers */
    XCVR_RX_DIG->DCOC_CTRL_0 = dcoc_ctrl_0_stack; /* Restore DCOC_CTRL_0 state to prior settings */
    XCVR_RX_DIG->DCOC_CTRL_1 = dcoc_ctrl_1_stack; /* Restore DCOC_CTRL_1 state to prior settings */
    XCVR_RX_DIG->RX_DIG_CTRL = rx_dig_ctrl_stack; /* Restore RX_DIG_CTRL state to prior settings */
    XCVR_RX_DIG->DCOC_CAL_GAIN = dcoc_cal_gain_state; /* Restore DCOC_CAL_GAIN state to prior setting */
    XCVR_RX_DIG->AGC_CTRL_1 = agc_ctrl_1_stack; /* Save state of RX_DIG_CTRL for later restore */

   return status;
}

/*! *********************************************************************************
 * DCOC_DAC_INIT_Cal : slope sign seek depending on measure's sign
 ***********************************************************************************/
void DCOC_DAC_INIT_Cal(uint8_t standalone_operation)
{
    int16_t dc_meas_i = 2000, dc_meas_i_p = 2000;
    int16_t dc_meas_q = 2000, dc_meas_q_p = 2000;
    uint8_t curr_tza_dac_i = 0, curr_tza_dac_q = 0; /* init to zero to avoid uninitialized warning in MCUX builds */
    uint8_t curr_bba_dac_i = 0, curr_bba_dac_q = 0; /* init to zero to avoid uninitialized warning in MCUX builds */
    uint8_t p_tza_dac_i = 0, p_tza_dac_q = 0; /* initialized to 0 to avoid warning in MCUX relase build */
    uint8_t p_bba_dac_i = 0, p_bba_dac_q = 0; /* initialized to 0 to avoid warning in MCUX relase build */
    uint8_t i = 0;
    uint8_t bba_gain = 11;
    bool TZA_I_OK = 0, TZA_Q_OK = 0, BBA_I_OK = 0, BBA_Q_OK = 0;

    uint32_t dcoc_ctrl_0_stack;
    uint32_t dcoc_ctrl_1_stack;
    uint32_t agc_ctrl_1_stack;
    uint32_t rx_dig_ctrl_stack;
    uint32_t dcoc_cal_gain_state;
    uint32_t xcvr_ctrl_stack = 0; /* initialized to 0 to avoid warning in MCUX relase build */

    uint32_t temp;

    /* Save registers */
    dcoc_ctrl_0_stack = XCVR_RX_DIG->DCOC_CTRL_0; /* Save state of DCOC_CTRL_0 for later restore */
    dcoc_ctrl_1_stack = XCVR_RX_DIG->DCOC_CTRL_1; /* Save state of DCOC_CTRL_1 for later restore */
    rx_dig_ctrl_stack =  XCVR_RX_DIG->RX_DIG_CTRL; /* Save state of RX_DIG_CTRL for later restore */
    agc_ctrl_1_stack = XCVR_RX_DIG->AGC_CTRL_1; /* Save state of RX_DIG_CTRL for later restore */
    dcoc_cal_gain_state = XCVR_RX_DIG->DCOC_CAL_GAIN; /* Save state of DCOC_CAL_GAIN for later restore */

    /* WarmUp */
    if (standalone_operation)
    {
        temp = XCVR_MISC->XCVR_CTRL;
        xcvr_ctrl_stack = temp;
        temp &= ~(XCVR_CTRL_XCVR_CTRL_PROTOCOL_MASK);
        temp |= XCVR_CTRL_XCVR_CTRL_PROTOCOL(0);
        XCVR_MISC->XCVR_CTRL = temp;
        XCVR_OverrideChannel(12, 1); /* Calibrate on channel #12, 2.426 GHz in BLE map */
        XCVR_ForceRxWu();
        XcvrCalDelay(2000);
    } 

    /* Register config */
    /* Ensure AGC, DCOC and RX_DIG_CTRL is in correct mode */
    temp = XCVR_RX_DIG->RX_DIG_CTRL;
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK; /* Turn OFF AGC */
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DCOC_CAL_EN_MASK; /* Disable for SW control of DCOC */
    temp &= ~XCVR_RX_DIG_RX_DIG_CTRL_RX_DC_RESID_EN_MASK; /* Disable for SW control of DCOC */
    XCVR_RX_DIG->RX_DIG_CTRL = temp;

    XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) | /* Enable LNA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) | /* Enable BBA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0x0) | /* Set LNA Manual Gain */
                              XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(0x0); /* Set BBA Manual Gain */

    /* DCOC_CTRL_0 @ 4005_C02C -- Define default DCOC DAC settings in manual mode */
    temp = XCVR_RX_DIG->DCOC_CTRL_0;
    temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_MAN(1); /* Enable  Manual DCOC */
    temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_SRC(1); /* Ensure DCOC Tracking is enabled */
    temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_TRK_EST_OVR(1); /* Enable DC Estimator */
    temp |= XCVR_RX_DIG_DCOC_CTRL_0_DCOC_CORRECT_EN(1); /* Ensure DC correction is enabled */
    temp &=~ XCVR_RX_DIG_DCOC_CTRL_0_TRACK_FROM_ZERO_MASK; /* Ensure track from zero is disabled during the estimation */
    XCVR_RX_DIG->DCOC_CTRL_0 = temp;

    XcvrCalDelay(TsettleCal);

    /* Set default DCOC DAC INIT Value */        
    /* LNA and BBA DAC Sweep */
    curr_bba_dac_i = 0x20;
    curr_bba_dac_q = 0x20;
    curr_tza_dac_i = 0x80;
    curr_tza_dac_q = 0x80;
    p_bba_dac_i = 0x20;
    p_bba_dac_q = 0x20;
    p_tza_dac_i = 0x80;
    p_tza_dac_q = 0x80;

    /* Perform a first DC measurement to ensure that measurement is not clipping */
    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q);

    do
    {
        bba_gain--;
        /* Set DAC user gain */
        XCVR_RX_DIG->AGC_CTRL_1 = XCVR_RX_DIG_AGC_CTRL_1_USER_LNA_GAIN_EN(1) |
                                  XCVR_RX_DIG_AGC_CTRL_1_LNA_USER_GAIN(0) | /* 2 */
                                  XCVR_RX_DIG_AGC_CTRL_1_USER_BBA_GAIN_EN(1) |
                                  XCVR_RX_DIG_AGC_CTRL_1_BBA_USER_GAIN(bba_gain) ; /* 10 */
        XcvrCalDelay(TsettleCal * 2);
        rx_dc_est_average(&dc_meas_i, &dc_meas_q, 64);
    } while ((ABS(dc_meas_i) > 1900) | (ABS(dc_meas_q) > 1900));

    for (i = 0; i < 0x0F; i++)
    {
        /* I channel :  */
        if (!TZA_I_OK)
        {   
            if ((ISIGN(dc_meas_i) != ISIGN(dc_meas_i_p)) && (i > 0))
            {    
                if (ABS(dc_meas_i) != MIN(ABS(dc_meas_i), ABS(dc_meas_i_p)))
                {
                    curr_tza_dac_i = p_tza_dac_i; 
                }

                TZA_I_OK = 1;
            }
            else 
            {
                p_tza_dac_i = curr_tza_dac_i;

                if (ISIGN(dc_meas_i)) /* If positif */
                {
                    curr_tza_dac_i--;
                }
                else 
                {
                    curr_tza_dac_i++;
                }
            }
        }
        else /* Sweep BBA I */
        {
            if (!BBA_I_OK)
            {  
                if ((ISIGN(dc_meas_i) != ISIGN(dc_meas_i_p)) && (curr_bba_dac_i != 0x20))
                {   
                    if (ABS(dc_meas_i) != MIN(ABS(dc_meas_i), ABS(dc_meas_i_p)))
                    {
                        curr_bba_dac_i = p_bba_dac_i;
                    }

                    BBA_I_OK = 1;
                }
                else 
                {
                    p_bba_dac_i = curr_bba_dac_i;
                    if (ISIGN(dc_meas_i)) /* If positif */
                    {
                        curr_bba_dac_i--;
                    }
                    else 
                    {
                        curr_bba_dac_i++;
                    }
                }
            }
        }

        /* Q channel : */
        if (!TZA_Q_OK)
        {   
            if ((ISIGN(dc_meas_q) != ISIGN(dc_meas_q_p)) && (i > 0))
            {    
                if (ABS(dc_meas_q) != MIN(ABS(dc_meas_q), ABS(dc_meas_q_p)))
                {
                    curr_tza_dac_q = p_tza_dac_q;
                }
                TZA_Q_OK = 1;
            }
            else
            {
                p_tza_dac_q = curr_tza_dac_q;
                if (ISIGN(dc_meas_q)) /* If positif */
                {
                    curr_tza_dac_q--;
                }
                else 
                {
                    curr_tza_dac_q++;
                } 
            }
        } 
        else  /* Sweep BBA Q */
        {
            if (!BBA_Q_OK)
            {  
                if ((ISIGN(dc_meas_q) != ISIGN(dc_meas_q_p)) && (curr_bba_dac_q != 0x20))
                {   
                    if (ABS(dc_meas_q) != MIN(ABS(dc_meas_q), ABS(dc_meas_q_p)))
                    {
                        curr_bba_dac_q = p_bba_dac_q;
                    }
                    BBA_Q_OK = 1;
                 }
                 else 
                 {
                     p_bba_dac_q = curr_bba_dac_q;
                     if (ISIGN(dc_meas_q)) /* If positif */
                     { 
                       curr_bba_dac_q--;
                     }
                     else 
                     {
                         curr_bba_dac_q++;
                     }
                 }
            }
        }

        /* DC OK break : */
        if (TZA_I_OK && TZA_Q_OK && BBA_I_OK && BBA_Q_OK)
        {
            break;
        }

        dc_meas_i_p = dc_meas_i; /* Store as previous value */
        dc_meas_q_p = dc_meas_q; /* Store as previous value */
        XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) |
                                     XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) |
                                     XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) |
                                     XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q);
        XcvrCalDelay(TsettleCal * 2);
        rx_dc_est_average(&dc_meas_i, &dc_meas_q, 64);
    }

    /* Apply optimized DCOC DAC INIT : */
    XCVR_RX_DIG->DCOC_DAC_INIT = XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_I(curr_bba_dac_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_BBA_DCOC_INIT_Q(curr_bba_dac_q) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_I(curr_tza_dac_i) |
                                 XCVR_RX_DIG_DCOC_DAC_INIT_TZA_DCOC_INIT_Q(curr_tza_dac_q);

    /* WarmDown */
    if (standalone_operation)
    {
        XCVR_ForceRxWd(); /* Don't leave the receiver running. */
        XcvrCalDelay(200);
        XCVR_OverrideChannel(0xFF,1); /* Release channel overrides */
        XCVR_MISC->XCVR_CTRL = xcvr_ctrl_stack;
    }

    /* Restore register */
    XCVR_RX_DIG->DCOC_CTRL_0 = dcoc_ctrl_0_stack; /* Restore DCOC_CTRL_0 state to prior settings */
    XCVR_RX_DIG->DCOC_CTRL_1 = dcoc_ctrl_1_stack; /* Restore DCOC_CTRL_1 state to prior settings */
    XCVR_RX_DIG->RX_DIG_CTRL = rx_dig_ctrl_stack; /* Restore RX_DIG_CTRL state to prior settings */
    XCVR_RX_DIG->DCOC_CAL_GAIN = dcoc_cal_gain_state; /* Restore DCOC_CAL_GAIN state to prior setting */
    XCVR_RX_DIG->AGC_CTRL_1 = agc_ctrl_1_stack; /* Save state of RX_DIG_CTRL for later restore */
}

void force_rx_init_pulse(void)
{
    // Enable RX_INIT override
    XCVR_TSM->OVRD2 &= ~(XCVR_TSM_OVRD2_RX_INIT_OVRD_MASK);  
    XCVR_TSM->OVRD2 |= XCVR_TSM_OVRD2_RX_INIT_OVRD_EN_MASK;  
  
    // Assert RX_INIT override
    XCVR_TSM->OVRD2 |= XCVR_TSM_OVRD2_RX_INIT_OVRD_MASK;  
  
     XcvrCalDelay(2);
  
    // De-assert RX_INIT override
    XCVR_TSM->OVRD2 &= ~(XCVR_TSM_OVRD2_RX_INIT_OVRD_MASK);  
  
    // Disable RX_INIT override
    XCVR_TSM->OVRD2 &= ~(XCVR_TSM_OVRD2_RX_INIT_OVRD_EN_MASK);  
}

#if (RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1)
void SetManAgcAutoDCOC(uint8_t man_agc_idx)
{
    /* Set AGC to manual mode at a specific AGC index */
    XCVR_RX_DIG->AGC_MAN_AGC_IDX = (XCVR_RX_DIG->AGC_MAN_AGC_IDX & ~XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_MAN_IDX_MASK) | XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_MAN_IDX(man_agc_idx);  
    XCVR_RX_DIG->AGC_MAN_AGC_IDX |= XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_DCOC_START_PT_MASK | XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_MAN_IDX_EN_MASK;
    XCVR_RX_DIG->RX_DIG_CTRL |= XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK; /* AGC must be enabled for the manual value to be applied */
    /* DCOC calibration only happens during RX initialization, so pulse the RX INIT signal manually */
    force_rx_init_pulse();
    XCVR_RX_DIG->RX_DIG_CTRL &= ~(XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK); /* AGC must be disabled for the manual value to remain applied */
}

void ClearManAgc(void)
{
    /* Clear manual AGC and return to automatic */
    XCVR_RX_DIG->RX_DIG_CTRL |= XCVR_RX_DIG_RX_DIG_CTRL_RX_AGC_EN_MASK; /* AGC must be enabled for the automatic gain to be resumed */
    XCVR_RX_DIG->AGC_MAN_AGC_IDX &= ~(XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_DCOC_START_PT_MASK | XCVR_RX_DIG_AGC_MAN_AGC_IDX_AGC_MAN_IDX_EN_MASK);
    /* DCOC calibration only happens during RX initialization, so pulse the RX INIT signal manually */
    force_rx_init_pulse();
}
#endif /* RADIO_IS_GEN_3P0 || RADIO_IS_GEN_2P1 */

