usrp_lib.cpp 26.1 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

Thomas Laurent's avatar
Thomas Laurent committed
315
  return(0);
316 317 318

}

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

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

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

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

}

341 342 343
/*! \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
344
 * \returns 0 in success
345
 */
laurent's avatar
laurent committed
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
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());
363

364 365
  return(0);
}
366

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

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

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

394
/*! \brief USRPx310 RX calibration table */
395
rx_gain_calib_table_t calib_table_x310[] = {
laurent's avatar
laurent committed
396 397 398 399 400 401 402 403 404
    {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
405 406
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
laurent's avatar
laurent committed
407
 * \returns 0 in success
408
 */
409
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
410

laurent's avatar
laurent committed
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 437
    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;
        }
438
    }
laurent's avatar
laurent committed
439 440 441 442 443
    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,
444
	   openair0_cfg->rx_gain_calib_table[i].offset,diff);
445 446
    if (min_diff > diff) {
      min_diff = diff;
447
      openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
448 449
    }
    i++;
laurent's avatar
laurent committed
450
    }
451 452
}

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

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

469
extern "C" {
laurent's avatar
laurent committed
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
    /*! \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";
493

494 495 496
    // 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
497
//    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
498
    
Thomas Laurent's avatar
Thomas Laurent committed
499 500
            //    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);
501

Thomas Laurent's avatar
Thomas Laurent committed
502 503 504 505 506 507 508 509
            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
510 511 512 513
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");
514

Thomas Laurent's avatar
Thomas Laurent committed
515 516
            //Setting device type to USRP X300/X310
            device->type=USRP_X300_DEV;
517

Thomas Laurent's avatar
Thomas Laurent committed
518 519 520 521 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);

            openair0_cfg[0].rx_gain_calib_table = calib_table_x310;

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

Thomas Laurent's avatar
Thomas Laurent committed
556 557 558 559
        } 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);
560 561 562

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

569 570 571 572 573 574
    // lock mboard clocks
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");

Thomas Laurent's avatar
Thomas Laurent committed
575 576 577 578 579 580 581 582
            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;
            }
583

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

  /* device specific */
  //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
laurent's avatar
laurent committed
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662
        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);
Thomas Laurent's avatar
Thomas Laurent committed
663
  sleep(1);
laurent's avatar
laurent committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713

        // 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;
714 715 716 717 718 719
  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;
720 721
  device->openair0_cfg = openair0_cfg;

knopp's avatar
knopp committed
722
  s->sample_rate = openair0_cfg[0].sample_rate;
723 724 725 726 727 728 729 730 731
  // 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;
732
  }
733
}
734
/*@}*/