usrp_lib.cpp 26.2 KB
Newer Older
1 2
/** usrp_lib.cpp
 *
3
 * \author: HongliangXU : hong-liang-xu@agilent.com
4 5 6 7 8 9 10 11
 */

#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/usrp/multi_usrp.hpp>
12
#include <uhd/version.hpp>
13 14 15 16 17 18
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
19
#include <time.h>
20
#include "UTIL/LOG/log_extern.h"
21
#include "common_lib.h"
22 23 24 25 26 27 28
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
 
#ifdef __AVX2__
#  include <immintrin.h>
#endif
29

30 31 32 33
#ifdef __arm__
#  include <arm_neon.h>
#endif

34 35 36 37 38
/** @addtogroup _USRP_PHY_RF_INTERFACE_
 * @{
 */

/*! \brief USRP Configuration */ 
39 40 41 42 43 44
typedef struct
{

  // --------------------------------
  // variables for USRP configuration
  // --------------------------------
45
  //! USRP device pointer
46 47
  uhd::usrp::multi_usrp::sptr usrp;
  //uhd::usrp::multi_usrp::sptr rx_usrp;
48
  
49
  //create a send streamer and a receive streamer
50
  //! USRP TX Stream
51
  uhd::tx_streamer::sptr tx_stream;
52
  //! USRP RX Stream
53 54
  uhd::rx_streamer::sptr rx_stream;

55
  //! USRP TX Metadata
56
  uhd::tx_metadata_t tx_md;
57
  //! USRP RX Metadata
58 59
  uhd::rx_metadata_t rx_md;

60
  //! USRP Timestamp Information
61
  uhd::time_spec_t tm_spec;
62

63
  //setup variables and allocate buffer
64
  //! USRP Metadata
65 66
  uhd::async_metadata_t async_md;

67
  //! Sampling rate
68
  double sample_rate;
69 70

  //! time offset between transmiter timestamp and receiver timestamp;
71
  double tdiff;
72 73

  //! TX forward samples. We use usrp_time_offset to get this value
74 75 76 77 78 79
  int tx_forward_nsamps; //166 for 20Mhz


  // --------------------------------
  // Debug and output control
  // --------------------------------
80
  //! Number of underflows
81
  int num_underflows;
82
  //! Number of overflows
83
  int num_overflows;
84 85
  
  //! Number of sequential errors
86
  int num_seq_errors;
87
  //! tx count
88
  int64_t tx_count;
89
  //! rx count
90
  int64_t rx_count;
91
  //! timestamp of RX packet
92 93 94 95
  openair0_timestamp rx_timestamp;

} usrp_state_t;

96 97 98
/*! \brief Called to start the USRP transceiver. Return 0 if OK, < 0 if error
    @param device pointer to the device structure specific to the RF hardware target
*/
99 100 101 102 103 104
static int trx_usrp_start(openair0_device *device)
{
  usrp_state_t *s = (usrp_state_t*)device->priv;

  // init recv and send streaming
  uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
105
  cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
106 107 108
  cmd.stream_now = false; // start at constant delay
  s->rx_stream->issue_stream_cmd(cmd);

109
  s->tx_md.time_spec = cmd.time_spec + uhd::time_spec_t(1-(double)s->tx_forward_nsamps/s->sample_rate);
110 111 112 113 114 115 116 117
  s->tx_md.has_time_spec = true;
  s->tx_md.start_of_burst = true;
  s->tx_md.end_of_burst = false;


  s->rx_count = 0;
  s->tx_count = 0;
  s->rx_timestamp = 0;
118 119

  return 0;
120
}
121 122 123
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources 
 * \param device the hardware to use
 */
124 125 126 127 128 129
static void trx_usrp_end(openair0_device *device)
{
  usrp_state_t *s = (usrp_state_t*)device->priv;

  s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);

navid's avatar
navid committed
130 131 132 133 134
  //send a mini EOB packet
  s->tx_md.end_of_burst = true;
  s->tx_stream->send("", 0, s->tx_md);
  s->tx_md.end_of_burst = false;
  
135
}
136

137 138 139 140 141 142 143 144
/*! \brief Called to send samples to the USRP 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 antenna_id index of the antenna if the device has multiple anteannas
      @param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/ 
145
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
146
{
147 148 149 150 151 152 153
   static long long int loop=0;
   static long time_min=0, time_max=0, time_avg=0;
   struct timespec tp_start, tp_end;
   long time_diff;
   clock_gettime(CLOCK_MONOTONIC_RAW, &tp_start);

  int ret=0, ret_i=0;
154
  usrp_state_t *s = (usrp_state_t*)device->priv;
knopp's avatar
knopp committed
155

156
  s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
knopp's avatar
knopp committed
157

158
  
159 160 161 162
  if(flags)
    s->tx_md.has_time_spec = true;
  else
    s->tx_md.has_time_spec = false;
163
  
knopp's avatar
knopp committed
164 165 166
  if (cc>1) {
    std::vector<void *> buff_ptrs;
    for (int i=0;i<cc;i++) buff_ptrs.push_back(buff[i]);
167
    ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md,1e-3);
knopp's avatar
knopp committed
168 169
  }
  else
170 171
    ret = (int)s->tx_stream->send(buff[0], nsamps, s->tx_md,1e-3);

172
  s->tx_md.start_of_burst = false;
173

174 175 176
  if (ret != nsamps) {
    printf("[xmit] tx samples %d != %d\n",ret,nsamps);
  }
177 178 179 180 181 182 183 184 185 186 187

  clock_gettime(CLOCK_MONOTONIC_RAW, &tp_end);
  time_diff = (tp_end.tv_sec - tp_start.tv_sec) *1E09  + (tp_end.tv_nsec - tp_start.tv_nsec);
  if  (time_min==0 ||loop==1 || time_min > time_diff)
    time_min=time_diff;
  if  (time_max==0 ||loop==1 || time_max < time_diff)
    time_max=time_diff;
  if (time_avg ==0 ||loop==1)
    time_avg= time_diff;
  else
    time_avg=(time_diff+time_avg) /2.0;
188

189
  /*   //prints statics of uhd every 10 seconds
190 191
   if ( loop % (10 * ((int)device->openair0_cfg[0].sample_rate /(int)nsamps )) ==0)
     LOG_I(HW,"usrp_write: min(ns)=%d, max(ns)=%d, avg(ns)=%d\n", (int)time_min, (int)time_max,(int)time_avg);
192
  */
193
   loop++;
194
  return ret;
195 196
}

197 198 199 200 201 202 203 204 205 206 207
/*! \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 antenna_id Index of antenna for which to receive samples
 * \returns the number of sample read
*/
208
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc)
209
{
210 211 212 213 214
   static long long int loop=0;
   static long time_min=0, time_max=0, time_avg=0;
   struct timespec tp_start, tp_end;
   long time_diff;
   clock_gettime(CLOCK_MONOTONIC_RAW, &tp_start);
215 216 217 218 219 220
   usrp_state_t *s = (usrp_state_t*)device->priv;
   int samples_received=0,i,j;
   int nsamps2;  // aligned to upper 32 or 16 byte boundary
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
   nsamps2 = (nsamps+7)>>3;
221
   __m256i buff_tmp[2][nsamps2];
222 223
#else
   nsamps2 = (nsamps+3)>>2;
224
   __m128i buff_tmp[2][nsamps2];
225 226 227
#endif
#elif defined(__arm__)
   nsamps2 = (nsamps+3)>>2;
228
   int16x8_t buff_tmp[2][nsamps2];
229 230
#endif

231

232
  if (device->type == USRP_B200_DEV) {  
233
    if (cc>1) {
gauthier's avatar
gauthier committed
234
    // receive multiple channels (e.g. RF A and RF B)
235
      std::vector<void *> buff_ptrs;
236
 
237 238 239
      for (int i=0;i<cc;i++) buff_ptrs.push_back(buff_tmp[i]);
      samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md);
    } else {
gauthier's avatar
gauthier committed
240
    // receive a single channel (e.g. from connector RF A)
241 242
      samples_received = s->rx_stream->recv(buff_tmp[0], nsamps, s->rx_md);
    }
243

244
  // bring RX data into 12 LSBs for softmodem RX
245 246
    for (int i=0;i<cc;i++) {
      for (int j=0; j<nsamps2; j++) {      
247 248
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
249
        ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
250
#else
251
        ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
252 253
#endif
#elif defined(__arm__)
254
        ((int16x8_t*)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
255
#endif
256
      }
257
    }
258
  } else if (device->type == USRP_X300_DEV) {
259
    if (cc>1) { 
260 261 262 263 264 265 266 267
    // receive multiple channels (e.g. RF A and RF B)
      std::vector<void *> buff_ptrs;
 
      for (int i=0;i<cc;i++) buff_ptrs.push_back(buff[i]);
      samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md);
    } else {
    // receive a single channel (e.g. from connector RF A)
      samples_received = s->rx_stream->recv(buff[0], nsamps, s->rx_md);
268
    }
269
  }
270

271 272 273 274
  if (samples_received < nsamps) {
    printf("[recv] received %d samples out of %d\n",samples_received,nsamps);
    
  }
275

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
  //handle the error code
  switch(s->rx_md.error_code){
  case uhd::rx_metadata_t::ERROR_CODE_NONE:
    break;
  case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
    printf("[recv] USRP RX OVERFLOW!\n");
    s->num_overflows++;
    break;
  case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
    printf("[recv] USRP RX TIMEOUT!\n");
    break;
  default:
    printf("[recv] Unexpected error on RX, Error code: 0x%x\n",s->rx_md.error_code);
    break;
  }
291 292 293
  s->rx_count += nsamps;
  s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
  *ptimestamp = s->rx_timestamp;
294

295 296 297 298 299 300 301 302 303 304
  clock_gettime(CLOCK_MONOTONIC_RAW, &tp_end);
  time_diff = (tp_end.tv_sec - tp_start.tv_sec) *1E09  + (tp_end.tv_nsec - tp_start.tv_nsec);
  if  (time_min==0 ||loop==1 || time_min > time_diff)
    time_min=time_diff;
  if  (time_max==0 ||loop==1 || time_max < time_diff)
    time_max=time_diff;
  if (time_avg ==0 ||loop==1)
    time_avg= time_diff;
  else
    time_avg=(time_diff+time_avg) /2.0;
305
  /*
306 307 308 309
  //prints statics of uhd every 10 seconds
  if ( loop % (10 * ((int)device->openair0_cfg[0].sample_rate /(int)nsamps )) ==0)
     LOG_I(HW,"usrp_read: min(ns)=%d, max(ns)=%d, avg(ns)=%d\n", (int)time_min, (int)time_max,(int)time_avg);

310
     loop++;*/
311 312 313
  return samples_received;
}

314 315 316
/*! \brief Get current timestamp of USRP
 * \param device the hardware to use
*/
317 318 319 320 321 322 323 324
openair0_timestamp get_usrp_time(openair0_device *device) 
{
 
  usrp_state_t *s = (usrp_state_t*)device->priv;
  
  return s->usrp->get_time_now().to_ticks(s->sample_rate);
} 

325 326 327 328
/*! \brief Compares two variables within precision
 * \param a first variable
 * \param b second variable
*/
329 330 331 332
static bool is_equal(double a, double b)
{
  return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
}
333

334 335 336 337 338 339
/*! \brief Set frequencies (TX/RX)
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \param dummy dummy variable not used
 * \returns 0 in success 
 */
340
int trx_usrp_set_freq(openair0_device* device, openair0_config_t *openair0_cfg, int dummy) {
341 342 343

  usrp_state_t *s = (usrp_state_t*)device->priv;

344
  printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);
345 346 347 348 349 350 351
  s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[0]);
  s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[0]);

  return(0);
  
}

352 353 354 355 356
/*! \brief Set RX frequencies 
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \returns 0 in success 
 */
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373
int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg) {

  usrp_state_t *s = (usrp_state_t*)device->priv;
  static int first_call=1;
  static double rf_freq,diff;

  uhd::tune_request_t rx_tune_req(openair0_cfg[0].rx_freq[0]);

  rx_tune_req.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
  rx_tune_req.rf_freq = openair0_cfg[0].rx_freq[0];
  rf_freq=openair0_cfg[0].rx_freq[0];
  s->usrp->set_rx_freq(rx_tune_req);

  return(0);
  
}

374 375 376 377 378
/*! \brief Set Gains (TX/RX)
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \returns 0 in success 
 */
379
int trx_usrp_set_gains(openair0_device* device, 
380
		       openair0_config_t *openair0_cfg) {
381 382 383 384

  usrp_state_t *s = (usrp_state_t*)device->priv;

  s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[0]);
385 386 387 388 389 390 391 392 393
  ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(0);
  // limit to maximum gain
  if (openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0] > gain_range.stop()) {
    
    printf("RX Gain 0 too high, reduce by %f dB\n",
	   openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0] - gain_range.stop());	   
    exit(-1);
  }
  s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0]);
394
  printf("Setting USRP RX gain to %f (rx_gain %f,gain_range.stop() %f)\n", openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0],openair0_cfg[0].rx_gain[0],gain_range.stop());
395

396 397
  return(0);
}
398

399 400 401
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
402
int trx_usrp_stop(openair0_device* device) {
403 404
  return(0);
}
405

406
/*! \brief USRPB210 RX calibration table */
407
rx_gain_calib_table_t calib_table_b210[] = {
Raymond Knopp's avatar
Raymond Knopp committed
408 409 410 411 412
  {3500000000.0,44.0},
  {2660000000.0,49.0},
  {2300000000.0,50.0},
  {1880000000.0,53.0},
  {816000000.0,58.0},
413 414
  {-1,0}};

415
/*! \brief USRPB210 RX calibration table */
416 417 418 419 420 421
rx_gain_calib_table_t calib_table_b210_38[] = {
  {3500000000.0,44.0},
  {2660000000.0,49.8},
  {2300000000.0,51.0},
  {1880000000.0,53.0},
  {816000000.0,57.0},
422 423
  {-1,0}};

424
/*! \brief USRPx310 RX calibration table */
425 426
rx_gain_calib_table_t calib_table_x310[] = {
  {3500000000.0,77.0},
427
  {2660000000.0,81.0},
428 429
  {2300000000.0,81.0},
  {1880000000.0,82.0},
430
  {816000000.0,85.0},
431 432
  {-1,0}};

433 434 435 436 437
/*! \brief Set RX gain offset 
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
 * \returns 0 in success 
 */
438
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
439 440 441

  int i=0;
  // loop through calibration table to find best adjustment factor for RX frequency
442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467
  double min_diff = 6e9,diff,gain_adj=0.0;
  if (bw_gain_adjust==1) {
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:      
      break;
    case 23040000:
      gain_adj=1.25;
      break;
    case 15360000:
      gain_adj=3.0;
      break;
    case 7680000:
      gain_adj=6.0;
      break;
    case 3840000:
      gain_adj=9.0;
      break;
    case 1920000:
      gain_adj=12.0;
      break;
    default:
      printf("unknown sampling rate %d\n",(int)openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
468 469
  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);
470
    printf("cal %d: freq %f, offset %f, diff %f\n",
471 472 473
	   i,
	   openair0_cfg->rx_gain_calib_table[i].freq,
	   openair0_cfg->rx_gain_calib_table[i].offset,diff);
474 475
    if (min_diff > diff) {
      min_diff = diff;
476
      openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
477 478 479 480 481 482
    }
    i++;
  }
  
}

483 484 485 486
/*! \brief print the USRP statistics  
* \param device the hardware to use
* \returns  0 on success
*/
487
int trx_usrp_get_stats(openair0_device* device) {
488

489 490 491
  return(0);

}
492 493 494 495 496

/*! \brief Reset the USRP statistics  
* \param device the hardware to use
* \returns  0 on success
*/
497
int trx_usrp_reset_stats(openair0_device* device) {
498 499 500 501

  return(0);

}
502

503

504

505
extern "C" {
506 507 508 509
/*! \brief Initialize Openair USRP target. It returns 0 if OK
* \param device the hardware to use
* \param openair0_cfg RF frontend parameters set by application
*/
510
  int device_init(openair0_device* device, openair0_config_t *openair0_cfg) {
511
    
512 513 514 515 516
    uhd::set_thread_priority_safe(1.0);
    usrp_state_t *s = (usrp_state_t*)malloc(sizeof(usrp_state_t));
    memset(s, 0, sizeof(usrp_state_t));
    
    // Initialize USRP device
517

518
  device->openair0_cfg = openair0_cfg;
519

520
  std::string args = "type=b200";
knopp's avatar
knopp committed
521 522


523
  uhd::device_addrs_t device_adds = uhd::device::find(args);
524
  size_t i;
525 526 527 528 529
  
  int vers=0,subvers=0,subsubvers=0;
  int bw_gain_adjust=0;

  sscanf(uhd::get_version_string().c_str(),"%d.%d.%d",&vers,&subvers,&subsubvers);
530

531
  printf("Checking for USRPs : UHD %s (%d.%d.%d)\n",uhd::get_version_string().c_str(),vers,subvers,subsubvers);
532
  
533 534
  if(device_adds.size() == 0)
  {
535 536 537 538 539 540 541
    double usrp_master_clock = 184.32e6;

    std::string args = "type=x300";
    
    // workaround for an api problem, master clock has to be set with the constructor not via set_master_clock_rate
    args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
    
knopp's avatar
knopp committed
542
//    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
543
    
544
    uhd::device_addrs_t device_adds = uhd::device::find(args);
545

546 547 548 549 550
    if(device_adds.size() == 0)
    {
      std::cerr<<"No USRP Device Found. " << std::endl;
      free(s);
      return -1;
knopp's avatar
knopp committed
551

552
    }
knopp's avatar
knopp committed
553

554

555 556 557 558 559 560 561 562
    printf("Found USRP X300\n");
    s->usrp = uhd::usrp::multi_usrp::make(args);
    //  s->usrp->set_rx_subdev_spec(rx_subdev);
    //  s->usrp->set_tx_subdev_spec(tx_subdev);

    // lock mboard clocks
    s->usrp->set_clock_source("internal");
    
563
    //Setting device type to USRP X300/X310 
564
    device->type=USRP_X300_DEV;
565

566 567 568
    // this is not working yet, master clock has to be set via constructor
    // set master clock rate and sample rate for tx & rx for streaming
    //s->usrp->set_master_clock_rate(usrp_master_clock);
569 570

    openair0_cfg[0].rx_gain_calib_table = calib_table_x310;
571
    
572 573
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
574
            // from usrp_time_offset
575
      //openair0_cfg[0].samples_per_packet    = 2048;
576
      openair0_cfg[0].tx_sample_advance     = 15;
577 578
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
579
      break;
580
    case 15360000:
581
      //openair0_cfg[0].samples_per_packet    = 2048;
582
      openair0_cfg[0].tx_sample_advance     = 45;
583 584
      openair0_cfg[0].tx_bw                 = 10e6;
      openair0_cfg[0].rx_bw                 = 10e6;
585
      break;
586
    case 7680000:
587
      //openair0_cfg[0].samples_per_packet    = 2048;
588
      openair0_cfg[0].tx_sample_advance     = 50;
589 590
      openair0_cfg[0].tx_bw                 = 5e6;
      openair0_cfg[0].rx_bw                 = 5e6;
591
      break;
592
    case 1920000:
593
      //openair0_cfg[0].samples_per_packet    = 2048;
594
      openair0_cfg[0].tx_sample_advance     = 50;
595 596
      openair0_cfg[0].tx_bw                 = 1.25e6;
      openair0_cfg[0].rx_bw                 = 1.25e6;
597 598 599 600 601 602 603
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }

604 605
  } else {
    printf("Found USRP B200");
606
    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096" ; 
607 608 609 610
    s->usrp = uhd::usrp::multi_usrp::make(args);

    //  s->usrp->set_rx_subdev_spec(rx_subdev);
    //  s->usrp->set_tx_subdev_spec(tx_subdev);
611 612 613 614
    
    // do not explicitly set the clock to "internal", because this will disable the gpsdo
    //    // lock mboard clocks
    //    s->usrp->set_clock_source("internal");
615
    // set master clock rate and sample rate for tx & rx for streaming
616

617
    device->type = USRP_B200_DEV;
618

knopp's avatar
knopp committed
619

620 621 622 623 624 625 626 627
    if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
      openair0_cfg[0].rx_gain_calib_table = calib_table_b210;
      bw_gain_adjust=0;
    }
    else {
      openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
      bw_gain_adjust=1;
    }
628

629 630
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
631
      s->usrp->set_master_clock_rate(30.72e6);
632
      //openair0_cfg[0].samples_per_packet    = 1024;
633
      openair0_cfg[0].tx_sample_advance     = 115;
634 635
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
636
      break;
637
    case 23040000:
638
      s->usrp->set_master_clock_rate(23.04e6); //to be checked
639
      //openair0_cfg[0].samples_per_packet    = 1024;
640
      openair0_cfg[0].tx_sample_advance     = 113;
641 642
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
643
      break;
644
    case 15360000:
645
      s->usrp->set_master_clock_rate(30.72e06);
646
      //openair0_cfg[0].samples_per_packet    = 1024;
647
      openair0_cfg[0].tx_sample_advance     = 103; 
648 649
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
650
      break;
651
    case 7680000:
Rohit Gupta's avatar
Rohit Gupta committed
652
      s->usrp->set_master_clock_rate(30.72e6);
653
      //openair0_cfg[0].samples_per_packet    = 1024;
654
      openair0_cfg[0].tx_sample_advance     = 80;
655 656
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
657
      break;
658
    case 1920000:
659
      s->usrp->set_master_clock_rate(30.72e6);
660
      //openair0_cfg[0].samples_per_packet    = 1024;
661
      openair0_cfg[0].tx_sample_advance     = 40;
662 663
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
664 665 666 667 668 669 670
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
knopp's avatar
knopp committed
671

672
  /* device specific */
673 674
  //openair0_cfg[0].txlaunch_wait = 1;//manage when TX processing is triggered
  //openair0_cfg[0].txlaunch_wait_slotcount = 1; //manage when TX processing is triggered
675 676
  openair0_cfg[0].iq_txshift = 4;//shift
  openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
677
  
678
  for(i=0;i<s->usrp->get_rx_num_channels();i++) {
679
    if (i<openair0_cfg[0].rx_num_channels) {
knopp's avatar
knopp committed
680
      s->usrp->set_rx_rate(openair0_cfg[0].sample_rate,i);
681 682
      //s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
      //printf("Setting rx freq/gain on channel %lu/%lu : BW %f (readback %f)\n",i,s->usrp->get_rx_num_channels(),openair0_cfg[0].rx_bw/1e6,s->usrp->get_rx_bandwidth(i)/1e6);
knopp's avatar
knopp committed
683
      s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[i],i);
684
      set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust);
685 686 687 688 689 690 691 692 693 694 695 696

      ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
      // limit to maximum gain
      if (openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] > gain_range.stop()) {
	
        printf("RX Gain %lu too high, lower by %f dB\n",i,openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] - gain_range.stop());
	exit(-1);
      }
      s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],i);
      printf("RX Gain %lu %f (%f) => %f (max %f)\n",i,
	     openair0_cfg[0].rx_gain[i],openair0_cfg[0].rx_gain_offset[i],
	     openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],gain_range.stop());
697 698
    }
  }
699
  for(i=0;i<s->usrp->get_tx_num_channels();i++) {
700
    if (i<openair0_cfg[0].tx_num_channels) {
knopp's avatar
knopp committed
701
      s->usrp->set_tx_rate(openair0_cfg[0].sample_rate,i);
702 703
      //s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
      //printf("Setting tx freq/gain on channel %lu/%lu: BW %f (readback %f)\n",i,s->usrp->get_tx_num_channels(),openair0_cfg[0].tx_bw/1e6,s->usrp->get_tx_bandwidth(i)/1e6);
knopp's avatar
knopp committed
704 705
      s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[i],i);
      s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[i],i);
706 707
    }
  }
knopp's avatar
knopp committed
708 709


710 711
  // display USRP settings
  std::cout << boost::format("Actual master clock: %fMHz...") % (s->usrp->get_master_clock_rate()/1e6) << std::endl;
712 713
  
  sleep(1);
714 715

  // create tx & rx streamer
716
  uhd::stream_args_t stream_args_rx("sc16", "sc16");
717
  //stream_args_rx.args["spp"] = str(boost::format("%d") % 2048);//(openair0_cfg[0].rx_num_channels*openair0_cfg[0].samples_per_packet));
718
  for (i = 0; i<openair0_cfg[0].rx_num_channels; i++)
719 720 721 722 723 724 725
    stream_args_rx.channels.push_back(i);
  s->rx_stream = s->usrp->get_rx_stream(stream_args_rx);
  std::cout << boost::format("rx_max_num_samps %u") % (s->rx_stream->get_max_num_samps()) << std::endl;
  //openair0_cfg[0].samples_per_packet = s->rx_stream->get_max_num_samps();

  uhd::stream_args_t stream_args_tx("sc16", "sc16");
  //stream_args_tx.args["spp"] = str(boost::format("%d") % 2048);//(openair0_cfg[0].tx_num_channels*openair0_cfg[0].samples_per_packet));
726
  for (i = 0; i<openair0_cfg[0].tx_num_channels; i++)
727 728
      stream_args_tx.channels.push_back(i);
  s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);
729 730
  std::cout << boost::format("tx_max_num_samps %u") % (s->tx_stream->get_max_num_samps()) << std::endl;

731

732 733 734 735 736
 /* Setting TX/RX BW after streamers are created due to USRP calibration issue */
  for(i=0;i<s->usrp->get_tx_num_channels();i++) {
    if (i<openair0_cfg[0].tx_num_channels) {
      s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
      printf("Setting tx freq/gain on channel %lu/%lu: BW %f (readback %f)\n",i,s->usrp->get_tx_num_channels(),openair0_cfg[0].tx_bw/1e6,s->usrp->get_tx_bandwidth(i)/1e6);
Rohit Gupta's avatar
Rohit Gupta committed
737 738 739 740 741
    }
  }
  for(i=0;i<s->usrp->get_rx_num_channels();i++) {
    if (i<openair0_cfg[0].rx_num_channels) {
      s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
742 743 744 745
      printf("Setting rx freq/gain on channel %lu/%lu : BW %f (readback %f)\n",i,s->usrp->get_rx_num_channels(),openair0_cfg[0].rx_bw/1e6,s->usrp->get_rx_bandwidth(i)/1e6);
    }
  }

746
  s->usrp->set_time_now(uhd::time_spec_t(0.0));
747
 
748

knopp's avatar
knopp committed
749 750
  for (i=0;i<openair0_cfg[0].rx_num_channels;i++) {
    if (i<openair0_cfg[0].rx_num_channels) {
751
      printf("RX Channel %lu\n",i);
knopp's avatar
knopp committed
752 753 754 755 756 757 758 759 760 761 762
      std::cout << boost::format("Actual RX sample rate: %fMSps...") % (s->usrp->get_rx_rate(i)/1e6) << std::endl;
      std::cout << boost::format("Actual RX frequency: %fGHz...") % (s->usrp->get_rx_freq(i)/1e9) << std::endl;
      std::cout << boost::format("Actual RX gain: %f...") % (s->usrp->get_rx_gain(i)) << std::endl;
      std::cout << boost::format("Actual RX bandwidth: %fM...") % (s->usrp->get_rx_bandwidth(i)/1e6) << std::endl;
      std::cout << boost::format("Actual RX antenna: %s...") % (s->usrp->get_rx_antenna(i)) << std::endl;
    }
  }

  for (i=0;i<openair0_cfg[0].tx_num_channels;i++) {

    if (i<openair0_cfg[0].tx_num_channels) { 
763
      printf("TX Channel %lu\n",i);
knopp's avatar
knopp committed
764 765 766 767 768 769 770 771
      std::cout << std::endl<<boost::format("Actual TX sample rate: %fMSps...") % (s->usrp->get_tx_rate(i)/1e6) << std::endl;
      std::cout << boost::format("Actual TX frequency: %fGHz...") % (s->usrp->get_tx_freq(i)/1e9) << std::endl;
      std::cout << boost::format("Actual TX gain: %f...") % (s->usrp->get_tx_gain(i)) << std::endl;
      std::cout << boost::format("Actual TX bandwidth: %fM...") % (s->usrp->get_tx_bandwidth(i)/1e6) << std::endl;
      std::cout << boost::format("Actual TX antenna: %s...") % (s->usrp->get_tx_antenna(i)) << std::endl;
    }
  }

772
  std::cout << boost::format("Device timestamp: %f...") % (s->usrp->get_time_now().get_real_secs()) << std::endl;
773 774 775 776

  device->priv = s;
  device->trx_start_func = trx_usrp_start;
  device->trx_write_func = trx_usrp_write;
777 778 779 780 781 782 783
  device->trx_read_func  = trx_usrp_read;
  device->trx_get_stats_func = trx_usrp_get_stats;
  device->trx_reset_stats_func = trx_usrp_reset_stats;
  device->trx_end_func   = trx_usrp_end;
  device->trx_stop_func  = trx_usrp_stop;
  device->trx_set_freq_func = trx_usrp_set_freq;
  device->trx_set_gains_func   = trx_usrp_set_gains;
784 785
  device->openair0_cfg = openair0_cfg;

knopp's avatar
knopp committed
786
  s->sample_rate = openair0_cfg[0].sample_rate;
787 788 789 790 791 792 793 794 795
  // TODO:
  // init tx_forward_nsamps based usrp_time_offset ex
  if(is_equal(s->sample_rate, (double)30.72e6))
    s->tx_forward_nsamps  = 176;
  if(is_equal(s->sample_rate, (double)15.36e6))
    s->tx_forward_nsamps = 90;
  if(is_equal(s->sample_rate, (double)7.68e6))
    s->tx_forward_nsamps = 50;
  return 0;
796
  }
797
}
798
/*@}*/