/*******************************************************************************
    OpenAirInterface 
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is 
    included in this distribution in the file called "COPYING". If not, 
    see <http://www.gnu.org/licenses/>.

   Contact Information
   OpenAirInterface Admin: openair_admin@eurecom.fr
   OpenAirInterface Tech : openair_tech@eurecom.fr
   OpenAirInterface Dev  : openair4g-devel@lists.eurecom.fr
  
   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

 *******************************************************************************/

/** bladerf_lib.c
 *
 * Author: navid nikaein
 */


#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include "bladerf_lib.h"

/** @addtogroup _BLADERF_PHY_RF_INTERFACE_
 * @{
 */

//! Number of BladeRF devices 
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
 
#ifdef __AVX2__
#  include <immintrin.h>
#endif

int num_devices=0;

/*These items configure the underlying asynch stream used by the the sync interface. 
 */

/*! \brief BladeRF Init function (not used at the moment)
 * \param device RF frontend parameters set by application
 */
int trx_brf_init(openair0_device *device) {
  
}

/*! \brief get current timestamp
 *\param device the hardware to use 
 *\param module the bladeRf module
 */
 
openair0_timestamp trx_get_timestamp(openair0_device *device, bladerf_module module) {
  int status;
  struct bladerf_metadata meta;
  brf_state_t *brf = (brf_state_t*)device->priv;
  memset(&meta, 0, sizeof(meta));
  
  if ((status=bladerf_get_timestamp(brf->dev, module, &meta.timestamp)) != 0) {
    fprintf(stderr,"Failed to get current %s timestamp: %s\n",(module == BLADERF_MODULE_RX ) ? "RX" : "TX", bladerf_strerror(status));
    return -1; 
  } // else {printf("Current RX timestampe  0x%016"PRIx64"\n", meta.timestamp); }

  return meta.timestamp;
}

/*! \brief Start BladeRF
 *\param device the hardware to use 
 */
int trx_brf_start(openair0_device *device) {

  return 0;
}

/*! \brief Get BladeRF stats
 *\param device the hardware to use 
 */
static void trx_brf_stats(openair0_device *device){


}

/*! \brief Called to send samples to the BladeRF RF target
      @param device pointer to the device structure specific to the RF hardware target
      @param timestamp The timestamp at whicch the first sample MUST be sent 
      @param buff Buffer which holds the samples
      @param nsamps number of samples to be sent
      @param cc index of the component carrier
*/ 
static int trx_brf_write(openair0_device *device,openair0_timestamp ptimestamp, void **buff, int nsamps, int cc) {
  
  int status;
  brf_state_t *brf = (brf_state_t*)device->priv;
  /* BRF has only 1 rx/tx chaine : is it correct? */
  int16_t *samples = (int16_t*)buff[0];
  
  //memset(&brf->meta_tx, 0, sizeof(brf->meta_tx));
  // When  BLADERF_META_FLAG_TX_NOW is used the timestamp is not used, so one can't schedule a tx 
  if (brf->meta_tx.flags == 0 ) 
    brf->meta_tx.flags = (BLADERF_META_FLAG_TX_BURST_START);// | BLADERF_META_FLAG_TX_BURST_END);// |  BLADERF_META_FLAG_TX_NOW);
  
  
  brf->meta_tx.timestamp= (uint64_t) (ptimestamp); 
  status = bladerf_sync_tx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_tx, 2*brf->tx_timeout_ms);
 
  if (brf->meta_tx.flags == BLADERF_META_FLAG_TX_BURST_START) 
    brf->meta_tx.flags =  BLADERF_META_FLAG_TX_UPDATE_TIMESTAMP;
  

  if (status != 0) {
    //fprintf(stderr,"Failed to TX sample: %s\n", bladerf_strerror(status));
    brf->num_tx_errors++;
    brf_error(status);
  } else if (brf->meta_tx.status & BLADERF_META_STATUS_UNDERRUN){
    /* libbladeRF does not report this status. It is here for future use. */ 
    fprintf(stderr, "TX Underrun detected. %u valid samples were read.\n",  brf->meta_tx.actual_count);
    brf->num_underflows++;
  } 
  //printf("Provided TX timestampe  %u, meta timestame %u\n", ptimestamp,brf->meta_tx.timestamp);
  
  //    printf("tx status %d \n",brf->meta_tx.status);
  brf->tx_current_ts=brf->meta_tx.timestamp;
  brf->tx_actual_nsamps+=brf->meta_tx.actual_count;
  brf->tx_nsamps+=nsamps;
  brf->tx_count++;
  

  return(0);
}

/*! \brief Receive samples from hardware.
 * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
 * the first channel. *ptimestamp is the time at which the first sample
 * was received.
 * \param device the hardware to use
 * \param[out] ptimestamp the time at which the first sample was received.
 * \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of samples \ref nsamps.
 * \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
 * \param cc  Index of component carrier
*/
static int trx_brf_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {

  int status=0;
  brf_state_t *brf = (brf_state_t*)device->priv;
  
  // BRF has only one rx/tx chain
  int16_t *samples = (int16_t*)buff[0];
  
  brf->meta_rx.flags = BLADERF_META_FLAG_RX_NOW;
  status = bladerf_sync_rx(brf->dev, samples, (unsigned int) nsamps, &brf->meta_rx, 2*brf->rx_timeout_ms);
  
  //  printf("Current RX timestampe  %u, nsamps %u, actual %u, cc %d\n",  brf->meta_rx.timestamp, nsamps, brf->meta_rx.actual_count, cc);
   
  if (status != 0) {
    fprintf(stderr, "RX failed: %s\n", bladerf_strerror(status)); 
    //    printf("RX failed: %s\n", bladerf_strerror(status)); 
    brf->num_rx_errors++;
  } else if ( brf->meta_rx.status & BLADERF_META_STATUS_OVERRUN) {
    brf->num_overflows++;
    printf("RX overrun (%d) is detected. t=%u. Got %u samples. nsymps %d\n", 
	   brf->num_overflows,brf->meta_rx.timestamp,  brf->meta_rx.actual_count, nsamps);
  } 
  //printf("Current RX timestampe  %u\n",  brf->meta_rx.timestamp);
  //printf("[BRF] (buff %p) ts=0x%"PRIu64" %s\n",samples, brf->meta_rx.timestamp,bladerf_strerror(status));
  brf->rx_current_ts=brf->meta_rx.timestamp;
  brf->rx_actual_nsamps+=brf->meta_rx.actual_count;
  brf->rx_nsamps+=nsamps;
  brf->rx_count++;
  
  
  *ptimestamp = brf->meta_rx.timestamp;
 
  return brf->meta_rx.actual_count;

}

/*! \brief Terminate operation of the BladeRF transceiver -- free all associated resources 
 * \param device the hardware to use
 */
int trx_brf_end(openair0_device *device) {

  int status;
  brf_state_t *brf = (brf_state_t*)device->priv;
  // Disable RX module, shutting down our underlying RX stream
  if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, false))  != 0) {
    fprintf(stderr, "Failed to disable RX module: %s\n", bladerf_strerror(status));
  }
  if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, false))  != 0) {
    fprintf(stderr, "Failed to disable TX module: %s\n",  bladerf_strerror(status));
  }
  bladerf_close(brf->dev);
  return 0;
}

/*! \brief print the BladeRF statistics  
* \param device the hardware to use
* \returns  0 on success
*/
int trx_brf_get_stats(openair0_device* device) {

  return(0);

}

/*! \brief Reset the BladeRF statistics  
* \param device the hardware to use
* \returns  0 on success
*/
int trx_brf_reset_stats(openair0_device* device) {

  return(0);

}

/*! \brief Stop USRP
 * \param device the hardware to use
 */
int trx_brf_stop(openair0_device* device) {

  return(0);

}

/*! \brief Set frequencies (TX/RX)
 * \param device the hardware to use
 * \returns 0 in success 
 */
int trx_brf_set_freq(openair0_device* device) {

  int status;
  brf_state_t *brf = (brf_state_t *)device->priv;
  openair0_config_t *openair0_cfg = (openair0_config_t *)device->openair0_cfg;


  if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->tx_freq[0])) != 0){
    fprintf(stderr,"Failed to set TX frequency: %s\n",bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set TX Frequency to %u\n", (unsigned int) openair0_cfg->tx_freq[0]);

  if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_freq[0])) != 0){
    fprintf(stderr,"Failed to set RX frequency: %s\n",bladerf_strerror(status));
    brf_error(status);
  } else 
    printf("[BRF] set RX frequency to %u\n",(unsigned int)openair0_cfg->rx_freq[0]);

  return(0);

}

/*! \brief Set Gains (TX/RX)
 * \param device the hardware to use
 * \returns 0 in success 
 */
int trx_brf_set_gains(openair0_device* device) {

  return(0);

}



#define RXDCLENGTH 16384
int16_t cos_fsover8[8]  = {2047,   1447,      0,  -1448,  -2047,  -1448,     0,   1447};
int16_t cos_3fsover8[8] = {2047,  -1448,      0,   1447,  -2047,   1447,     0,  -1448};

rx_gain_calib_table_t calib_table_fx4[] = {
  {2300000000.0,53.5},
  {1880000000.0,57.0},
  {816000000.0,73.0},
  {-1,0}};

void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index) {

  int i=0;
  // loop through calibration table to find best adjustment factor for RX frequency
  double min_diff = 6e9,diff;
  
  while (openair0_cfg->rx_gain_calib_table[i].freq>0) {
    diff = fabs(openair0_cfg->rx_freq[chain_index] - openair0_cfg->rx_gain_calib_table[i].freq);
    printf("cal %d: freq %f, offset %f, diff %f\n",
	   i,
	   openair0_cfg->rx_gain_calib_table[i].freq,
	   openair0_cfg->rx_gain_calib_table[i].offset,diff);
    if (min_diff > diff) {
      min_diff = diff;
      openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset;
    }
    i++;
  }
  
}

void calibrate_rf(openair0_device *device) {


  brf_state_t *brf = (brf_state_t *)device->priv;
  openair0_timestamp ptimestamp;
  int16_t *calib_buffp,*calib_tx_buffp;
  int16_t calib_buff[2*RXDCLENGTH];
  int16_t calib_tx_buff[2*RXDCLENGTH];
  int i,j,offI,offQ,offIold,offQold,offInew,offQnew,offphase,offphaseold,offphasenew,offgain,offgainold,offgainnew;
  int32_t meanI,meanQ,meanIold,meanQold;
  int cnt=0,loop;

  // put TX on a far-away frequency to avoid interference in RX band
  bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->rx_freq[0] + 200e6);  
  // Set gains to close to max
  bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, 60);
  bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, 60);

  // fill TX buffer with fs/8 complex sinusoid
  j=0;
  for (i=0;i<RXDCLENGTH;i++) {
    calib_tx_buff[j++] = cos_fsover8[i&7];
    calib_tx_buff[j++] = cos_fsover8[(i+6)&7];  // sin
  }
  calib_buffp = &calib_buff[0];
  calib_tx_buffp = &calib_tx_buff[0];
  // Calibrate RX DC offset

  offIold=offQold=2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offIold);
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQold);
  for (i=0;i<10;i++)
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
  
  for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
    meanIold+=calib_buff[j++];
    meanQold+=calib_buff[j++];
  }
  meanIold/=RXDCLENGTH;
  meanQold/=RXDCLENGTH;
  printf("[BRF] RX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold);

  offI=offQ=-2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offI);
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQ);
  for (i=0;i<10;i++)
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
  
  for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
    meanI+=calib_buff[j++];
    meanQ+=calib_buff[j++];
  }
  meanI/=RXDCLENGTH;
  meanQ/=RXDCLENGTH;
  //  printf("[BRF] RX DC: (%d,%d) => (%d,%d)\n",offI,offQ,meanI,meanQ);

  while (cnt++ < 12) {

    offInew=(offIold+offI)>>1;
    offQnew=(offQold+offQ)>>1;

    if (meanI*meanI < meanIold*meanIold) {
      meanIold = meanI;
      offIold = offI;
      printf("[BRF] *** RX DC: offI %d => %d\n",offIold,meanI);
    }
    if (meanQ*meanQ < meanQold*meanQold) {
      meanQold = meanQ;
      offQold = offQ;
      printf("[BRF] *** RX DC: offQ %d => %d\n",offQold,meanQ);
    }
    offI = offInew;
    offQ = offQnew;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offI);
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQ);

    for (i=0;i<10;i++)
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      meanI+=calib_buff[j++];
      meanQ+=calib_buff[j++];
    }
    meanI/=RXDCLENGTH;
    meanQ/=RXDCLENGTH;
    printf("[BRF] RX DC: (%d,%d) => (%d,%d)\n",offI,offQ,meanI,meanQ);
  }

  printf("[BRF] RX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold);
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_I,offIold);
  bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_LMS_DCOFF_Q,offQold);

  // TX DC offset
  // PUT TX as f_RX + fs/4
  // loop back BLADERF_LB_RF_LNA1
  bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->rx_freq[0] + (unsigned int) device->openair0_cfg->sample_rate/4);  
  bladerf_set_loopback (brf->dev,BLADERF_LB_RF_LNA1);

  offIold=2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offIold);
  for (i=0;i<10;i++) {
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
  }
  for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
    switch (i&3) {
    case 0:
      meanIold+=calib_buff[j++];
      break;
    case 1:
      meanQold+=calib_buff[j++];
      break;
    case 2:
      meanIold-=calib_buff[j++];
      break;
    case 3:
      meanQold-=calib_buff[j++];
      break;
    }
  }
  //  meanIold/=RXDCLENGTH;
  //  meanQold/=RXDCLENGTH;
  printf("[BRF] TX DC (offI): %d => (%d,%d)\n",offIold,meanIold,meanQold);

  offI=-2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offI);
  for (i=0;i<10;i++) {
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
  }
  for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
    switch (i&3) {
    case 0:
      meanI+=calib_buff[j++];
      break;
    case 1:
      meanQ+=calib_buff[j++];
      break;
    case 2:
      meanI-=calib_buff[j++];
      break;
    case 3:
      meanQ-=calib_buff[j++];
      break;
    }
  }
  //  meanI/=RXDCLENGTH;
  //  meanQ/=RXDCLENGTH;
  printf("[BRF] TX DC (offI): %d => (%d,%d)\n",offI,meanI,meanQ);
  cnt = 0;
  while (cnt++ < 12) {

    offInew=(offIold+offI)>>1;
    if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
      printf("[BRF] TX DC (offI): ([%d,%d]) => %d : %d\n",offIold,offI,offInew,meanI*meanI+meanQ*meanQ);
      meanIold = meanI;
      meanQold = meanQ;
      offIold = offI;
    }
    offI = offInew;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offI);

    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      switch (i&3) {
      case 0:
	meanI+=calib_buff[j++];
	break;
      case 1:
	meanQ+=calib_buff[j++];
	break;
      case 2:
	meanI-=calib_buff[j++];
	break;
      case 3:
	meanQ-=calib_buff[j++];
	break;
      }
    }
    //    meanI/=RXDCLENGTH;
    //   meanQ/=RXDCLENGTH;
    //    printf("[BRF] TX DC (offI): %d => (%d,%d)\n",offI,meanI,meanQ);
  }

  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_I,offIold);

  offQold=2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQold);
  for (i=0;i<10;i++) {
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
  }
  // project on fs/4
  for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
    switch (i&3) {
    case 0:
      meanIold+=calib_buff[j++];
      break;
    case 1:
      meanQold+=calib_buff[j++];
      break;
    case 2:
      meanIold-=calib_buff[j++];
      break;
    case 3:
      meanQold-=calib_buff[j++];
      break;
    }
  }
  //  meanIold/=RXDCLENGTH;
  //  meanQold/=RXDCLENGTH;
  printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQold,meanIold,meanQold);

  offQ=-2048;
  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQ);
  for (i=0;i<10;i++) {
    trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
  }
  for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
    switch (i&3) {
    case 0:
      meanI+=calib_buff[j++];
      break;
    case 1:
      meanQ+=calib_buff[j++];
      break;
    case 2:
      meanI-=calib_buff[j++];
      break;
    case 3:
      meanQ-=calib_buff[j++];
      break;
    }
  }
  //  meanI/=RXDCLENGTH;
  //  meanQ/=RXDCLENGTH;
  printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);

  cnt=0;
  while (cnt++ < 12) {

    offQnew=(offQold+offQ)>>1;
    if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
      printf("[BRF] TX DC (offQ): ([%d,%d]) => %d : %d\n",offQold,offQ,offQnew,meanI*meanI+meanQ*meanQ);

      meanIold = meanI;
      meanQold = meanQ;
      offQold = offQ;
    }
    offQ = offQnew;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQ);

    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      switch (i&3) {
      case 0:
	meanI+=calib_buff[j++];
	break;
      case 1:
	meanQ+=calib_buff[j++];
	break;
      case 2:
	meanI-=calib_buff[j++];
	break;
      case 3:
	meanQ-=calib_buff[j++];
	break;
      }
    }
    //    meanI/=RXDCLENGTH;
    //   meanQ/=RXDCLENGTH;
    //    printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
  }

  printf("[BRF] TX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold);

  bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_LMS_DCOFF_Q,offQold);

  // TX IQ imbalance
  for (loop=0;loop<2;loop++) {
    offphaseold=4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphaseold);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on fs/8 (Image of TX signal in +ve frequencies)
    for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
      meanIold+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
      meanQold+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
      j+=2;
    }
    
    meanIold/=RXDCLENGTH;
    meanQold/=RXDCLENGTH;
    printf("[BRF] TX IQ (offphase): %d => (%d,%d)\n",offphaseold,meanIold,meanQold);
    
    offphase=-4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphase);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on fs/8 (Image of TX signal in +ve frequencies)
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      meanI+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
      meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
      j+=2;
    }
    
    meanI/=RXDCLENGTH;
    meanQ/=RXDCLENGTH;
    printf("[BRF] TX IQ (offphase): %d => (%d,%d)\n",offphase,meanI,meanQ);
    
    cnt=0;
    while (cnt++ < 13) {
      
      offphasenew=(offphaseold+offphase)>>1;
      printf("[BRF] TX IQ (offphase): ([%d,%d]) => %d : %d\n",offphaseold,offphase,offphasenew,meanI*meanI+meanQ*meanQ);
      if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {

	
	meanIold = meanI;
	meanQold = meanQ;
	offphaseold = offphase;
      }
      offphase = offphasenew;
      bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphase);
      
      for (i=0;i<10;i++) {
	trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
	trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
      }
      // project on fs/8 (Image of TX signal in +ve frequencies)
      for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
	meanI+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
	meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
	j+=2;
      }
      meanI/=RXDCLENGTH;
      meanQ/=RXDCLENGTH;
      
      //    printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
    }
    
    printf("[BRF] TX IQ offphase: %d => (%d,%d)\n",offphaseold,meanIold,meanQold);
    
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_PHASE,offphaseold);
    
    offgainold=4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgainold);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on fs/8 (Image of TX signal in +ve frequencies)
    for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
      meanIold+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
      meanQold+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
      j+=2;
    }
    
    meanIold/=RXDCLENGTH;
    meanQold/=RXDCLENGTH;
    printf("[BRF] TX IQ (offgain): %d => (%d,%d)\n",offgainold,meanIold,meanQold);
    
    offgain=-4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgain);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on fs/8 (Image of TX signal in +ve frequencies)
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      meanI+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
      meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
      j+=2;
    }
    
    meanI/=RXDCLENGTH;
    meanQ/=RXDCLENGTH;
    printf("[BRF] TX IQ (offgain): %d => (%d,%d)\n",offgain,meanI,meanQ);
    
    cnt=0;
    while (cnt++ < 13) {
      
      offgainnew=(offgainold+offgain)>>1;
      if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
	printf("[BRF] TX IQ (offgain): ([%d,%d]) => %d : %d\n",offgainold,offgain,offgainnew,meanI*meanI+meanQ*meanQ);
	
	meanIold = meanI;
	meanQold = meanQ;
	offgainold = offgain;
      }
      offgain = offgainnew;
      bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgain);
      
      for (i=0;i<10;i++) {
	trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
	trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
      }
      // project on fs/8 (Image of TX signal in +ve frequencies)
      for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
	meanI+= (calib_buff[j]*cos_fsover8[i&7] - calib_buff[j+1]*cos_fsover8[(i+2)&7])>>11;
	meanQ+= (calib_buff[j]*cos_fsover8[(i+2)&7] + calib_buff[j+1]*cos_fsover8[i&7])>>11;
	j+=2;
      }
      meanI/=RXDCLENGTH;
      meanQ/=RXDCLENGTH;
      
      //    printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
    }
    
    printf("[BRF] TX IQ offgain: %d => (%d,%d)\n",offgainold,meanIold,meanQold);
    
    bladerf_set_correction(brf->dev,BLADERF_MODULE_TX,BLADERF_CORR_FPGA_GAIN,offgainold);
  }

  // RX IQ imbalance
  for (loop=0;loop<2;loop++) {
    offphaseold=4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphaseold);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on -3fs/8 (Image of TX signal in -ve frequencies)
    for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
      meanIold+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
      meanQold+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
      j+=2;
    }
    
    meanIold/=RXDCLENGTH;
    meanQold/=RXDCLENGTH;
    printf("[BRF] RX IQ (offphase): %d => (%d,%d)\n",offphaseold,meanIold,meanQold);
    
    offphase=-4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphase);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on -3fs/8 (Image of TX signal in -ve frequencies)
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      meanI+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
      meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
      j+=2;
    }
    
    meanI/=RXDCLENGTH;
    meanQ/=RXDCLENGTH;
    printf("[BRF] RX IQ (offphase): %d => (%d,%d)\n",offphase,meanI,meanQ);
    
    cnt=0;
    while (cnt++ < 13) {
      
      offphasenew=(offphaseold+offphase)>>1;
      printf("[BRF] RX IQ (offphase): ([%d,%d]) => %d : %d\n",offphaseold,offphase,offphasenew,meanI*meanI+meanQ*meanQ);
      if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {

	
	meanIold = meanI;
	meanQold = meanQ;
	offphaseold = offphase;
      }
      offphase = offphasenew;
      bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphase);
      
      for (i=0;i<10;i++) {
	trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
	trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
      }
      // project on -3fs/8 (Image of TX signal in -ve frequencies)
      for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
	meanI+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
	meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
	j+=2;
      }
      meanI/=RXDCLENGTH;
      meanQ/=RXDCLENGTH;
      
      //    printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
    }
    
    printf("[BRF] RX IQ offphase: %d => (%d,%d)\n",offphaseold,meanIold,meanQold);
    
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_PHASE,offphaseold);
    
    offgainold=4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgainold);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on -3fs/8 (Image of TX signal in +ve frequencies)
    for (meanIold=meanQold=i=j=0;i<RXDCLENGTH;i++) {
      meanIold+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
      meanQold+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
      j+=2;
    }
    
    meanIold/=RXDCLENGTH;
    meanQold/=RXDCLENGTH;
    printf("[BRF] RX IQ (offgain): %d => (%d,%d)\n",offgainold,meanIold,meanQold);
    
    offgain=-4096;
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgain);
    for (i=0;i<10;i++) {
      trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
    }
    // project on 3fs/8 (Image of TX signal in -ve frequencies)
    for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
      meanI+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
      meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
      j+=2;
    }
    
    meanI/=RXDCLENGTH;
    meanQ/=RXDCLENGTH;
    printf("[BRF] RX IQ (offgain): %d => (%d,%d)\n",offgain,meanI,meanQ);
    
    cnt=0;
    while (cnt++ < 13) {
      
      offgainnew=(offgainold+offgain)>>1;
      if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
	printf("[BRF] RX IQ (offgain): ([%d,%d]) => %d : %d\n",offgainold,offgain,offgainnew,meanI*meanI+meanQ*meanQ);
	
	meanIold = meanI;
	meanQold = meanQ;
	offgainold = offgain;
      }
      offgain = offgainnew;
      bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgain);
      
      for (i=0;i<10;i++) {
	trx_brf_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
	trx_brf_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0);
      }
      // project on -3fs/8 (Image of TX signal in -ve frequencies)
      for (meanI=meanQ=i=j=0;i<RXDCLENGTH;i++) {
	meanI+= (calib_buff[j]*cos_3fsover8[i&7] - calib_buff[j+1]*cos_3fsover8[(i+2)&7])>>11;
	meanQ+= (calib_buff[j]*cos_3fsover8[(i+2)&7] + calib_buff[j+1]*cos_3fsover8[i&7])>>11;
	j+=2;
      }
      meanI/=RXDCLENGTH;
      meanQ/=RXDCLENGTH;
      
      //    printf("[BRF] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
    }
    
    printf("[BRF] RX IQ offgain: %d => (%d,%d)\n",offgainold,meanIold,meanQold);
    
    bladerf_set_correction(brf->dev,BLADERF_MODULE_RX,BLADERF_CORR_FPGA_GAIN,offgainold);
  }

  bladerf_set_frequency(brf->dev,BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->tx_freq[0]);  
  bladerf_set_loopback(brf->dev,BLADERF_LB_NONE);
  bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, (unsigned int) device->openair0_cfg->rx_gain[0]-device->openair0_cfg[0].rx_gain_offset[0]);
  bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, (unsigned int) device->openair0_cfg->tx_gain[0]);
  //  write_output("blade_rf_test.m","rxs",calib_buff,RXDCLENGTH,1,1);
}

/*! \brief Initialize Openair BLADERF target. It returns 0 if OK 
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 */
int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
  int status;
  int card=0;
  
  brf_state_t *brf = (brf_state_t*)malloc(sizeof(brf_state_t));
  memset(brf, 0, sizeof(brf_state_t));
  /* device specific */
  openair0_cfg->txlaunch_wait = 1;//manage when TX processing is triggered
  openair0_cfg->txlaunch_wait_slotcount = 1; //manage when TX processing is triggered
  openair0_cfg->iq_txshift = 0;// shift
  openair0_cfg->iq_rxrescale = 15;//rescale iqs
  
  // init required params
  switch ((int)openair0_cfg->sample_rate) {
  case 30720000:
    openair0_cfg->samples_per_packet    = 2048;
    openair0_cfg->tx_sample_advance     = 0;
    openair0_cfg->tx_scheduling_advance = 8*openair0_cfg->samples_per_packet;
    break;
  case 15360000:
    openair0_cfg->samples_per_packet    = 2048;
    openair0_cfg->tx_sample_advance     = 0;
    openair0_cfg->tx_scheduling_advance = 4*openair0_cfg->samples_per_packet;
    break;
  case 7680000:
    openair0_cfg->samples_per_packet    = 1024;
    openair0_cfg->tx_sample_advance     = 0;
    openair0_cfg->tx_scheduling_advance = 4*openair0_cfg->samples_per_packet;
    break;
  case 1920000:
    openair0_cfg->samples_per_packet    = 256;
    openair0_cfg->tx_sample_advance     = 50;
    openair0_cfg->tx_scheduling_advance = 8*openair0_cfg->samples_per_packet;
    break;
  default:
    printf("Error: unknown sampling rate %f\n",openair0_cfg->sample_rate);
    exit(-1);
    break;
  }
  openair0_cfg->iq_txshift= 0;
  openair0_cfg->iq_rxrescale = 15; /*not sure*/
  openair0_cfg->rx_gain_calib_table = calib_table_fx4;

  //  The number of buffers to use in the underlying data stream
  brf->num_buffers   = 128;
  // the size of the underlying stream buffers, in samples
  brf->buffer_size   = (unsigned int) openair0_cfg->samples_per_packet;//*sizeof(int32_t); // buffer size = 4096 for sample_len of 1024
  brf->num_transfers = 16;
  brf->rx_timeout_ms = 0;  
  brf->tx_timeout_ms = 0;
  brf->sample_rate=(unsigned int)openair0_cfg->sample_rate;

  memset(&brf->meta_rx, 0, sizeof(brf->meta_rx));
  memset(&brf->meta_tx, 0, sizeof(brf->meta_tx));

  printf("\n[BRF] sampling_rate %d, num_buffers %d,  buffer_size %d, num transfer %d, timeout_ms (rx %d, tx %d)\n", 
	 brf->sample_rate, brf->num_buffers, brf->buffer_size,brf->num_transfers, brf->rx_timeout_ms, brf->tx_timeout_ms);
  
  if ((status=bladerf_open(&brf->dev, "")) != 0 ) {
    fprintf(stderr,"Failed to open brf device: %s\n",bladerf_strerror(status));
    brf_error(status);
  }
  printf("[BRF] init dev %p\n", brf->dev);
  switch(bladerf_device_speed(brf->dev)){
  case BLADERF_DEVICE_SPEED_SUPER:
    printf("[BRF] Device operates at max speed\n");
    break;
  default:
    printf("[BRF] Device does not operates at max speed, change the USB port\n");
    brf_error(BLADERF_ERR_UNSUPPORTED);
  }
  // RX  
  // Example of CLI output: RX Frequency: 2539999999Hz
  
  if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_freq[0])) != 0){
    fprintf(stderr,"Failed to set RX frequency: %s\n",bladerf_strerror(status));
    brf_error(status);
  } else 
    printf("[BRF] set RX frequency to %u\n",(unsigned int)openair0_cfg->rx_freq[0]);
  


  unsigned int actual_value=0;
  if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->sample_rate, &actual_value)) != 0){
    fprintf(stderr,"Failed to set RX sample rate: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else  
    printf("[BRF] set RX sample rate to %u, %u\n", (unsigned int) openair0_cfg->sample_rate, actual_value);
 

  if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_RX, (unsigned int) openair0_cfg->rx_bw*2, &actual_value)) != 0){
    fprintf(stderr,"Failed to set RX bandwidth: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set RX bandwidth to %u, %u\n",(unsigned int)openair0_cfg->rx_bw*2, actual_value);
 
  set_rx_gain_offset(&openair0_cfg[0],0);
  if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_RX, (int) openair0_cfg->rx_gain[0]-openair0_cfg[0].rx_gain_offset[0])) != 0) {
    fprintf(stderr,"Failed to set RX gain: %s\n",bladerf_strerror(status));
    brf_error(status);
  } else 
    printf("[BRF] set RX gain to %d (%d)\n",(int)(openair0_cfg->rx_gain[0]-openair0_cfg[0].rx_gain_offset[0]),(int)openair0_cfg[0].rx_gain_offset[0]);

  // TX
  
  if ((status=bladerf_set_frequency(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->tx_freq[0])) != 0){
    fprintf(stderr,"Failed to set TX frequency: %s\n",bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set TX Frequency to %u\n", (unsigned int) openair0_cfg->tx_freq[0]);

  if ((status=bladerf_set_sample_rate(brf->dev, BLADERF_MODULE_TX, (unsigned int) openair0_cfg->sample_rate, NULL)) != 0){
    fprintf(stderr,"Failed to set TX sample rate: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set TX sampling rate to %u \n", (unsigned int) openair0_cfg->sample_rate);

  if ((status=bladerf_set_bandwidth(brf->dev, BLADERF_MODULE_TX,(unsigned int)openair0_cfg->tx_bw*2, NULL)) != 0){
    fprintf(stderr, "Failed to set TX bandwidth: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set TX bandwidth to %u \n", (unsigned int) openair0_cfg->tx_bw*2);

  if ((status=bladerf_set_gain(brf->dev, BLADERF_MODULE_TX, (int) openair0_cfg->tx_gain[0])) != 0) {
    fprintf(stderr,"Failed to set TX gain: %s\n",bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] set the TX gain to %d\n", (int)openair0_cfg->tx_gain[0]);
  

 /* Configure the device's TX module for use with the sync interface.
   * SC16 Q11 samples *with* metadata are used. */
  if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_TX,BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->tx_timeout_ms)) != 0 ) {
    fprintf(stderr,"Failed to configure TX sync interface: %s\n", bladerf_strerror(status));
     brf_error(status);
  }else 
    printf("[BRF] configured TX  sync interface \n");

/* Configure the device's RX module for use with the sync interface.
   * SC16 Q11 samples *with* metadata are used. */
  if ((status=bladerf_sync_config(brf->dev, BLADERF_MODULE_RX, BLADERF_FORMAT_SC16_Q11_META,brf->num_buffers,brf->buffer_size,brf->num_transfers,brf->rx_timeout_ms)) != 0 ) {
    fprintf(stderr,"Failed to configure RX sync interface: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] configured Rx sync interface \n");


   /* We must always enable the TX module after calling bladerf_sync_config(), and 
    * before  attempting to TX samples via  bladerf_sync_tx(). */
  if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_TX, true)) != 0) {
    fprintf(stderr,"Failed to enable TX module: %s\n", bladerf_strerror(status));
    brf_error(status);
  } else 
    printf("[BRF] TX module enabled \n");
 
 /* We must always enable the RX module after calling bladerf_sync_config(), and 
    * before  attempting to RX samples via  bladerf_sync_rx(). */
  if ((status=bladerf_enable_module(brf->dev, BLADERF_MODULE_RX, true)) != 0) {
    fprintf(stderr,"Failed to enable RX module: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] RX module enabled \n");

  // calibrate 
    
 if ((status=bladerf_calibrate_dc(brf->dev, BLADERF_MODULE_TX)) != 0) {
    fprintf(stderr,"Failed to calibrate TX DC: %s\n", bladerf_strerror(status));
    brf_error(status);
  } else 
    printf("[BRF] TX module calibrated DC \n");
 
  if ((status=bladerf_calibrate_dc(brf->dev, BLADERF_MODULE_RX)) != 0) {
    fprintf(stderr,"Failed to calibrate RX DC: %s\n", bladerf_strerror(status));
    brf_error(status);
  }else 
    printf("[BRF] RX module calibrated DC \n");
  


  bladerf_log_set_verbosity(get_brf_log_level(openair0_cfg->log_level));
  
  printf("BLADERF: Initializing openair0_device\n");
  device->priv           = brf; 
  device->Mod_id         = num_devices++;
  device->type             = BLADERF_DEV; 
  device->trx_start_func = trx_brf_start;
  device->trx_end_func   = trx_brf_end;
  device->trx_read_func  = trx_brf_read;
  device->trx_write_func = trx_brf_write;
  device->trx_get_stats_func   = trx_brf_get_stats;
  device->trx_reset_stats_func = trx_brf_reset_stats;
  device->trx_stop_func        = trx_brf_stop;
  device->trx_set_freq_func    = trx_brf_set_freq;
  device->trx_set_gains_func   = trx_brf_set_gains;
  device->openair0_cfg = openair0_cfg;

  calibrate_rf(device);

  //  memcpy((void*)&device->openair0_cfg,(void*)&openair0_cfg[0],sizeof(openair0_config_t));

  return 0;
}

/*! \brief bladeRF error report 
 * \param status 
 */
int brf_error(int status) {
  
  //exit(-1);
  //return 1; // or status error code
}


/*! \brief Open BladeRF from serial port
 * \param serial name of serial port on which to open BladeRF device
 */
struct bladerf * open_bladerf_from_serial(const char *serial) {

  int status;
  struct bladerf *dev;
  struct bladerf_devinfo info;
  /* Initialize all fields to "don't care" wildcard values.
   *
   * Immediately passing this to bladerf_open_with_devinfo() would cause
   * libbladeRF to open any device on any available backend. */
  bladerf_init_devinfo(&info);
  /* Specify the desired device's serial number, while leaving all other
   * fields in the info structure wildcard values */
  strncpy(info.serial, serial, BLADERF_SERIAL_LENGTH - 1);
  info.serial[BLADERF_SERIAL_LENGTH - 1] = '\0';
  status = bladerf_open_with_devinfo(&dev, &info);
  
  if (status == BLADERF_ERR_NODEV) {
    printf("No devices available with serial=%s\n", serial);
    return NULL;
  } else if (status != 0) {
    fprintf(stderr, "Failed to open device with serial=%s (%s)\n", serial, bladerf_strerror(status));
    return NULL;
  } else {
    return dev;
  }
}

/*! \brief Get BladeRF log level
 * \param log_level log level
 */
int get_brf_log_level(int log_level){

  int level=BLADERF_LOG_LEVEL_INFO;
  return  BLADERF_LOG_LEVEL_DEBUG; // BLADERF_LOG_LEVEL_VERBOSE;// BLADERF_LOG_LEVEL_DEBUG; //
  switch(log_level) {
  case LOG_DEBUG:
    level=BLADERF_LOG_LEVEL_DEBUG;
    break;
  case LOG_INFO:
    level= BLADERF_LOG_LEVEL_INFO;
    break;
  case LOG_WARNING:
    level=BLADERF_LOG_LEVEL_WARNING;
    break;
  case LOG_ERR:
    level=BLADERF_LOG_LEVEL_ERROR;
    break;
  case LOG_CRIT:
    level=BLADERF_LOG_LEVEL_CRITICAL;
    break;
  case LOG_EMERG:
    level = BLADERF_LOG_LEVEL_SILENT;
    break;
  default:
    break;
  }
  return level;
}
/*@}*/
