usrp_lib.cpp 35.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

22 23
/** usrp_lib.cpp
 *
24
 * \author: HongliangXU : hong-liang-xu@agilent.com
25 26 27 28 29 30 31 32
 */

#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/usrp/multi_usrp.hpp>
33
#include <uhd/version.hpp>
34 35 36 37 38 39
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
40
#include <time.h>
41
#include "UTIL/LOG/log_extern.h"
42
#include "common_lib.h"
laurent's avatar
laurent committed
43 44
#include "assertions.h"

45 46 47
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
laurent's avatar
laurent committed
48

49 50 51
#ifdef __AVX2__
#  include <immintrin.h>
#endif
52

53 54 55 56
#ifdef __arm__
#  include <arm_neon.h>
#endif

57 58 59 60
/** @addtogroup _USRP_PHY_RF_INTERFACE_
 * @{
 */

laurent's avatar
laurent committed
61 62
/*! \brief USRP Configuration */
typedef struct {
63

laurent's avatar
laurent committed
64 65 66 67 68
    // --------------------------------
    // variables for USRP configuration
    // --------------------------------
    //! USRP device pointer
    uhd::usrp::multi_usrp::sptr usrp;
69
  
70
  //create a send streamer and a receive streamer
71
  //! USRP TX Stream
72
  uhd::tx_streamer::sptr tx_stream;
73
  //! USRP RX Stream
74 75
  uhd::rx_streamer::sptr rx_stream;

76
  //! USRP TX Metadata
77
  uhd::tx_metadata_t tx_md;
78
  //! USRP RX Metadata
79 80
  uhd::rx_metadata_t rx_md;

laurent's avatar
laurent committed
81 82
    //! Sampling rate
    double sample_rate;
83

laurent's avatar
laurent committed
84 85
    //! TX forward samples. We use usrp_time_offset to get this value
    int tx_forward_nsamps; //166 for 20Mhz
86

laurent's avatar
laurent committed
87 88 89 90 91 92 93 94 95 96
    // --------------------------------
    // Debug and output control
    // --------------------------------
    int num_underflows;
    int num_overflows;
    int num_seq_errors;
    int64_t tx_count;
    int64_t rx_count;
    //! timestamp of RX packet
    openair0_timestamp rx_timestamp;
97

laurent's avatar
laurent committed
98
} usrp_state_t;
99 100 101



102 103 104
/*! \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
*/
laurent's avatar
laurent committed
105
static int trx_usrp_start(openair0_device *device) {
106

laurent's avatar
laurent committed
107
    usrp_state_t *s = (usrp_state_t*)device->priv;
108

laurent's avatar
laurent committed
109 110 111 112 113
    // init recv and send streaming
    uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
    cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
    cmd.stream_now = true;
    s->rx_stream->issue_stream_cmd(cmd);
114

laurent's avatar
laurent committed
115 116 117 118
    s->tx_md.time_spec = cmd.time_spec + uhd::time_spec_t(1-(double)s->tx_forward_nsamps/s->sample_rate);
    s->tx_md.has_time_spec = true;
    s->tx_md.start_of_burst = true;
    s->tx_md.end_of_burst = false;
119

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

laurent's avatar
laurent committed
131 132 133 134 135
    s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
    //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;
136 137

}
138

139 140
/*! \brief Called to send samples to the USRP RF target
      @param device pointer to the device structure specific to the RF hardware target
laurent's avatar
laurent committed
141
      @param timestamp The timestamp at whicch the first sample MUST be sent
142 143 144 145
      @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
laurent's avatar
laurent committed
146 147 148 149
*/
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
    int ret=0;
    usrp_state_t *s = (usrp_state_t*)device->priv;
knopp's avatar
knopp committed
150

laurent's avatar
laurent committed
151 152
    s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
    s->tx_md.has_time_spec = flags;
knopp's avatar
knopp committed
153

154
  
knopp's avatar
knopp committed
155
  if(flags>0)
156 157 158
    s->tx_md.has_time_spec = true;
  else
    s->tx_md.has_time_spec = false;
159

knopp's avatar
knopp committed
160 161 162 163 164 165 166 167 168
  if (flags == 2) { // start of burst
    s->tx_md.start_of_burst = true;
    s->tx_md.end_of_burst = false;
  }
  else if (flags == 3) { // end of burst
    s->tx_md.start_of_burst = false;
    s->tx_md.end_of_burst = true;
  }
  else if (flags == 4) { // start and end
169 170 171
    s->tx_md.start_of_burst = true;
    s->tx_md.end_of_burst = true;
  }
knopp's avatar
knopp committed
172
  else if (flags==1) { // middle of burst
173 174 175
    s->tx_md.start_of_burst = false;
    s->tx_md.end_of_burst = false;
  }
176
  
knopp's avatar
knopp committed
177 178
  if (cc>1) {
    std::vector<void *> buff_ptrs;
laurent's avatar
laurent committed
179 180
        for (int i=0; i<cc; i++)
            buff_ptrs.push_back(buff[i]);
181
    ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md,1e-3);
laurent's avatar
laurent committed
182
    } else
183 184 185
    ret = (int)s->tx_stream->send(buff[0], nsamps, s->tx_md,1e-3);


186

laurent's avatar
laurent committed
187 188
    if (ret != nsamps)
        LOG_E(PHY,"[xmit] tx samples %d != %d\n",ret,nsamps);
189

laurent's avatar
laurent committed
190
    return ret;
191 192
}

193 194 195 196 197 198 199 200 201 202 203
/*! \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
*/
laurent's avatar
laurent committed
204 205 206 207
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
    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
208 209
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
laurent's avatar
laurent committed
210 211
    nsamps2 = (nsamps+7)>>3;
    __m256i buff_tmp[2][nsamps2];
212
#else
laurent's avatar
laurent committed
213 214
    nsamps2 = (nsamps+3)>>2;
    __m128i buff_tmp[2][nsamps2];
215 216
#endif
#elif defined(__arm__)
laurent's avatar
laurent committed
217 218
    nsamps2 = (nsamps+3)>>2;
    int16x8_t buff_tmp[2][nsamps2];
219 220
#endif

laurent's avatar
laurent committed
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    if (device->type == USRP_B200_DEV) {
        if (cc>1) {
            // 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_tmp[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=0;
            while (samples_received != nsamps) {
                samples_received += s->rx_stream->recv(buff_tmp[0]+samples_received,
                                                       nsamps-samples_received, s->rx_md);
                if (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE)
                    break;
            }
        }
        // bring RX data into 12 LSBs for softmodem RX
        for (int i=0; i<cc; i++) {
            for (int j=0; j<nsamps2; j++) {
240 241
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
242
        ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
243
#else
244
        ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
245 246
#endif
#elif defined(__arm__)
247
        ((int16x8_t*)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
248
#endif
249
      }
250
    }
251
  } else if (device->type == USRP_X300_DEV) {
252
    if (cc>1) { 
253 254 255 256 257 258 259 260
    // 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);
261
    }
262
  }
laurent's avatar
laurent committed
263 264
    if (samples_received < nsamps)
        LOG_E(PHY,"[recv] received %d samples out of %d\n",samples_received,nsamps);
265

laurent's avatar
laurent committed
266 267
    if ( s->rx_md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
        LOG_E(PHY,s->rx_md.to_pp_string(true).c_str());
268

laurent's avatar
laurent committed
269 270 271 272 273 274 275
    s->rx_count += nsamps;
    s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
    *ptimestamp = s->rx_timestamp;
#ifdef DEBUG_USRP
    check(50);
#endif
    return samples_received;
276 277
}

278 279 280 281
/*! \brief Compares two variables within precision
 * \param a first variable
 * \param b second variable
*/
laurent's avatar
laurent committed
282
static bool is_equal(double a, double b) {
283 284
  return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
}
285

286 287 288 289 290 291 292 293 294
void *freq_thread(void *arg) {
  
  openair0_device *device=(openair0_device *)arg;
  usrp_state_t *s = (usrp_state_t*)device->priv;
  
  s->usrp->set_tx_freq(device->openair0_cfg[0].tx_freq[0]);
  s->usrp->set_rx_freq(device->openair0_cfg[0].rx_freq[0]);
}
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
295 296 297 298 299
 * \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 
 */
300
int trx_usrp_set_freq(openair0_device* device, openair0_config_t *openair0_cfg, int dont_block) {
301 302

  usrp_state_t *s = (usrp_state_t*)device->priv;
303
  pthread_t f_thread;
304

305
  printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);
306 307 308 309 310 311 312 313

  // spawn a thread to handle the frequency change to not block the calling thread
  if (dont_block == 1)
    pthread_create(&f_thread,NULL,freq_thread,(void*)device);
  else {
    s->usrp->set_tx_freq(device->openair0_cfg[0].tx_freq[0]);
    s->usrp->set_rx_freq(device->openair0_cfg[0].rx_freq[0]);
  }
laurent's avatar
laurent committed
314

315 316 317

}

laurent's avatar
laurent committed
318
/*! \brief Set RX frequencies
319 320
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
laurent's avatar
laurent committed
321
 * \returns 0 in success
322
 */
323 324
int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg) {

laurent's avatar
laurent committed
325 326 327
    usrp_state_t *s = (usrp_state_t*)device->priv;
    static int first_call=1;
    static double rf_freq,diff;
328

laurent's avatar
laurent committed
329
    uhd::tune_request_t rx_tune_req(openair0_cfg[0].rx_freq[0]);
330

laurent's avatar
laurent committed
331 332 333 334 335 336
    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);
337 338 339

}

340 341 342
/*! \brief Set Gains (TX/RX)
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
laurent's avatar
laurent committed
343
 * \returns 0 in success
344
 */
laurent's avatar
laurent committed
345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
int trx_usrp_set_gains(openair0_device* device,
                       openair0_config_t *openair0_cfg) {

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

    s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[0]);
    ::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()) {
        LOG_E(PHY,"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]);
    LOG_I(PHY,"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());
362

363 364
  return(0);
}
365

366 367 368
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
369
int trx_usrp_stop(openair0_device* device) {
370 371
  return(0);
}
372

373
/*! \brief USRPB210 RX calibration table */
374
rx_gain_calib_table_t calib_table_b210[] = {
laurent's avatar
laurent committed
375 376 377 378 379 380 381
    {3500000000.0,44.0},
    {2660000000.0,49.0},
    {2300000000.0,50.0},
    {1880000000.0,53.0},
    {816000000.0,58.0},
    {-1,0}
};
382

383
/*! \brief USRPB210 RX calibration table */
384
rx_gain_calib_table_t calib_table_b210_38[] = {
laurent's avatar
laurent committed
385 386 387 388 389 390 391
    {3500000000.0,44.0},
    {2660000000.0,49.8},
    {2300000000.0,51.0},
    {1880000000.0,53.0},
    {816000000.0,57.0},
    {-1,0}
};
392

393
/*! \brief USRPx310 RX calibration table */
394
rx_gain_calib_table_t calib_table_x310[] = {
laurent's avatar
laurent committed
395 396 397 398 399 400 401 402 403
    {3500000000.0,77.0},
    {2660000000.0,81.0},
    {2300000000.0,81.0},
    {1880000000.0,82.0},
    {816000000.0,85.0},
    {-1,0}
};

/*! \brief Set RX gain offset
404 405
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
laurent's avatar
laurent committed
406
 * \returns 0 in success
407
 */
408
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
409

laurent's avatar
laurent committed
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
    int i=0;
    // loop through calibration table to find best adjustment factor for RX frequency
    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:
            LOG_E(PHY,"unknown sampling rate %d\n",(int)openair0_cfg[0].sample_rate);
            exit(-1);
            break;
        }
437
    }
laurent's avatar
laurent committed
438 439 440 441 442
    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);
        LOG_I(PHY,"cal %d: freq %f, offset %f, diff %f\n",
              i,
              openair0_cfg->rx_gain_calib_table[i].freq,
443
	   openair0_cfg->rx_gain_calib_table[i].offset,diff);
444 445
    if (min_diff > diff) {
      min_diff = diff;
446
      openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
447 448
    }
    i++;
laurent's avatar
laurent committed
449
    }
450 451
}

laurent's avatar
laurent committed
452
/*! \brief print the USRP statistics
453 454 455
* \param device the hardware to use
* \returns  0 on success
*/
456
int trx_usrp_get_stats(openair0_device* device) {
laurent's avatar
laurent committed
457
    return(0);
458
}
459

laurent's avatar
laurent committed
460 461 462 463
/*! \brief Reset the USRP statistics
 * \param device the hardware to use
 * \returns  0 on success
 */
464
int trx_usrp_reset_stats(openair0_device* device) {
laurent's avatar
laurent committed
465
    return(0);
466
}
467

468
extern "C" {
laurent's avatar
laurent committed
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491
    /*! \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
         */
    int device_init(openair0_device* device, openair0_config_t *openair0_cfg) {
        //uhd::set_thread_priority_safe(1.0);
        usrp_state_t *s = (usrp_state_t*)calloc(sizeof(usrp_state_t),1);
        // Initialize USRP device
        device->openair0_cfg = openair0_cfg;

        std::string args = "type=b200";
        uhd::device_addrs_t device_adds = uhd::device::find(args);

        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);
        LOG_I(PHY,"Checking for USRPs : UHD %s (%d.%d.%d)\n",
              uhd::get_version_string().c_str(),vers,subvers,subsubvers);

        if(device_adds.size() == 0)  {
            double usrp_master_clock = 184.32e6;
            std::string args = "type=x300";
492

493 494 495
    // 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
496
//    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
497
    
498
    uhd::device_addrs_t device_adds = uhd::device::find(args);
499

500 501 502 503 504
    if(device_adds.size() == 0)
    {
      std::cerr<<"No USRP Device Found. " << std::endl;
      free(s);
      return -1;
knopp's avatar
knopp committed
505

506
    }
knopp's avatar
knopp committed
507

508

509 510 511 512 513 514
    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
515 516 517 518
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");
519
    
520
    //Setting device type to USRP X300/X310 
521
    device->type=USRP_X300_DEV;
522

523 524 525
    // 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);
526 527

    openair0_cfg[0].rx_gain_calib_table = calib_table_x310;
528
    
529 530
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
531
            // from usrp_time_offset
532
      //openair0_cfg[0].samples_per_packet    = 2048;
533
      openair0_cfg[0].tx_sample_advance     = 15;
534 535
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
536
      break;
537
    case 15360000:
538
      //openair0_cfg[0].samples_per_packet    = 2048;
539
      openair0_cfg[0].tx_sample_advance     = 45;
540 541
      openair0_cfg[0].tx_bw                 = 10e6;
      openair0_cfg[0].rx_bw                 = 10e6;
542
      break;
543
    case 7680000:
544
      //openair0_cfg[0].samples_per_packet    = 2048;
545
      openair0_cfg[0].tx_sample_advance     = 50;
546 547
      openair0_cfg[0].tx_bw                 = 5e6;
      openair0_cfg[0].rx_bw                 = 5e6;
548
      break;
549
    case 1920000:
550
      //openair0_cfg[0].samples_per_packet    = 2048;
551
      openair0_cfg[0].tx_sample_advance     = 50;
552 553
      openair0_cfg[0].tx_bw                 = 1.25e6;
      openair0_cfg[0].rx_bw                 = 1.25e6;
554 555 556 557 558 559 560
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }

561 562
  } else {
    printf("Found USRP B200");
563
    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=15360, recv_frame_size=15360" ; 
564 565 566 567
    s->usrp = uhd::usrp::multi_usrp::make(args);

    //  s->usrp->set_rx_subdev_spec(rx_subdev);
    //  s->usrp->set_tx_subdev_spec(tx_subdev);
568 569 570 571
    
    // do not explicitly set the clock to "internal", because this will disable the gpsdo
    //    // lock mboard clocks
    //    s->usrp->set_clock_source("internal");
572
    // set master clock rate and sample rate for tx & rx for streaming
573

574 575 576 577 578 579
    // lock mboard clocks
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");

580
    device->type = USRP_B200_DEV;
581

knopp's avatar
knopp committed
582

583 584 585 586 587 588 589 590
    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;
    }
591

592 593
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
594
      s->usrp->set_master_clock_rate(30.72e6);
595
      //openair0_cfg[0].samples_per_packet    = 1024;
596
      openair0_cfg[0].tx_sample_advance     = 115;
597 598
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
599
      break;
600
    case 23040000:
601
      s->usrp->set_master_clock_rate(23.04e6); //to be checked
602
      //openair0_cfg[0].samples_per_packet    = 1024;
603
      openair0_cfg[0].tx_sample_advance     = 113;
604 605
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
606
      break;
607
    case 15360000:
608
      s->usrp->set_master_clock_rate(30.72e06);
609
      //openair0_cfg[0].samples_per_packet    = 1024;
610
      openair0_cfg[0].tx_sample_advance     = 103; 
611 612
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
613
      break;
614
    case 7680000:
Rohit Gupta's avatar
Rohit Gupta committed
615
      s->usrp->set_master_clock_rate(30.72e6);
616
      //openair0_cfg[0].samples_per_packet    = 1024;
617
      openair0_cfg[0].tx_sample_advance     = 80;
618 619
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
620
      break;
621
    case 1920000:
622
      s->usrp->set_master_clock_rate(30.72e6);
623
      //openair0_cfg[0].samples_per_packet    = 1024;
624
      openair0_cfg[0].tx_sample_advance     = 40;
625 626
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
627 628 629 630 631 632 633
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
knopp's avatar
knopp committed
634

635
  /* device specific */
636 637
  //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
638 639
  openair0_cfg[0].iq_txshift = 4;//shift
  openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
640
  
641
  for(i=0;i<s->usrp->get_rx_num_channels();i++) {
642
    if (i<openair0_cfg[0].rx_num_channels) {
knopp's avatar
knopp committed
643
      s->usrp->set_rx_rate(openair0_cfg[0].sample_rate,i);
644 645
      //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
646
      s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[i],i);
647
      set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust);
648 649 650 651 652 653 654 655 656 657 658 659

      ::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());
660 661
    }
  }
662
  for(i=0;i<s->usrp->get_tx_num_channels();i++) {
663
    if (i<openair0_cfg[0].tx_num_channels) {
knopp's avatar
knopp committed
664
      s->usrp->set_tx_rate(openair0_cfg[0].sample_rate,i);
665 666
      //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
667 668
      s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[i],i);
      s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[i],i);
669 670
    }
  }
knopp's avatar
knopp committed
671 672


673 674
  // display USRP settings
  std::cout << boost::format("Actual master clock: %fMHz...") % (s->usrp->get_master_clock_rate()/1e6) << std::endl;
675 676
  
  sleep(1);
677 678

  // create tx & rx streamer
679
  uhd::stream_args_t stream_args_rx("sc16", "sc16");
680 681 682 683
  int samples=openair0_cfg[0].sample_rate;
  samples/=24000;
  //  stream_args_rx.args["spp"] = str(boost::format("%d") % samples);

684
  for (i = 0; i<openair0_cfg[0].rx_num_channels; i++)
685 686 687 688 689 690 691
    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));
692
  for (i = 0; i<openair0_cfg[0].tx_num_channels; i++)
693 694
      stream_args_tx.channels.push_back(i);
  s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);
695 696
  std::cout << boost::format("tx_max_num_samps %u") % (s->tx_stream->get_max_num_samps()) << std::endl;

697

698 699 700 701 702
 /* 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
703 704 705 706 707
    }
  }
  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);
708 709 710 711
      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);
    }
  }

712
  s->usrp->set_time_now(uhd::time_spec_t(0.0));
713
 
714

knopp's avatar
knopp committed
715 716
  for (i=0;i<openair0_cfg[0].rx_num_channels;i++) {
    if (i<openair0_cfg[0].rx_num_channels) {
717
      printf("RX Channel %lu\n",i);
knopp's avatar
knopp committed
718 719 720 721 722 723 724 725 726 727 728
      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) { 
729
      printf("TX Channel %lu\n",i);
knopp's avatar
knopp committed
730 731 732 733 734 735 736 737
      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;
    }
  }

738
  std::cout << boost::format("Device timestamp: %f...") % (s->usrp->get_time_now().get_real_secs()) << std::endl;
739

laurent's avatar
laurent committed
740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922
            //    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
            uhd::device_addrs_t device_adds = uhd::device::find(args);

            if(device_adds.size() == 0) {
                std::cerr<<"No USRP Device Found. " << std::endl;
                free(s);
                return -1;
            }
            LOG_I(PHY,"Found USRP X300\n");
            s->usrp = uhd::usrp::multi_usrp::make(args);
            // lock mboard clocks
            s->usrp->set_clock_source("internal");

            //Setting device type to USRP X300/X310
            device->type=USRP_X300_DEV;

            // 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);

            openair0_cfg[0].rx_gain_calib_table = calib_table_x310;

            switch ((int)openair0_cfg[0].sample_rate) {
            case 30720000:
                // from usrp_time_offset
                openair0_cfg[0].tx_sample_advance     = 15;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            case 15360000:
                openair0_cfg[0].tx_sample_advance     = 45;
                openair0_cfg[0].tx_bw                 = 10e6;
                openair0_cfg[0].rx_bw                 = 10e6;
                break;
            case 7680000:
                openair0_cfg[0].tx_sample_advance     = 50;
                openair0_cfg[0].tx_bw                 = 5e6;
                openair0_cfg[0].rx_bw                 = 5e6;
                break;
            case 1920000:
                openair0_cfg[0].tx_sample_advance     = 50;
                openair0_cfg[0].tx_bw                 = 1.25e6;
                openair0_cfg[0].rx_bw                 = 1.25e6;
                break;
            default:
                LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
                exit(-1);
                break;
            }

        } else {
            LOG_I(PHY,"Found USRP B200\n");
            args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=15360, recv_frame_size=15360" ;
            s->usrp = uhd::usrp::multi_usrp::make(args);
            device->type = USRP_B200_DEV;
            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;
            }

            switch ((int)openair0_cfg[0].sample_rate) {
            case 30720000:
                s->usrp->set_master_clock_rate(30.72e6);
                openair0_cfg[0].tx_sample_advance     = 115;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            case 23040000:
                s->usrp->set_master_clock_rate(23.04e6); //to be checked
                openair0_cfg[0].tx_sample_advance     = 113;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            case 15360000:
                s->usrp->set_master_clock_rate(30.72e06);
                openair0_cfg[0].tx_sample_advance     = 103;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            case 7680000:
                s->usrp->set_master_clock_rate(30.72e6);
                openair0_cfg[0].tx_sample_advance     = 80;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            case 1920000:
                s->usrp->set_master_clock_rate(30.72e6);
                openair0_cfg[0].tx_sample_advance     = 40;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            default:
                LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
                exit(-1);
                break;
            }
        }
        openair0_cfg[0].iq_txshift = 4;//shift
        openair0_cfg[0].iq_rxrescale = 15;//rescale iqs

        for(int i=0; i<s->usrp->get_rx_num_channels(); i++) {
            if (i<openair0_cfg[0].rx_num_channels) {
                s->usrp->set_rx_rate(openair0_cfg[0].sample_rate,i);
                s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[i],i);
                set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust);

                ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
                // limit to maximum gain
                AssertFatal( openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] <= gain_range.stop(),
                             "RX Gain too high, lower by %f dB\n",
                             openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] - gain_range.stop());
                s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],i);
                LOG_I(PHY,"RX Gain %d %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());
            }
        }
        for(int i=0; i<s->usrp->get_tx_num_channels(); i++) {
            if (i<openair0_cfg[0].tx_num_channels) {
                s->usrp->set_tx_rate(openair0_cfg[0].sample_rate,i);
                s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[i],i);
                s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[i],i);
            }
        }

        //s->usrp->set_clock_source("external");
        //s->usrp->set_time_source("external");

        // display USRP settings
        LOG_I(PHY,"Actual master clock: %fMHz...\n",s->usrp->get_master_clock_rate()/1e6);

        // create tx & rx streamer
        uhd::stream_args_t stream_args_rx("sc16", "sc16");
        int samples=openair0_cfg[0].sample_rate;
        //while ( samples > s->rx_stream->get_max_num_samps())
        samples/=24000;
        stream_args_rx.args["spp"] = str(boost::format("%d") % samples );
        for (int i = 0; i<openair0_cfg[0].rx_num_channels; i++)
            stream_args_rx.channels.push_back(i);
        s->rx_stream = s->usrp->get_rx_stream(stream_args_rx);
        LOG_I(PHY,"rx_max_num_samps %u\n",s->rx_stream->get_max_num_samps());

        uhd::stream_args_t stream_args_tx("sc16", "sc16");
        for (int i = 0; i<openair0_cfg[0].tx_num_channels; i++)
            stream_args_tx.channels.push_back(i);
        s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);

        /* Setting TX/RX BW after streamers are created due to USRP calibration issue */
        for(int i=0; i<s->usrp->get_tx_num_channels() && i<openair0_cfg[0].tx_num_channels; i++)
            s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);

        for(int i=0; i<s->usrp->get_rx_num_channels() && i<openair0_cfg[0].rx_num_channels; i++)
            s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);

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

        for (int i=0; i<openair0_cfg[0].rx_num_channels; i++) {
            LOG_I(PHY,"RX Channel %d\n",i);
            LOG_I(PHY,"  Actual RX sample rate: %fMSps...\n",s->usrp->get_rx_rate(i)/1e6);
            LOG_I(PHY,"  Actual RX frequency: %fGHz...\n", s->usrp->get_rx_freq(i)/1e9);
            LOG_I(PHY,"  Actual RX gain: %f...\n", s->usrp->get_rx_gain(i));
            LOG_I(PHY,"  Actual RX bandwidth: %fM...\n", s->usrp->get_rx_bandwidth(i)/1e6);
            LOG_I(PHY,"  Actual RX antenna: %s...\n", s->usrp->get_rx_antenna(i).c_str());
        }

        for (int i=0; i<openair0_cfg[0].tx_num_channels; i++) {
            LOG_I(PHY,"TX Channel %d\n",i);
            LOG_I(PHY,"  Actual TX sample rate: %fMSps...\n", s->usrp->get_tx_rate(i)/1e6);
            LOG_I(PHY,"  Actual TX frequency: %fGHz...\n", s->usrp->get_tx_freq(i)/1e9);
            LOG_I(PHY,"  Actual TX gain: %f...\n", s->usrp->get_tx_gain(i));
            LOG_I(PHY,"  Actual TX bandwidth: %fM...\n", s->usrp->get_tx_bandwidth(i)/1e6);
            LOG_I(PHY,"  Actual TX antenna: %s...\n", s->usrp->get_tx_antenna(i).c_str());
        }

        LOG_I(PHY,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());

        device->priv = s;
        device->trx_start_func = trx_usrp_start;
        device->trx_write_func = trx_usrp_write;
        device->trx_read_func  = trx_usrp_read;
923 924 925 926 927 928
  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;
929 930
  device->openair0_cfg = openair0_cfg;

knopp's avatar
knopp committed
931
  s->sample_rate = openair0_cfg[0].sample_rate;
932 933 934 935 936 937 938 939 940
  // 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;
941
  }
942
}
943
/*@}*/