/*******************************************************************************
    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@eurecom.fr

   Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

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


#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/ether.h>
#include <unistd.h>
#include <errno.h>

#include "common_lib.h"
#include "LMS_SDR.h"
#include "LMS7002M.h"
#include "Si5351C.h"
#include "LMS_StreamBoard.h"
#include "LMS7002M_RegistersMap.h"

#include <cmath>


///define for parameter enumeration if prefix might be needed
#define LMS7param(id) id

LMScomms* usbport;
LMScomms* comport;
LMS7002M* lms7;
Si5351C* Si;
LMS_StreamBoard *lmsStream;

#define RXDCLENGTH 4096
#define NUMBUFF 40
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};

extern "C"
{
int write_output(const char *fname,const char *vname,void *data,int length,int dec,char format);
}

int trx_lms_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int antenna_id, int flags) {
  
 LMS_TRxWrite((int16_t*)buff[0], nsamps,0, timestamp);

 return nsamps;
}


int trx_lms_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int antenna_id) {
    
  uint64_t timestamp;
  int16_t *dst_ptr = (int16_t*) buff[0];
  int ret;
  ret = LMS_TRxRead(dst_ptr, nsamps,0,&timestamp, 100);
  *ptimestamp=timestamp;

  return ret;   
}
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) {

  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;
  int8_t offI,offQ,offIold,offQold,offInew,offQnew,offphase,offphaseold,offphasenew,offgain,offgainold,offgainnew;
  int32_t meanI,meanQ,meanIold,meanQold;
  int cnt=0,loop;
  liblms7_status opStatus;
  int16_t dcoffi;
  int16_t dcoffq;
  int16_t dccorri;
  int16_t dccorrq;
    const int16_t firCoefs[] =
    {
        -2531,
        -517,
        2708,
        188,
        -3059,
        216,
        3569,
        -770,
        -4199,
        1541,
        4886,
        -2577,
        -5552,
        3909,
        6108,
        -5537,
        -6457,
        7440,
        6507,
        -9566,
        -6174,
        11845,
        5391,
        -14179,
        -4110,
        16457,
        2310,
        -18561,
        0,
        20369,
        -2780,
        -21752,
        5963,
        22610,
        -9456,
        -22859,
        13127,
        22444,
        -16854,
        -21319,
        20489,
        19492,
        -23883,
        -17002,
        26881,
        13902,
        -29372,
        -10313,
        31226,
        6345,
        -32380,
        -2141,
        32767,
        -2141,
        -32380,
        6345,
        31226,
        -10313,
        -29372,
        13902,
        26881,
        -17002,
        -23883,
        19492,
        20489,
        -21319,
        -16854,
        22444,
        13127,
        -22859,
        -9456,
        22610,
        5963,
        -21752,
        -2780,
        20369,
        0,
        -18561,
        2310,
        16457,
        -4110,
        -14179,
        5391,
        11845,
        -6174,
        -9566,
        6507,
        7440,
        -6457,
        -5537,
        6108,
        3909,
        -5552,
        -2577,
        4886,
        1541,
        -4199,
        -770,
        3569,
        216,
        -3059,
        188,
        2708,
        -517,
        -2531
    };

  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];

  lms7->BackupAllRegisters();
  uint8_t ch = (uint8_t)lms7->Get_SPI_Reg_bits(LMS7param(MAC));
  //Stage 1
  uint8_t sel_band1_trf = (uint8_t)lms7->Get_SPI_Reg_bits(LMS7param(SEL_BAND1_TRF));
  uint8_t sel_band2_trf = (uint8_t)lms7->Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF));
  {
    uint16_t requiredRegs[] = { 0x0400, 0x040A, 0x010D, 0x040C };
    uint16_t requiredMask[] = { 0x6000, 0x3007, 0x0040, 0x00FF }; //CAPSEL, AGC_MODE, AGC_AVG, EN_DCOFF, Bypasses
    uint16_t requiredValue[] = { 0x0000, 0x1007, 0x0040, 0x00BD };
    
    lms7->Modify_SPI_Reg_mask(requiredRegs, requiredMask, requiredValue, 0, 3);
  }

  //  opStatus = lms7->SetFrequencySX(LMS7002M::Rx, device->openair0_cfg[0].tx_freq[0]/1e6,30.72);
  // put TX on fs/4
  opStatus = lms7->CalibrateRxSetup(device->openair0_cfg[0].sample_rate/1e6);
  if (opStatus != LIBLMS7_SUCCESS) {
    printf("Cannot calibrate for %f MHz\n",device->openair0_cfg[0].sample_rate/1e6);
    exit(-1);
  }
    // fill TX buffer with fs/8 complex sinusoid
  offIold=offQold=64;
  lms7->SetRxDCOFF(offIold,offQold);
  LMS_RxStart();  
  for (i=0;i<NUMBUFF;i++)
    trx_lms_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("[LMS] RX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold);

  offI=offQ=-64;
  lms7->SetRxDCOFF(offI,offQ);

  for (i=0;i<NUMBUFF;i++)
    trx_lms_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("[LMS] RX DC: (%d,%d) => (%d,%d)\n",offI,offQ,meanI,meanQ);

  while (cnt++ < 7) {

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

    if (meanI*meanI < meanIold*meanIold) {
      meanIold = meanI;
      offIold = offI;
      printf("[LMS] *** RX DC: offI %d => %d\n",offIold,meanI);
    }
    if (meanQ*meanQ < meanQold*meanQold) {
      meanQold = meanQ;
      offQold = offQ;
      printf("[LMS] *** RX DC: offQ %d => %d\n",offQold,meanQ);
    }
    offI = offInew;
    offQ = offQnew;

    lms7->SetRxDCOFF(offI,offQ);

    for (i=0;i<NUMBUFF;i++)
      trx_lms_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("[LMS] RX DC: (%d,%d) => (%d,%d)\n",offI,offQ,meanI,meanQ);
  }

  if (meanI*meanI < meanIold*meanIold) {
    meanIold = meanI;
    offIold = offI;
    printf("[LMS] *** RX DC: offI %d => %d\n",offIold,meanI);
  }
  if (meanQ*meanQ < meanQold*meanQold) {
    meanQold = meanQ;
    offQold = offQ;
    printf("[LMS] *** RX DC: offQ %d => %d\n",offQold,meanQ);
  }
  
  printf("[LMS] RX DC: (%d,%d) => (%d,%d)\n",offIold,offQold,meanIold,meanQold);
  
  lms7->SetRxDCOFF(offIold,offQold);

  dcoffi = offIold;
  dcoffq = offQold;

  lms7->Modify_SPI_Reg_bits(LMS7param(MAC), ch);
  lms7->Modify_SPI_Reg_bits(LMS7param(AGC_MODE_RXTSP), 1);
  lms7->Modify_SPI_Reg_bits(LMS7param(CAPSEL), 0);

  // TX LO leakage
  offQold=offIold=127;
  lms7->SPI_write(0x0204,(((int16_t)offIold)<<7)|offQold);

  {
    uint16_t requiredRegs[] = { 0x0400, 0x040A, 0x010D, 0x040C };
    uint16_t requiredMask[] = { 0x6000, 0x3007, 0x0040, 0x00FF }; //CAPSEL, AGC_MODE, AGC_AVG, EN_DCOFF, Bypasses
    uint16_t requiredValue[] = { 0x0000, 0x1007, 0x0040, 0x00BD };
    
    lms7->Modify_SPI_Reg_mask(requiredRegs, requiredMask, requiredValue, 0, 3);
  }
  sel_band1_trf = (uint8_t)lms7->Get_SPI_Reg_bits(LMS7param(SEL_BAND1_TRF));
  sel_band2_trf = (uint8_t)lms7->Get_SPI_Reg_bits(LMS7param(SEL_BAND2_TRF));
  //B
  lms7->Modify_SPI_Reg_bits(0x0100, 0, 0, 1); //EN_G_TRF 1
  if (sel_band1_trf == 1)
    {
      lms7->Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_1_RFE), 0); //PD_RLOOPB_1_RFE 0
      lms7->Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB1_RFE), 0); //EN_INSHSW_LB1 0
    }
  if (sel_band2_trf == 1)
    {
      lms7->Modify_SPI_Reg_bits(LMS7param(PD_RLOOPB_2_RFE), 0); //PD_RLOOPB_2_RFE 0
      lms7->Modify_SPI_Reg_bits(LMS7param(EN_INSHSW_LB2_RFE), 0); // EN_INSHSW_LB2 0
    }
  //  FixRXSaturation();
  
  lms7->Modify_SPI_Reg_bits(LMS7param(GFIR3_BYP_RXTSP), 0); //GFIR3_BYP 0
  lms7->Modify_SPI_Reg_bits(LMS7param(HBD_OVR_RXTSP), 2);
  lms7->Modify_SPI_Reg_bits(LMS7param(GFIR3_L_RXTSP), 7);
  lms7->Modify_SPI_Reg_bits(LMS7param(GFIR3_N_RXTSP), 7);
  
  lms7->SetGFIRCoefficients(LMS7002M::Rx, 2, firCoefs, sizeof(firCoefs) / sizeof(int16_t));
    
  for (i=0;i<NUMBUFF;i++) {
    trx_lms_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_lms_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 0);
  }

  write_output("calibrx.m","rxs",calib_buffp,RXDCLENGTH,1,1);
  exit(-1);
  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("[LMS] TX DC (offI): %d => (%d,%d)\n",offIold,meanIold,meanQold);

  offI=-128;
  lms7->SPI_write(0x0204,(((int16_t)offI)<<7)|offQold);

  for (i=0;i<NUMBUFF;i++) {
    trx_lms_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_lms_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 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("[LMS] TX DC (offI): %d => (%d,%d)\n",offI,meanI,meanQ);
  cnt = 0;
  while (cnt++ < 8) {

    offInew=(offIold+offI)>>1;
    if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
      printf("[LMS] TX DC (offI): ([%d,%d]) => %d : %d\n",offIold,offI,offInew,meanI*meanI+meanQ*meanQ);
      meanIold = meanI;
      meanQold = meanQ;
      offIold = offI;
    }
    offI = offInew;
    lms7->SPI_write(0x0204,(((int16_t)offI)<<7)|offQold);

    for (i=0;i<NUMBUFF;i++) {
      trx_lms_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_lms_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 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("[LMS] TX DC (offI): %d => (%d,%d)\n",offI,meanI,meanQ);
  }


  if (meanI*meanI+meanQ*meanQ < meanIold*meanIold +meanQold*meanQold) {
    printf("[LMS] TX DC (offI): ([%d,%d]) => %d : %d\n",offIold,offI,offInew,meanI*meanI+meanQ*meanQ);
    meanIold = meanI;
    meanQold = meanQ;
    offIold = offI;
  }
  offQ=-128;
  lms7->SPI_write(0x0204,(((int16_t)offIold)<<7)|offQ);

  for (i=0;i<NUMBUFF;i++) {
    trx_lms_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
    trx_lms_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 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("[LMS] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);

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

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

      meanIold = meanI;
      meanQold = meanQ;
      offQold = offQ;
    }
    offQ = offQnew;
    lms7->SPI_write(0x0204,(((int16_t)offIold)<<7)|offQ);


    for (i=0;i<NUMBUFF;i++) {
      trx_lms_read(device, &ptimestamp, (void **)&calib_buffp, RXDCLENGTH, 0);
      trx_lms_write(device,ptimestamp+5*RXDCLENGTH, (void **)&calib_tx_buffp, RXDCLENGTH, 0, 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("[LMS] TX DC (offQ): %d => (%d,%d)\n",offQ,meanI,meanQ);
  }

  LMS_RxStop();

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

  dccorri = offIold;
  dccorrq = offQold;
  
  
  lms7->RestoreAllRegisters();
  lms7->Modify_SPI_Reg_bits(LMS7param(MAC), ch);

  lms7->Modify_SPI_Reg_bits(LMS7param(DCOFFI_RFE), dcoffi);
  lms7->Modify_SPI_Reg_bits(LMS7param(DCOFFQ_RFE), dcoffq);
  lms7->Modify_SPI_Reg_bits(LMS7param(DCCORRI_TXTSP), dccorri);
  lms7->Modify_SPI_Reg_bits(LMS7param(DCCORRQ_TXTSP), dccorrq);
  //  lms7->Modify_SPI_Reg_bits(LMS7param(GCORRI_TXTSP), gcorri);
  //  lms7->Modify_SPI_Reg_bits(LMS7param(GCORRQ_TXTSP), gcorrq);
  //  lms7->Modify_SPI_Reg_bits(LMS7param(IQCORR_TXTSP), iqcorr);
  
  //  lms7->Modify_SPI_Reg_bits(LMS7param(DC_BYP_TXTSP), 0); //DC_BYP
  lms7->Modify_SPI_Reg_bits(0x0208, 1, 0, 0); //GC_BYP PH_BYP
  
}
*/

int trx_lms_set_gains(openair0_device* device, openair0_config_t *openair0_cfg) {

  double gv = openair0_cfg[0].rx_gain[0] - openair0_cfg[0].rx_gain_offset[0];

  if (gv > 31) {
    printf("RX Gain 0 too high, reduce by %f dB\n",gv-31);
    gv = 31;
  }
  if (gv < 0) {
    printf("RX Gain 0 too low, increase by %f dB\n",-gv);
    gv = 0;
  }
  printf("[LMS] Setting 7002M G_PGA_RBB to %d\n", (int16_t)gv);
  lms7->Modify_SPI_Reg_bits(LMS7param(G_PGA_RBB),(int16_t)gv);

  return(0);
}

int trx_lms_start(openair0_device *device){
 

 LMS_Init(0, 128*1024);   
 usbport = LMS_GetUSBPort();
 //connect data stream port
  LMS_UpdateDeviceList(usbport);
  const char *name = LMS_GetDeviceName(usbport, 0);
  printf("Connecting to device: %s\n",name);



  if (LMS_DeviceOpen(usbport, 0)==0)
  {
    Si = new Si5351C();
    lms7 = new LMS7002M(usbport);
    liblms7_status opStatus;

    printf("Configuring Si5351C\n");
    Si->Initialize(usbport);
    Si->SetPLL(0, 25000000, 0);
    Si->SetPLL(1, 25000000, 0);
    Si->SetClock(0, 27000000, true, false);
    Si->SetClock(1, 27000000, true, false);
    for (int i = 2; i < 8; ++i)
      Si->SetClock(i, 27000000, false, false);
    Si5351C::Status status = Si->ConfigureClocks();
    if (status != Si5351C::SUCCESS)
      {
	printf("Failed to configure Si5351C");
	exit(-1);
      }
    status = Si->UploadConfiguration();
    if (status != Si5351C::SUCCESS)
      printf("Failed to upload Si5351C configuration");
    


    lms7->ResetChip();

    opStatus = lms7->LoadConfig(device->openair0_cfg[0].configFilename);
    
    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Failed to load configuration file %s\n",device->openair0_cfg[0].configFilename);
      exit(-1);
    }
    opStatus = lms7->UploadAll();

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Failed to upload configuration file\n");
      exit(-1);
    }

    // Set TX filter
    
    printf("Tuning TX filter\n");
    opStatus = lms7->TuneTxFilter(LMS7002M::TxFilter::TX_HIGHBAND,device->openair0_cfg[0].tx_bw/1e6);

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Warning: Could not tune TX filter to %f MHz\n",device->openair0_cfg[0].tx_bw/1e6);
    }
    
    printf("Tuning RX filter\n");
    
    opStatus = lms7->TuneRxFilter(LMS7002M::RxFilter::RX_LPF_LOWBAND,device->openair0_cfg[0].rx_bw/1e6);

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Warning: Could not tune RX filter to %f MHz\n",device->openair0_cfg[0].rx_bw/1e6);
    }

    /*    printf("Tuning TIA filter\n");
    opStatus = lms7->TuneRxFilter(LMS7002M::RxFilter::RX_TIA,7.0);

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Warning: Could not tune RX TIA filter\n");
      }*/

    opStatus = lms7->SetInterfaceFrequency(lms7->GetFrequencyCGEN_MHz(), 
					   lms7->Get_SPI_Reg_bits(HBI_OVR_TXTSP), 
					   lms7->Get_SPI_Reg_bits(HBD_OVR_RXTSP));
    if (opStatus != LIBLMS7_SUCCESS) {
      printf("SetInterfaceFrequency failed: %f,%d,%d\n",
	     lms7->GetFrequencyCGEN_MHz(), 
	     lms7->Get_SPI_Reg_bits(HBI_OVR_TXTSP), 
	     lms7->Get_SPI_Reg_bits(HBD_OVR_RXTSP));
    }
    else {
      printf("SetInterfaceFrequency as %f,%d,%d\n",
	     lms7->GetFrequencyCGEN_MHz(), 
	     lms7->Get_SPI_Reg_bits(HBI_OVR_TXTSP), 
	     lms7->Get_SPI_Reg_bits(HBD_OVR_RXTSP));
    } 
    lmsStream = new LMS_StreamBoard(usbport);    
    LMS_StreamBoard::Status opStreamStatus; 
    // this will configure that sampling rate at output of FPGA
    opStreamStatus = lmsStream->ConfigurePLL(usbport,
					     device->openair0_cfg[0].sample_rate,
					     device->openair0_cfg[0].sample_rate,90);
    if (opStatus != LIBLMS7_SUCCESS){
      printf("Sample rate programming failed\n");
      exit(-1);
    }

    opStatus = lms7->SetFrequencySX(LMS7002M::Tx, device->openair0_cfg[0].tx_freq[0]/1e6,30.72);

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Cannot set TX frequency %f MHz\n",device->openair0_cfg[0].tx_freq[0]/1e6);
      exit(-1);
    }
    else {
      printf("Set TX frequency %f MHz\n",device->openair0_cfg[0].tx_freq[0]/1e6);
    }
    opStatus = lms7->SetFrequencySX(LMS7002M::Rx, device->openair0_cfg[0].rx_freq[0]/1e6,30.72);

    if (opStatus != LIBLMS7_SUCCESS) {
      printf("Cannot set RX frequency %f MHz\n",device->openair0_cfg[0].rx_freq[0]/1e6);
      exit(-1);
    }
    else {
      printf("Set RX frequency %f MHz\n",device->openair0_cfg[0].rx_freq[0]/1e6);
    }

    trx_lms_set_gains(device, device->openair0_cfg);
    // Run calibration procedure
    //    calibrate_rf(device);
    //lms7->CalibrateTx(5.0);
    LMS_RxStart();
  }
  else
  {
   return(-1);
  }
  
 //connect control port 

 /* comport = LMS_GetCOMPort();
  LMS_UpdateDeviceList(comport);
  name = LMS_GetDeviceName(comport, 0);
  if (*name == 0)
      comport = usbport;  //attempt to use data port 
  else
  {
    printf("Connecting to device: %s\n",name);
    if (LMS_DeviceOpen(comport, 0)!=0)
        return (-1);
  }
   lms7 = new LMS7002M(comport);
   if( access( "./config.ini", F_OK ) != -1 ) //load config file
   lms7->LoadConfig("./config.ini");
   //calibration takes too long
   //lms7->CalibrateRx(5.0);  
   //lms7->CalibrateTx(5.0);
 */

  return 0;
}


int trx_lms_stop(int card) {
  /*
  LMS_DeviceClose(usbport);
  LMS_DeviceClose(comport);
  delete lms7;
  return LMS_Destroy();
  */
}

int trx_lms_set_freq(openair0_device* device, openair0_config_t *openair0_cfg,int exmimo_dump_config) {
  //Control port must be connected 
   
  lms7->SetFrequencySX(LMS7002M::Tx,openair0_cfg->tx_freq[0]/1e6,30.72);
  lms7->SetFrequencySX(LMS7002M::Rx,openair0_cfg->rx_freq[0]/1e6,30.72);
  printf ("[LMS] rx frequency:%f;\n",openair0_cfg->rx_freq[0]/1e6);
  set_rx_gain_offset(openair0_cfg,0);
  return(0);
    
}

// 31 = 19 dB => 105 dB total gain @ 2.6 GHz
rx_gain_calib_table_t calib_table_sodera[] = {
  {3500000000.0,70.0},
  {2660000000.0,80.0},
  {2300000000.0,80.0},
  {1880000000.0,74.0},  // on W PAD
  {816000000.0,76.0},   // on W PAD
  {-1,0}};







int trx_lms_get_stats(openair0_device* device) {

  return(0);

}

int trx_lms_reset_stats(openair0_device* device) {

  return(0);

}

int openair0_set_gains(openair0_device* device, 
		       openair0_config_t *openair0_cfg) {

  return(0);
}

int openair0_set_frequencies(openair0_device* device, openair0_config_t *openair0_cfg, int dummy) {

  return(0);
}



void trx_lms_end(openair0_device *device) {


}


int openair0_dev_init_lms(openair0_device *device, openair0_config_t *openair0_cfg){


  printf("LMSSDR: Initializing openair0_device for %s ...\n", ((device->func_type == BBU_FUNC) ? "BBU": "RRH"));

  switch ((int)openair0_cfg[0].sample_rate) {
  case 30720000:
    // from usrp_time_offset
    openair0_cfg[0].samples_per_packet    = 2048;
    openair0_cfg[0].tx_sample_advance     = 15;
    openair0_cfg[0].tx_bw                 = 30.72e6;
    openair0_cfg[0].rx_bw                 = 30.72e6;
    openair0_cfg[0].tx_scheduling_advance = 8*openair0_cfg[0].samples_per_packet;
    break;
  case 15360000:
    openair0_cfg[0].samples_per_packet    = 2048;
    openair0_cfg[0].tx_sample_advance     = 45;
    openair0_cfg[0].tx_bw                 = 28e6;
    openair0_cfg[0].rx_bw                 = 10e6;
    openair0_cfg[0].tx_scheduling_advance = 8*openair0_cfg[0].samples_per_packet;
    break;
  case 7680000:
    openair0_cfg[0].samples_per_packet    = 1024;
    openair0_cfg[0].tx_sample_advance     = 70;
    openair0_cfg[0].tx_bw                 = 28e6;
    openair0_cfg[0].rx_bw                 = 5.0e6;
    openair0_cfg[0].tx_scheduling_advance = 5*openair0_cfg[0].samples_per_packet;
    break;
  case 1920000:
    openair0_cfg[0].samples_per_packet    = 256;
    openair0_cfg[0].tx_sample_advance     = 50;
    openair0_cfg[0].tx_bw                 = 1.25e6;
    openair0_cfg[0].rx_bw                 = 1.25e6;
    openair0_cfg[0].tx_scheduling_advance = 8*openair0_cfg[0].samples_per_packet;
    break;
  default:
    printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
    exit(-1);
    break;
  }

  openair0_cfg[0].rx_gain_calib_table = calib_table_sodera;
  set_rx_gain_offset(openair0_cfg,0);

  device->Mod_id           = 1;
  device->trx_start_func   = trx_lms_start;
  device->trx_write_func   = trx_lms_write;
  device->trx_read_func    = trx_lms_read;  
  device->trx_get_stats_func   = trx_lms_get_stats;
  device->trx_reset_stats_func = trx_lms_reset_stats;
  device->trx_end_func = trx_lms_end;
  device->trx_stop_func = trx_lms_stop;
  device->trx_set_freq_func = trx_lms_set_freq;
  device->trx_set_gains_func = trx_lms_set_gains;
  
  device->openair0_cfg = openair0_cfg;

  return 0;
}