usrp_lib.cpp 28.4 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
 */

knopp's avatar
knopp committed
22 23
/** usrp_lib.cpp
 *
24
 * \author: HongliangXU : hong-liang-xu@agilent.com
knopp's avatar
knopp committed
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>
knopp's avatar
knopp committed
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"
knopp's avatar
knopp committed
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 {
knopp's avatar
knopp committed
63

laurent's avatar
laurent committed
64 65 66 67 68
    // --------------------------------
    // variables for USRP configuration
    // --------------------------------
    //! USRP device pointer
    uhd::usrp::multi_usrp::sptr usrp;
laurent's avatar
laurent committed
69 70 71 72 73 74 75 76 77 78 79

    //create a send streamer and a receive streamer
    //! USRP TX Stream
    uhd::tx_streamer::sptr tx_stream;
    //! USRP RX Stream
    uhd::rx_streamer::sptr rx_stream;

    //! USRP TX Metadata
    uhd::tx_metadata_t tx_md;
    //! USRP RX Metadata
    uhd::rx_metadata_t rx_md;
knopp's avatar
knopp committed
80

laurent's avatar
laurent committed
81 82
    //! Sampling rate
    double sample_rate;
knopp's avatar
knopp committed
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;
knopp's avatar
knopp committed
97

laurent's avatar
laurent committed
98
} usrp_state_t;
knopp's avatar
knopp committed
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) {
knopp's avatar
knopp committed
106

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

laurent's avatar
laurent committed
109 110 111
    // 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);
laurent's avatar
laurent committed
112
    cmd.stream_now = false; // start at constant delay
laurent's avatar
laurent committed
113
    s->rx_stream->issue_stream_cmd(cmd);
knopp's avatar
knopp committed
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;
knopp's avatar
knopp committed
119

laurent's avatar
laurent committed
120 121 122 123
    s->rx_count = 0;
    s->tx_count = 0;
    s->rx_timestamp = 0;
    return 0;
knopp's avatar
knopp committed
124
}
laurent's avatar
laurent committed
125
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
126 127
 * \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;
knopp's avatar
knopp committed
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;
knopp's avatar
knopp committed
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
141
      @param timestamp The timestamp at which the first sample MUST be sent
142 143
      @param buff Buffer which holds the samples
      @param nsamps number of samples to be sent
144
      @param antenna_id index of the antenna if the device has multiple antennas
145
      @param flags flags must be set to TRUE if timestamp parameter needs to be applied
laurent's avatar
laurent committed
146 147
*/
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  int ret=0;
  usrp_state_t *s = (usrp_state_t*)device->priv;
  
  int nsamps2;  // aligned to upper 32 or 16 byte boundary
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
  nsamps2 = (nsamps+7)>>3;
  __m256i buff_tx[2][nsamps2];
#else
  nsamps2 = (nsamps+3)>>2;
  __m128i buff_tx[2][nsamps2];
#endif
#elif defined(__arm__)
  nsamps2 = (nsamps+3)>>2;
  int16x8_t buff_tx[2][nsamps2];
#endif
  
  // bring RX data into 12 LSBs for softmodem RX
  for (int i=0; i<cc; i++) {
    for (int j=0; j<nsamps2; j++) {
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
      buff_tx[i][j] = _mm256_slli_epi16(((__m256i*)buff[i])[j],4);
#else
172
      buff_tx[i][j] = _mm_slli_epi16(((__m128i*)buff[i])[j],4);
173 174
#endif
#elif defined(__arm__)
175
      buff_tx[i][j] = vshlq_n_s16(((int16x8_t*)buff[i])[j],4);
176
#endif
laurent's avatar
laurent committed
177
    }
178
  }
laurent's avatar
laurent committed
179

180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
  s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
  s->tx_md.has_time_spec = flags;
  
  
  if(flags>0)
    s->tx_md.has_time_spec = true;
  else
    s->tx_md.has_time_spec = false;
  
  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
    s->tx_md.start_of_burst = true;
    s->tx_md.end_of_burst = true;
  } else if (flags==1) { // middle of burst
    s->tx_md.start_of_burst = false;
    s->tx_md.end_of_burst = false;
  }
  
  if (cc>1) {
    std::vector<void *> buff_ptrs;
    for (int i=0; i<cc; i++)
      buff_ptrs.push_back(buff_tx[i]);
    ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md,1e-3);
  } else
    ret = (int)s->tx_stream->send(buff_tx[0], nsamps, s->tx_md,1e-3);
  
  
  
  if (ret != nsamps)
    LOG_E(PHY,"[xmit] tx samples %d != %d\n",ret,nsamps);

  return ret;
knopp's avatar
knopp committed
217 218
}

219 220 221 222 223 224 225 226 227 228 229
/*! \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
230 231 232 233
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
234 235
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
laurent's avatar
laurent committed
236 237
    nsamps2 = (nsamps+7)>>3;
    __m256i buff_tmp[2][nsamps2];
238
#else
laurent's avatar
laurent committed
239 240
    nsamps2 = (nsamps+3)>>2;
    __m128i buff_tmp[2][nsamps2];
241 242
#endif
#elif defined(__arm__)
laurent's avatar
laurent committed
243 244
    nsamps2 = (nsamps+3)>>2;
    int16x8_t buff_tmp[2][nsamps2];
245 246
#endif

laurent's avatar
laurent committed
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265
    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++) {
266 267
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
laurent's avatar
laurent committed
268
                ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
269
#else
laurent's avatar
laurent committed
270
                ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
271 272
#endif
#elif defined(__arm__)
laurent's avatar
laurent committed
273
                ((int16x8_t*)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
274
#endif
laurent's avatar
laurent committed
275 276 277 278 279 280 281 282 283 284 285 286 287
            }
        }
    } else if (device->type == USRP_X300_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[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);
        }
288
    }
laurent's avatar
laurent committed
289 290
    if (samples_received < nsamps)
        LOG_E(PHY,"[recv] received %d samples out of %d\n",samples_received,nsamps);
291

laurent's avatar
laurent committed
292
    if ( s->rx_md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
Cedric Roux's avatar
Cedric Roux committed
293
        LOG_E(PHY, "%s\n", s->rx_md.to_pp_string(true).c_str());
294

laurent's avatar
laurent committed
295 296 297 298
    s->rx_count += nsamps;
    s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
    *ptimestamp = s->rx_timestamp;
    return samples_received;
knopp's avatar
knopp committed
299 300
}

301 302 303 304
/*! \brief Compares two variables within precision
 * \param a first variable
 * \param b second variable
*/
laurent's avatar
laurent committed
305
static bool is_equal(double a, double b) {
laurent's avatar
laurent committed
306
    return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
knopp's avatar
knopp committed
307
}
knopp's avatar
knopp committed
308

309
void *freq_thread(void *arg) {
laurent's avatar
laurent committed
310 311 312 313 314 315

    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]);
316 317
}
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
318 319 320
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \param dummy dummy variable not used
laurent's avatar
laurent committed
321
 * \returns 0 in success
322
 */
323
int trx_usrp_set_freq(openair0_device* device, openair0_config_t *openair0_cfg, int dont_block) {
knopp's avatar
knopp committed
324

laurent's avatar
laurent committed
325 326
    usrp_state_t *s = (usrp_state_t*)device->priv;
    pthread_t f_thread;
knopp's avatar
knopp committed
327

laurent's avatar
laurent committed
328
    printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);
329

laurent's avatar
laurent committed
330 331 332 333 334 335 336
    // 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
337

laurent's avatar
laurent committed
338
    return(0);
knopp's avatar
knopp committed
339 340 341

}

laurent's avatar
laurent committed
342
/*! \brief Set RX frequencies
343 344
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
laurent's avatar
laurent committed
345
 * \returns 0 in success
346
 */
knopp's avatar
knopp committed
347 348
int openair0_set_rx_frequencies(openair0_device* device, openair0_config_t *openair0_cfg) {

laurent's avatar
laurent committed
349 350 351
    usrp_state_t *s = (usrp_state_t*)device->priv;
    static int first_call=1;
    static double rf_freq,diff;
knopp's avatar
knopp committed
352

laurent's avatar
laurent committed
353
    uhd::tune_request_t rx_tune_req(openair0_cfg[0].rx_freq[0]);
knopp's avatar
knopp committed
354

laurent's avatar
laurent committed
355 356 357 358 359 360
    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);
knopp's avatar
knopp committed
361 362 363

}

364 365 366
/*! \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
367
 * \returns 0 in success
368
 */
laurent's avatar
laurent committed
369 370 371 372
int trx_usrp_set_gains(openair0_device* device,
                       openair0_config_t *openair0_cfg) {

    usrp_state_t *s = (usrp_state_t*)device->priv;
373 374
    ::uhd::gain_range_t gain_range_tx = s->usrp->get_tx_gain_range(0);
    s->usrp->set_tx_gain(gain_range_tx.stop()-openair0_cfg[0].tx_gain[0]);
laurent's avatar
laurent committed
375 376 377 378 379 380 381 382 383 384 385
    ::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());
386

laurent's avatar
laurent committed
387
    return(0);
knopp's avatar
knopp committed
388
}
389

390 391 392
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
393
int trx_usrp_stop(openair0_device* device) {
laurent's avatar
laurent committed
394
    return(0);
395
}
396

397
/*! \brief USRPB210 RX calibration table */
398
rx_gain_calib_table_t calib_table_b210[] = {
laurent's avatar
laurent committed
399 400 401 402 403 404 405
    {3500000000.0,44.0},
    {2660000000.0,49.0},
    {2300000000.0,50.0},
    {1880000000.0,53.0},
    {816000000.0,58.0},
    {-1,0}
};
406

407
/*! \brief USRPB210 RX calibration table */
408
rx_gain_calib_table_t calib_table_b210_38[] = {
laurent's avatar
laurent committed
409 410 411 412 413 414 415
    {3500000000.0,44.0},
    {2660000000.0,49.8},
    {2300000000.0,51.0},
    {1880000000.0,53.0},
    {816000000.0,57.0},
    {-1,0}
};
416

417
/*! \brief USRPx310 RX calibration table */
418
rx_gain_calib_table_t calib_table_x310[] = {
laurent's avatar
laurent committed
419 420 421 422 423 424 425 426 427
    {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
428 429
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
laurent's avatar
laurent committed
430
 * \returns 0 in success
431
 */
432
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
433

laurent's avatar
laurent committed
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
    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;
        }
461
    }
laurent's avatar
laurent committed
462 463 464 465 466
    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,
laurent's avatar
laurent committed
467 468 469 470 471 472
              openair0_cfg->rx_gain_calib_table[i].offset,diff);
        if (min_diff > diff) {
            min_diff = diff;
            openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
        }
        i++;
laurent's avatar
laurent committed
473
    }
474 475
}

laurent's avatar
laurent committed
476
/*! \brief print the USRP statistics
477 478 479
* \param device the hardware to use
* \returns  0 on success
*/
480
int trx_usrp_get_stats(openair0_device* device) {
laurent's avatar
laurent committed
481
    return(0);
482
}
483

laurent's avatar
laurent committed
484 485 486 487
/*! \brief Reset the USRP statistics
 * \param device the hardware to use
 * \returns  0 on success
 */
488
int trx_usrp_reset_stats(openair0_device* device) {
laurent's avatar
laurent committed
489
    return(0);
490
}
491

492
extern "C" {
laurent's avatar
laurent committed
493 494 495 496 497
    /*! \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) {
498
        uhd::set_thread_priority_safe(1.0);
laurent's avatar
laurent committed
499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
        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";
516

laurent's avatar
laurent committed
517 518 519
            // 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
520
//    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
laurent's avatar
laurent committed
521

Thomas Laurent's avatar
Thomas Laurent committed
522 523
            //    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);
524

Thomas Laurent's avatar
Thomas Laurent committed
525 526 527 528 529 530 531 532
            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
laurent's avatar
laurent committed
533 534 535 536
            if (openair0_cfg[0].clock_source == internal)
                s->usrp->set_clock_source("internal");
            else
                s->usrp->set_clock_source("external");
537

Thomas Laurent's avatar
Thomas Laurent committed
538 539
            //Setting device type to USRP X300/X310
            device->type=USRP_X300_DEV;
540

Thomas Laurent's avatar
Thomas Laurent committed
541 542 543 544 545 546
            // 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;

547 548
            LOG_I(PHY,"%s() sample_rate:%u\n", __FUNCTION__, (int)openair0_cfg[0].sample_rate);

Thomas Laurent's avatar
Thomas Laurent committed
549 550
            switch ((int)openair0_cfg[0].sample_rate) {
            case 30720000:
laurent's avatar
laurent committed
551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
                // from usrp_time_offset
                //openair0_cfg[0].samples_per_packet    = 2048;
                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].samples_per_packet    = 2048;
                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].samples_per_packet    = 2048;
                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].samples_per_packet    = 2048;
                openair0_cfg[0].tx_sample_advance     = 50;
                openair0_cfg[0].tx_bw                 = 1.25e6;
                openair0_cfg[0].rx_bw                 = 1.25e6;
Thomas Laurent's avatar
Thomas Laurent committed
574 575 576 577 578 579
                break;
            default:
                LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
                exit(-1);
                break;
            }
580

Thomas Laurent's avatar
Thomas Laurent committed
581 582 583 584
        } 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);
585

laurent's avatar
laurent committed
586 587 588 589 590 591 592 593 594 595 596 597 598
            //  s->usrp->set_rx_subdev_spec(rx_subdev);
            //  s->usrp->set_tx_subdev_spec(tx_subdev);

            // do not explicitly set the clock to "internal", because this will disable the gpsdo
            //    // lock mboard clocks
            //    s->usrp->set_clock_source("internal");
            // set master clock rate and sample rate for tx & rx for streaming

            // lock mboard clocks
            if (openair0_cfg[0].clock_source == internal)
                s->usrp->set_clock_source("internal");
            else
                s->usrp->set_clock_source("external");
599

Thomas Laurent's avatar
Thomas Laurent committed
600 601 602 603 604 605 606 607
            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;
            }
608

laurent's avatar
laurent committed
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
            switch ((int)openair0_cfg[0].sample_rate) {
            case 30720000:
                s->usrp->set_master_clock_rate(30.72e6);
                //openair0_cfg[0].samples_per_packet    = 1024;
                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].samples_per_packet    = 1024;
                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].samples_per_packet    = 1024;
                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].samples_per_packet    = 1024;
                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].samples_per_packet    = 1024;
                openair0_cfg[0].tx_sample_advance     = 40;
                openair0_cfg[0].tx_bw                 = 20e6;
                openair0_cfg[0].rx_bw                 = 20e6;
                break;
            default:
laurent's avatar
laurent committed
646 647 648 649 650
                LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
                exit(-1);
                break;
            }
        }
Thomas Laurent's avatar
Thomas Laurent committed
651

laurent's avatar
laurent committed
652 653 654
        /* 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
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674
        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());
            }
        }
675

laurent's avatar
laurent committed
676
        for(int i=0; i<s->usrp->get_tx_num_channels(); i++) {
677
	  ::uhd::gain_range_t gain_range_tx = s->usrp->get_tx_gain_range(i);
laurent's avatar
laurent committed
678 679 680
            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);
681
                s->usrp->set_tx_gain(gain_range_tx.stop()-openair0_cfg[0].tx_gain[i],i);
682 683

                LOG_I(PHY,"USRP TX_GAIN:%3.2lf gain_range:%3.2lf tx_gain:%3.2lf\n", gain_range_tx.stop()-openair0_cfg[0].tx_gain[i], gain_range_tx.stop(), openair0_cfg[0].tx_gain[i]);
laurent's avatar
laurent committed
684 685 686 687 688 689 690 691
            }
        }

        //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);
laurent's avatar
laurent committed
692
        sleep(1);
laurent's avatar
laurent committed
693 694 695 696

        // create tx & rx streamer
        uhd::stream_args_t stream_args_rx("sc16", "sc16");
        int samples=openair0_cfg[0].sample_rate;
697 698 699 700 701
	int max=s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps();
        samples/=10000;
	LOG_I(PHY,"RF board max packet size %u, size for 100µs jitter %d \n", max, samples);
	if ( samples < max )
	  stream_args_rx.args["spp"] = str(boost::format("%d") % samples );
Cedric Roux's avatar
Cedric Roux committed
702
	LOG_I(PHY,"rx_max_num_samps %zu\n",
703 704
	      s->usrp->get_rx_stream(stream_args_rx)->get_max_num_samps());

laurent's avatar
laurent committed
705 706 707
        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);
708
	
laurent's avatar
laurent committed
709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
        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;
laurent's avatar
laurent committed
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
        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;
        device->openair0_cfg = openair0_cfg;

        s->sample_rate = openair0_cfg[0].sample_rate;
        // 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;
    }
knopp's avatar
knopp committed
766
}
767
/*@}*/