usrp_lib.cpp 52.2 KB
Newer Older
1 2 3 4 5
/*
 * 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
Cedric Roux's avatar
Cedric Roux committed
6
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
 * 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
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
Florian Kaltenberger's avatar
Florian Kaltenberger committed
36 37
#include <boost/thread.hpp>
#include <boost/format.hpp>
38 39 40 41
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
42
#include <time.h>
43
#include "common/utils/LOG/log.h"
44
#include "common_lib.h"
laurent's avatar
laurent committed
45
#include "assertions.h"
Mongazon's avatar
Mongazon committed
46 47
#include <sys/sysinfo.h>
#include <sys/resource.h>
laurent's avatar
laurent committed
48

49 50 51
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
laurent's avatar
laurent committed
52

53 54 55
#ifdef __AVX2__
#  include <immintrin.h>
#endif
56

57 58 59 60
#ifdef __arm__
#  include <arm_neon.h>
#endif

61 62 63 64
/** @addtogroup _USRP_PHY_RF_INTERFACE_
 * @{
 */

laurent's avatar
laurent committed
65 66
/*! \brief USRP Configuration */
typedef struct {
67

Florian Kaltenberger's avatar
Florian Kaltenberger committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
  // --------------------------------
  // variables for USRP configuration
  // --------------------------------
  //! USRP device pointer
  uhd::usrp::multi_usrp::sptr usrp;

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

  //! Sampling rate
  double sample_rate;

  //! TX forward samples. We use usrp_time_offset to get this value
  int tx_forward_nsamps; //166 for 20Mhz

  // --------------------------------
  // Debug and output control
  // --------------------------------
  int num_underflows;
  int num_overflows;
  int num_seq_errors;
  int64_t tx_count;
  int64_t rx_count;
  int wait_for_first_pps;
  int use_gps;
  //! timestamp of RX packet
  openair0_timestamp rx_timestamp;
103

laurent's avatar
laurent committed
104
} usrp_state_t;
105

Florian Kaltenberger's avatar
Florian Kaltenberger committed
106 107
//void print_notes(void)
//{
Florian Kaltenberger's avatar
Florian Kaltenberger committed
108 109 110 111 112 113
// Helpful notes
//  std::cout << boost::format("**************************************Helpful Notes on Clock/PPS Selection**************************************\n");
//  std::cout << boost::format("As you can see, the default 10 MHz Reference and 1 PPS signals are now from the GPSDO.\n");
//  std::cout << boost::format("If you would like to use the internal reference(TCXO) in other applications, you must configure that explicitly.\n");
//  std::cout << boost::format("You can no longer select the external SMAs for 10 MHz or 1 PPS signaling.\n");
//  std::cout << boost::format("****************************************************************************************************************\n");
Florian Kaltenberger's avatar
Florian Kaltenberger committed
114 115
//}

Florian Kaltenberger's avatar
Florian Kaltenberger committed
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
static int sync_to_gps(openair0_device *device) {
  uhd::set_thread_priority_safe();
  //std::string args;
  //Set up program options
  //po::options_description desc("Allowed options");
  //desc.add_options()
  //("help", "help message")
  //("args", po::value<std::string>(&args)->default_value(""), "USRP device arguments")
  //;
  //po::variables_map vm;
  //po::store(po::parse_command_line(argc, argv, desc), vm);
  //po::notify(vm);
  //Print the help message
  //if (vm.count("help"))
  //{
  //  std::cout << boost::format("Synchronize USRP to GPS %s") % desc << std::endl;
  // return EXIT_FAILURE;
  //}
  //Create a USRP device
  //std::cout << boost::format("\nCreating the USRP device with: %s...\n") % args;
  //uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
  //std::cout << boost::format("Using Device: %s\n") % usrp->get_pp_string();
  usrp_state_t *s = (usrp_state_t *)device->priv;

  try {
    size_t num_mboards = s->usrp->get_num_mboards();
    size_t num_gps_locked = 0;

    for (size_t mboard = 0; mboard < num_mboards; mboard++) {
      std::cout << "Synchronizing mboard " << mboard << ": " << s->usrp->get_mboard_name(mboard) << std::endl;
      //Set references to GPSDO
      s->usrp->set_clock_source("gpsdo", mboard);
      s->usrp->set_time_source("gpsdo", mboard);
      //std::cout << std::endl;
      //print_notes();
      //std::cout << std::endl;
      //Check for 10 MHz lock
      std::vector<std::string> sensor_names = s->usrp->get_mboard_sensor_names(mboard);

      if(std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) {
        std::cout << "Waiting for reference lock..." << std::flush;
        bool ref_locked = false;

        for (int i = 0; i < 30 and not ref_locked; i++) {
          ref_locked = s->usrp->get_mboard_sensor("ref_locked", mboard).to_bool();

          if (not ref_locked) {
            std::cout << "." << std::flush;
            boost::this_thread::sleep(boost::posix_time::seconds(1));
          }
        }
167

Florian Kaltenberger's avatar
Florian Kaltenberger committed
168 169 170 171 172 173
        if(ref_locked) {
          std::cout << "LOCKED" << std::endl;
        } else {
          std::cout << "FAILED" << std::endl;
          std::cout << "Failed to lock to GPSDO 10 MHz Reference. Exiting." << std::endl;
          exit(EXIT_FAILURE);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
174
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
175 176 177
      } else {
        std::cout << boost::format("ref_locked sensor not present on this board.\n");
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
178

Florian Kaltenberger's avatar
Florian Kaltenberger committed
179 180
      //Wait for GPS lock
      bool gps_locked = s->usrp->get_mboard_sensor("gps_locked", mboard).to_bool();
Florian Kaltenberger's avatar
Florian Kaltenberger committed
181

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
      if(gps_locked) {
        num_gps_locked++;
        std::cout << boost::format("GPS Locked\n");
      } else {
        std::cerr << "WARNING:  GPS not locked - time will not be accurate until locked" << std::endl;
      }

      //Set to GPS time
      uhd::time_spec_t gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
      //s->usrp->set_time_next_pps(gps_time+1.0, mboard);
      s->usrp->set_time_next_pps(uhd::time_spec_t(0.0));
      //Wait for it to apply
      //The wait is 2 seconds because N-Series has a known issue where
      //the time at the last PPS does not properly update at the PPS edge
      //when the time is actually set.
      boost::this_thread::sleep(boost::posix_time::seconds(2));
      //Check times
      gps_time = uhd::time_spec_t(time_t(s->usrp->get_mboard_sensor("gps_time", mboard).to_int()));
      uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps(mboard);
      std::cout << "USRP time: " << (boost::format("%0.9f") % time_last_pps.get_real_secs()) << std::endl;
      std::cout << "GPSDO time: " << (boost::format("%0.9f") % gps_time.get_real_secs()) << std::endl;
      //if (gps_time.get_real_secs() == time_last_pps.get_real_secs())
      //    std::cout << std::endl << "SUCCESS: USRP time synchronized to GPS time" << std::endl << std::endl;
      //else
      //    std::cerr << std::endl << "ERROR: Failed to synchronize USRP time to GPS time" << std::endl << std::endl;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
207
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240

    if (num_gps_locked == num_mboards and num_mboards > 1) {
      //Check to see if all USRP times are aligned
      //First, wait for PPS.
      uhd::time_spec_t time_last_pps = s->usrp->get_time_last_pps();

      while (time_last_pps == s->usrp->get_time_last_pps()) {
        boost::this_thread::sleep(boost::posix_time::milliseconds(1));
      }

      //Sleep a little to make sure all devices have seen a PPS edge
      boost::this_thread::sleep(boost::posix_time::milliseconds(200));
      //Compare times across all mboards
      bool all_matched = true;
      uhd::time_spec_t mboard0_time = s->usrp->get_time_last_pps(0);

      for (size_t mboard = 1; mboard < num_mboards; mboard++) {
        uhd::time_spec_t mboard_time = s->usrp->get_time_last_pps(mboard);

        if (mboard_time != mboard0_time) {
          all_matched = false;
          std::cerr << (boost::format("ERROR: Times are not aligned: USRP 0=%0.9f, USRP %d=%0.9f")
                        % mboard0_time.get_real_secs()
                        % mboard
                        % mboard_time.get_real_secs()) << std::endl;
        }
      }

      if (all_matched) {
        std::cout << "SUCCESS: USRP times aligned" << std::endl << std::endl;
      } else {
        std::cout << "ERROR: USRP times are not aligned" << std::endl << std::endl;
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
241
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
242 243 244 245 246 247 248 249 250
  } catch (std::exception &e) {
    std::cout << boost::format("\nError: %s") % e.what();
    std::cout << boost::format("This could mean that you have not installed the GPSDO correctly.\n\n");
    std::cout << boost::format("Visit one of these pages if the problem persists:\n");
    std::cout << boost::format(" * N2X0/E1X0: http://files.ettus.com/manual/page_gpsdo.html");
    std::cout << boost::format(" * X3X0: http://files.ettus.com/manual/page_gpsdo_x3x0.html\n\n");
    std::cout << boost::format(" * E3X0: http://files.ettus.com/manual/page_usrp_e3x0.html#e3x0_hw_gps\n\n");
    exit(EXIT_FAILURE);
  }
251

Florian Kaltenberger's avatar
Florian Kaltenberger committed
252
  return EXIT_SUCCESS;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
253
}
254

255 256 257 258
#if defined(USRP_REC_PLAY)
#include "usrp_lib.h"
static FILE    *pFile = NULL;
int             mmapfd = 0;
Mongazon's avatar
Mongazon committed
259 260
int             iqfd = 0;
int             use_mmap = 1; // default is to use mmap
261 262 263 264 265 266 267 268 269
struct stat     sb;
iqrec_t        *ms_sample = NULL;                      // memory for all subframes
unsigned int    nb_samples = 0;
unsigned int    cur_samples = 0;
int64_t         wrap_count = 0;
int64_t         wrap_ts = 0;
unsigned int    u_sf_mode = 0;                         // 1=record, 2=replay
unsigned int    u_sf_record = 0;                       // record mode
unsigned int    u_sf_replay = 0;                       // replay mode
Mongazon's avatar
Mongazon committed
270
char            u_sf_filename[1024] = "";              // subframes file path
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
unsigned int    u_sf_max = DEF_NB_SF;                  // max number of recorded subframes
unsigned int    u_sf_loops = DEF_SF_NB_LOOP;           // number of loops in replay mode
unsigned int    u_sf_read_delay = DEF_SF_DELAY_READ;   // read delay in replay mode
unsigned int    u_sf_write_delay = DEF_SF_DELAY_WRITE; // write delay in replay mode

char config_opt_sf_file[] = CONFIG_OPT_SF_FILE;
char config_def_sf_file[] = DEF_SF_FILE;
char config_hlp_sf_file[] = CONFIG_HLP_SF_FILE;
char config_opt_sf_rec[] = CONFIG_OPT_SF_REC;
char config_hlp_sf_rec[] = CONFIG_HLP_SF_REC;
char config_opt_sf_rep[] = CONFIG_OPT_SF_REP;
char config_hlp_sf_rep[] = CONFIG_HLP_SF_REP;
char config_opt_sf_max[] = CONFIG_OPT_SF_MAX;
char config_hlp_sf_max[] = CONFIG_HLP_SF_MAX;
char config_opt_sf_loops[] = CONFIG_OPT_SF_LOOPS;
char config_hlp_sf_loops[] = CONFIG_HLP_SF_LOOPS;
char config_opt_sf_rdelay[] = CONFIG_OPT_SF_RDELAY;
char config_hlp_sf_rdelay[] = CONFIG_HLP_SF_RDELAY;
char config_opt_sf_wdelay[] = CONFIG_OPT_SF_WDELAY;
char config_hlp_sf_wdelay[] = CONFIG_HLP_SF_WDELAY;

#endif

294 295 296
/*! \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
297
static int trx_usrp_start(openair0_device *device) {
298
#if defined(USRP_REC_PLAY)
299

Florian Kaltenberger's avatar
Florian Kaltenberger committed
300 301 302 303 304
  if (u_sf_mode != 2) { // not replay mode
#endif
    usrp_state_t *s = (usrp_state_t *)device->priv;
    // setup GPIO for TDD, GPIO(4) = ATR_RX
    //set data direction register (DDR) to output
305
    s->usrp->set_gpio_attr("FP0", "DDR", 0x1f, 0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
306
    //set control register to ATR
307
    s->usrp->set_gpio_attr("FP0", "CTRL", 0x1f,0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
308
    //set ATR register
309
    s->usrp->set_gpio_attr("FP0", "ATR_RX", 1<<4, 0x1f);
laurent's avatar
laurent committed
310 311
    // init recv and send streaming
    uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
312 313
    LOG_I(PHY,"Time in secs now: %llu \n", s->usrp->get_time_now().to_ticks(s->sample_rate));
    LOG_I(PHY,"Time in secs last pps: %llu \n", s->usrp->get_time_last_pps().to_ticks(s->sample_rate));
314 315 316

    if (s->use_gps == 1) {
      s->wait_for_first_pps = 1;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
317 318 319
      cmd.time_spec = s->usrp->get_time_last_pps() + uhd::time_spec_t(1.0);
    } else {
      s->wait_for_first_pps = 0;
320 321 322
      cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
    }

laurent's avatar
laurent committed
323
    cmd.stream_now = false; // start at constant delay
laurent's avatar
laurent committed
324 325 326 327 328 329 330 331
    s->rx_stream->issue_stream_cmd(cmd);
    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;
    s->rx_count = 0;
    s->tx_count = 0;
    s->rx_timestamp = 0;
332
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
333 334 335 336
  }

#endif
  return 0;
337
}
laurent's avatar
laurent committed
338
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
339 340
 * \param device the hardware to use
 */
laurent's avatar
laurent committed
341
static void trx_usrp_end(openair0_device *device) {
342 343
#if defined(USRP_REC_PLAY) // For some ugly reason, this can be called several times...
  static int done = 0;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
344

345
  if (done == 1) return;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
346

347
  done = 1;
348

Florian Kaltenberger's avatar
Florian Kaltenberger committed
349 350 351
  if (u_sf_mode != 2) { // not subframes replay
#endif
    usrp_state_t *s = (usrp_state_t *)device->priv;
laurent's avatar
laurent committed
352 353 354 355 356
    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;
357
    sleep(1);
358
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
359 360
  }

361 362
#endif
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380

  if (u_sf_mode == 1) { // subframes store
    pFile = fopen (u_sf_filename,"wb+");

    if (pFile == NULL) {
      std::cerr << "Cannot open " << u_sf_filename << std::endl;
    } else {
      unsigned int i = 0;
      unsigned int modu = 0;

      if ((modu = nb_samples % 10) != 0) {
        nb_samples -= modu; // store entire number of frames
      }

      std::cerr << "Writing " << nb_samples << " subframes to " << u_sf_filename << " ..." << std::endl;

      for (i = 0; i < nb_samples; i++) {
        fwrite(ms_sample+i, sizeof(unsigned char), sizeof(iqrec_t), pFile);
381
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
382 383 384

      fclose (pFile);
      std::cerr << "File " << u_sf_filename << " closed." << std::endl;
385
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
  }

  if (u_sf_mode == 1) { // record
    if (ms_sample != NULL) {
      free((void *)ms_sample);
      ms_sample = NULL;
    }
  }

  if (u_sf_mode == 2) { // replay
    if (use_mmap) {
      if (ms_sample != MAP_FAILED) {
        munmap(ms_sample, sb.st_size);
        ms_sample = NULL;
      }

      if (mmapfd != 0) {
        close(mmapfd);
        mmapfd = 0;
      }
    } else {
407
      if (ms_sample != NULL) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
408 409
        free(ms_sample);
        ms_sample = NULL;
410
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
411 412 413 414

      if (iqfd != 0) {
        close(iqfd);
        iqfd = 0;
415 416
      }
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
417 418 419
  }

#endif
420
}
421

422 423
/*! \brief Called to send samples to the USRP RF target
      @param device pointer to the device structure specific to the RF hardware target
424
      @param timestamp The timestamp at which the first sample MUST be sent
425 426
      @param buff Buffer which holds the samples
      @param nsamps number of samples to be sent
427
      @param antenna_id index of the antenna if the device has multiple antennas
428
      @param flags flags must be set to TRUE if timestamp parameter needs to be applied
laurent's avatar
laurent committed
429 430
*/
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags) {
431
  int ret=0;
432
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
433

434
  if (u_sf_mode != 2) { // not replay mode
Florian Kaltenberger's avatar
Florian Kaltenberger committed
435 436 437
#endif
    usrp_state_t *s = (usrp_state_t *)device->priv;
    int nsamps2;  // aligned to upper 32 or 16 byte boundary
438 439
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
Florian Kaltenberger's avatar
Florian Kaltenberger committed
440 441
    nsamps2 = (nsamps+7)>>3;
    __m256i buff_tx[2][nsamps2];
442
#else
Florian Kaltenberger's avatar
Florian Kaltenberger committed
443 444
    nsamps2 = (nsamps+3)>>2;
    __m128i buff_tx[2][nsamps2];
445 446
#endif
#elif defined(__arm__)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
447 448
    nsamps2 = (nsamps+3)>>2;
    int16x8_t buff_tx[2][nsamps2];
449
#endif
Florian Kaltenberger's avatar
Florian Kaltenberger committed
450 451 452 453

    // bring RX data into 12 LSBs for softmodem RX
    for (int i=0; i<cc; i++) {
      for (int j=0; j<nsamps2; j++) {
454 455
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
Florian Kaltenberger's avatar
Florian Kaltenberger committed
456
        buff_tx[i][j] = _mm256_slli_epi16(((__m256i *)buff[i])[j],4);
457
#else
Florian Kaltenberger's avatar
Florian Kaltenberger committed
458
        buff_tx[i][j] = _mm_slli_epi16(((__m128i *)buff[i])[j],4);
459 460
#endif
#elif defined(__arm__)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
461
        buff_tx[i][j] = vshlq_n_s16(((int16x8_t *)buff[i])[j],4);
462
#endif
Florian Kaltenberger's avatar
Florian Kaltenberger committed
463
      }
laurent's avatar
laurent committed
464 465
    }

Florian Kaltenberger's avatar
Florian Kaltenberger committed
466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
    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(flags==10) { // fail safe mode
      s->tx_md.has_time_spec = false;
      s->tx_md.start_of_burst = false;
      s->tx_md.end_of_burst = true;
    }

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

507 508 509 510 511 512 513 514
#if defined(USRP_REC_PLAY)
  } else {
    struct timespec req;
    req.tv_sec = 0;
    req.tv_nsec = u_sf_write_delay * 1000;
    nanosleep(&req, NULL);
    ret = nsamps;
  }
515

Florian Kaltenberger's avatar
Florian Kaltenberger committed
516
#endif
517
  return ret;
518 519
}

520 521 522 523 524 525 526 527 528 529 530
/*! \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
531
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
532
  usrp_state_t *s = (usrp_state_t *)device->priv;
533 534 535
  int samples_received=0,i,j;
  int nsamps2;  // aligned to upper 32 or 16 byte boundary
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
536

537
  if (u_sf_mode != 2) { // not replay mode
Florian Kaltenberger's avatar
Florian Kaltenberger committed
538
#endif
539 540
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
laurent's avatar
laurent committed
541 542
    nsamps2 = (nsamps+7)>>3;
    __m256i buff_tmp[2][nsamps2];
543
#else
laurent's avatar
laurent committed
544 545
    nsamps2 = (nsamps+3)>>2;
    __m128i buff_tmp[2][nsamps2];
546 547
#endif
#elif defined(__arm__)
laurent's avatar
laurent committed
548 549
    nsamps2 = (nsamps+3)>>2;
    int16x8_t buff_tmp[2][nsamps2];
550 551
#endif

laurent's avatar
laurent committed
552
    if (device->type == USRP_B200_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
      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->wait_for_first_pps == 0) && (s->rx_md.error_code!=uhd::rx_metadata_t::ERROR_CODE_NONE))
            break;

          if ((s->wait_for_first_pps == 1) && (samples_received != nsamps)) {
            printf("sleep...\n"); //usleep(100);
          }
laurent's avatar
laurent committed
574
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
575 576 577 578 579 580 581

        if (samples_received == nsamps) s->wait_for_first_pps=0;
      }

      // bring RX data into 12 LSBs for softmodem RX
      for (int i=0; i<cc; i++) {
        for (int j=0; j<nsamps2; j++) {
582 583
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
Florian Kaltenberger's avatar
Florian Kaltenberger committed
584
          ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
585
#else
Florian Kaltenberger's avatar
Florian Kaltenberger committed
586
          ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
587 588
#endif
#elif defined(__arm__)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
589
          ((int16x8_t *)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
590
#endif
laurent's avatar
laurent committed
591
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
592
      }
laurent's avatar
laurent committed
593
    } else if (device->type == USRP_X300_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
594 595 596
      if (cc>1) {
        // receive multiple channels (e.g. RF A and RF B)
        std::vector<void *> buff_ptrs;
laurent's avatar
laurent committed
597

Florian Kaltenberger's avatar
Florian Kaltenberger committed
598 599 600 601 602 603 604
        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);
      }
605
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
606

laurent's avatar
laurent committed
607
    if (samples_received < nsamps)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
608
      LOG_E(PHY,"[recv] received %d samples out of %d\n",samples_received,nsamps);
609

laurent's avatar
laurent committed
610
    if ( s->rx_md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
611
      LOG_E(PHY, "%s\n", s->rx_md.to_pp_string(true).c_str());
612

laurent's avatar
laurent committed
613 614 615
    s->rx_count += nsamps;
    s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
    *ptimestamp = s->rx_timestamp;
616 617
#if defined (USRP_REC_PLAY)
  }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
618 619

#endif
620
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
621

622 623 624
  if (u_sf_mode == 1) { // record mode
    // Copy subframes to memory (later dump on a file)
    if (nb_samples < u_sf_max) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
625
      (ms_sample+nb_samples)->header = BELL_LABS_IQ_HEADER;
626 627 628 629 630 631 632 633
      (ms_sample+nb_samples)->ts = *ptimestamp;
      memcpy((ms_sample+nb_samples)->samples, buff[0], nsamps*4);
      nb_samples++;
    }
  } else if (u_sf_mode == 2) { // replay mode
    if (cur_samples == nb_samples) {
      cur_samples = 0;
      wrap_count++;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
634

635
      if (wrap_count == u_sf_loops) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
636 637
        std::cerr << "USRP device terminating subframes replay mode after " << u_sf_loops << " loops." << std::endl;
        return 0; // should make calling process exit
638
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
639

640
      wrap_ts = wrap_count * (nb_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000));
Florian Kaltenberger's avatar
Florian Kaltenberger committed
641

Mongazon's avatar
Mongazon committed
642
      if (!use_mmap) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
643 644 645 646 647
        if (lseek(iqfd, 0, SEEK_SET) == 0) {
          std::cerr << "Seeking at the beginning of IQ file" << std::endl;
        } else {
          std::cerr << "Problem seeking at the beginning of IQ file" << std::endl;
        }
Mongazon's avatar
Mongazon committed
648
      }
649
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
650

Mongazon's avatar
Mongazon committed
651 652
    if (use_mmap) {
      if (cur_samples < nb_samples) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
653 654 655 656 657 658 659 660 661
        *ptimestamp = (ms_sample[0].ts + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000))) + wrap_ts;

        if (cur_samples == 0) {
          std::cerr << "starting subframes file with wrap_count=" << wrap_count << " wrap_ts=" << wrap_ts
                    << " ts=" << *ptimestamp << std::endl;
        }

        memcpy(buff[0], &ms_sample[cur_samples].samples[0], nsamps*4);
        cur_samples++;
Mongazon's avatar
Mongazon committed
662 663 664 665
      }
    } else {
      // read sample from file
      if (read(iqfd, ms_sample, sizeof(iqrec_t)) != sizeof(iqrec_t)) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
666 667 668 669 670 671
        std::cerr << "pb reading iqfile at index " << sizeof(iqrec_t)*cur_samples << std::endl;
        close(iqfd);
        free(ms_sample);
        ms_sample = NULL;
        iqfd = 0;
        exit(-1);
Mongazon's avatar
Mongazon committed
672 673 674
      }

      if (cur_samples < nb_samples) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
        static int64_t ts0 = 0;

        if ((cur_samples == 0) && (wrap_count == 0)) {
          ts0 = ms_sample->ts;
        }

        *ptimestamp = ts0 + (cur_samples * (((int)(device->openair0_cfg[0].sample_rate)) / 1000)) + wrap_ts;

        if (cur_samples == 0) {
          std::cerr << "starting subframes file with wrap_count=" << wrap_count << " wrap_ts=" << wrap_ts
                    << " ts=" << *ptimestamp << std::endl;
        }

        memcpy(buff[0], &ms_sample->samples[0], nsamps*4);
        cur_samples++;
        // Prepare for next read
        off_t where = lseek(iqfd, cur_samples * sizeof(iqrec_t), SEEK_SET);
692 693
      }
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
694

695
    struct timespec req;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
696

697
    req.tv_sec = 0;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
698

699
    req.tv_nsec = u_sf_read_delay * 1000;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
700

701
    nanosleep(&req, NULL);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
702

703 704
    return nsamps;
  }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
705

706 707
#endif
  return samples_received;
708 709
}

710 711 712 713
/*! \brief Compares two variables within precision
 * \param a first variable
 * \param b second variable
*/
laurent's avatar
laurent committed
714
static bool is_equal(double a, double b) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
715
  return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
716
}
717

718
void *freq_thread(void *arg) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
719 720 721 722
  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]);
723 724
}
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
725 726 727
 * \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
728
 * \returns 0 in success
729
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
730 731 732 733 734 735 736 737 738 739 740 741
int trx_usrp_set_freq(openair0_device *device, openair0_config_t *openair0_cfg, int dont_block) {
  usrp_state_t *s = (usrp_state_t *)device->priv;
  pthread_t f_thread;
  printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);

  // 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]);
  }
742

Florian Kaltenberger's avatar
Florian Kaltenberger committed
743
  return(0);
744 745
}

laurent's avatar
laurent committed
746
/*! \brief Set RX frequencies
747 748
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
laurent's avatar
laurent committed
749
 * \returns 0 in success
750
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
751 752 753 754 755 756 757 758 759 760
int openair0_set_rx_frequencies(openair0_device *device, openair0_config_t *openair0_cfg) {
  usrp_state_t *s = (usrp_state_t *)device->priv;
  static int first_call=1;
  static double rf_freq,diff;
  uhd::tune_request_t rx_tune_req(openair0_cfg[0].rx_freq[0]);
  rx_tune_req.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
  rx_tune_req.rf_freq = openair0_cfg[0].rx_freq[0];
  rf_freq=openair0_cfg[0].rx_freq[0];
  s->usrp->set_rx_freq(rx_tune_req);
  return(0);
761 762
}

763 764 765
/*! \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
766
 * \returns 0 in success
767
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
768
int trx_usrp_set_gains(openair0_device *device,
laurent's avatar
laurent committed
769
                       openair0_config_t *openair0_cfg) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
770 771 772 773 774 775 776 777 778 779 780
  usrp_state_t *s = (usrp_state_t *)device->priv;
  ::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]);
  ::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);
  }
laurent's avatar
laurent committed
781

Florian Kaltenberger's avatar
Florian Kaltenberger committed
782 783 784 785 786
  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());
  return(0);
787
}
788

789 790 791
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
792 793
int trx_usrp_stop(openair0_device *device) {
  return(0);
794
}
795

796
/*! \brief USRPB210 RX calibration table */
797
rx_gain_calib_table_t calib_table_b210[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
798 799 800 801 802 803
  {3500000000.0,44.0},
  {2660000000.0,49.0},
  {2300000000.0,50.0},
  {1880000000.0,53.0},
  {816000000.0,58.0},
  {-1,0}
laurent's avatar
laurent committed
804
};
805

806
/*! \brief USRPB210 RX calibration table */
807
rx_gain_calib_table_t calib_table_b210_38[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
808 809 810 811 812 813
  {3500000000.0,44.0},
  {2660000000.0,49.8},
  {2300000000.0,51.0},
  {1880000000.0,53.0},
  {816000000.0,57.0},
  {-1,0}
laurent's avatar
laurent committed
814
};
815

816
/*! \brief USRPx310 RX calibration table */
817
rx_gain_calib_table_t calib_table_x310[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
818 819 820 821 822 823
  {3500000000.0,77.0},
  {2660000000.0,81.0},
  {2300000000.0,81.0},
  {1880000000.0,82.0},
  {816000000.0,85.0},
  {-1,0}
laurent's avatar
laurent committed
824 825 826
};

/*! \brief Set RX gain offset
827 828
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
laurent's avatar
laurent committed
829
 * \returns 0 in success
830
 */
831
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
832 833 834
  int i=0;
  // loop through calibration table to find best adjustment factor for RX frequency
  double min_diff = 6e9,diff,gain_adj=0.0;
835

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
  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;
865
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
866 867 868 869 870 871 872 873 874 875 876 877
  }

  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,
          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;
laurent's avatar
laurent committed
878
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
879 880 881

    i++;
  }
882 883
}

laurent's avatar
laurent committed
884
/*! \brief print the USRP statistics
885 886 887
* \param device the hardware to use
* \returns  0 on success
*/
Florian Kaltenberger's avatar
Florian Kaltenberger committed
888 889
int trx_usrp_get_stats(openair0_device *device) {
  return(0);
890
}
891

laurent's avatar
laurent committed
892 893 894 895
/*! \brief Reset the USRP statistics
 * \param device the hardware to use
 * \returns  0 on success
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
896 897
int trx_usrp_reset_stats(openair0_device *device) {
  return(0);
898
}
899

900 901
#if defined(USRP_REC_PLAY)
extern "C" {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
902 903 904 905 906
  /*! \brief Initializer for USRP record/playback config
   * \param parameter array description
   * \returns  0 on success
   */
  int trx_usrp_recplay_config_init(paramdef_t *usrp_recplay_params) {
907 908 909 910
    // --subframes-file
    memcpy(usrp_recplay_params[0].optname, config_opt_sf_file, strlen(config_opt_sf_file));
    usrp_recplay_params[0].helpstr = config_hlp_sf_file;
    usrp_recplay_params[0].paramflags=PARAMFLAG_NOFREE;
Mongazon's avatar
Mongazon committed
911
    usrp_recplay_params[0].strptr=(char **)&u_sf_filename;
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
    usrp_recplay_params[0].defstrval = NULL;
    usrp_recplay_params[0].type=TYPE_STRING;
    usrp_recplay_params[0].numelt=sizeof(u_sf_filename);
    // --subframes-record
    memcpy(usrp_recplay_params[1].optname, config_opt_sf_rec, strlen(config_opt_sf_rec));
    usrp_recplay_params[1].helpstr = config_hlp_sf_rec;
    usrp_recplay_params[1].paramflags=PARAMFLAG_BOOL;
    usrp_recplay_params[1].uptr=&u_sf_record;
    usrp_recplay_params[1].defuintval=0;
    usrp_recplay_params[1].type=TYPE_UINT;
    usrp_recplay_params[1].numelt=0;
    // --subframes-replay
    memcpy(usrp_recplay_params[2].optname, config_opt_sf_rep, strlen(config_opt_sf_rep));
    usrp_recplay_params[2].helpstr = config_hlp_sf_rep;
    usrp_recplay_params[2].paramflags=PARAMFLAG_BOOL;
    usrp_recplay_params[2].uptr=&u_sf_replay;
    usrp_recplay_params[2].defuintval=0;
    usrp_recplay_params[2].type=TYPE_UINT;
    usrp_recplay_params[2].numelt=0;
    // --subframes-max
    memcpy(usrp_recplay_params[3].optname, config_opt_sf_max, strlen(config_opt_sf_max));
    usrp_recplay_params[3].helpstr = config_hlp_sf_max;
    usrp_recplay_params[3].paramflags=0;
    usrp_recplay_params[3].uptr=&u_sf_max;
    usrp_recplay_params[3].defuintval=DEF_NB_SF;
    usrp_recplay_params[3].type=TYPE_UINT;
    usrp_recplay_params[3].numelt=0;
    // --subframes-loops
    memcpy(usrp_recplay_params[4].optname, config_opt_sf_loops, strlen(config_opt_sf_loops));
    usrp_recplay_params[4].helpstr = config_hlp_sf_loops;
    usrp_recplay_params[4].paramflags=0;
    usrp_recplay_params[4].uptr=&u_sf_loops;
    usrp_recplay_params[4].defuintval=DEF_SF_NB_LOOP;
    usrp_recplay_params[4].type=TYPE_UINT;
    usrp_recplay_params[4].numelt=0;
    // --subframes-read-delay
    memcpy(usrp_recplay_params[5].optname, config_opt_sf_rdelay, strlen(config_opt_sf_rdelay));
    usrp_recplay_params[5].helpstr = config_hlp_sf_rdelay;
    usrp_recplay_params[5].paramflags=0;
    usrp_recplay_params[5].uptr=&u_sf_read_delay;
    usrp_recplay_params[5].defuintval=DEF_SF_DELAY_READ;
    usrp_recplay_params[5].type=TYPE_UINT;
    usrp_recplay_params[5].numelt=0;
    // --subframes-write-delay
    memcpy(usrp_recplay_params[6].optname, config_opt_sf_wdelay, strlen(config_opt_sf_wdelay));
    usrp_recplay_params[6].helpstr = config_hlp_sf_wdelay;
    usrp_recplay_params[6].paramflags=0;
    usrp_recplay_params[6].uptr=&u_sf_write_delay;
    usrp_recplay_params[6].defuintval=DEF_SF_DELAY_WRITE;
    usrp_recplay_params[6].type=TYPE_UINT;
    usrp_recplay_params[6].numelt=0;
    return 0; // always ok
Florian Kaltenberger's avatar
Florian Kaltenberger committed
964
  }
965 966 967
}
#endif

968
extern "C" {
969 970 971 972
  /*! \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
   */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
973
  int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
974 975
    LOG_D(PHY, "openair0_cfg[0].sdr_addrs == '%s'\n", openair0_cfg[0].sdr_addrs);
    LOG_D(PHY, "openair0_cfg[0].clock_source == '%d'\n", openair0_cfg[0].clock_source);
976
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
977 978 979 980
    paramdef_t usrp_recplay_params[7];
    struct sysinfo systeminfo;
    // to check
    static int done = 0;
981

Florian Kaltenberger's avatar
Florian Kaltenberger committed
982 983 984
    if (done == 1) {
      return 0;
    } // prevent from multiple init
985

Florian Kaltenberger's avatar
Florian Kaltenberger committed
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
    done = 1;
    // end to check
    // Use mmap for IQ files for systems with less than 6GB total RAM
    sysinfo(&systeminfo);

    if (systeminfo.totalram < 6144000000) {
      use_mmap = 0;
    }

    memset(usrp_recplay_params, 0, 7*sizeof(paramdef_t));
    memset(&u_sf_filename[0], 0, 1024);

    if (trx_usrp_recplay_config_init(usrp_recplay_params) != 0) {
      std::cerr << "USRP device record/replay mode configuration error exiting" << std::endl;
      return -1;
    }

    config_process_cmdline(usrp_recplay_params,sizeof(usrp_recplay_params)/sizeof(paramdef_t),NULL);

    if (strlen(u_sf_filename) == 0) {
      (void) strcpy(u_sf_filename, DEF_SF_FILE);
    }

    if (u_sf_replay == 1) u_sf_mode = 2;

    if (u_sf_record == 1) u_sf_mode = 1;

    if (u_sf_mode == 2) {
      // Replay subframes from from file
      int bw_gain_adjust=0;
      device->openair0_cfg = openair0_cfg;
      device->type = USRP_B200_DEV;
      openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
      bw_gain_adjust=1;
      openair0_cfg[0].tx_sample_advance     = 80;
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
      openair0_cfg[0].iq_txshift = 4;//shift
      openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
      set_rx_gain_offset(&openair0_cfg[0],0,bw_gain_adjust);
      device->priv = NULL;
      device->trx_start_func = trx_usrp_start;
      device->trx_write_func = trx_usrp_write;
      device->trx_read_func  = trx_usrp_read;
      device->trx_get_stats_func = trx_usrp_get_stats;
      device->trx_reset_stats_func = trx_usrp_reset_stats;
      device->trx_end_func   = trx_usrp_end;
      device->trx_stop_func  = trx_usrp_stop;
      device->trx_set_freq_func = trx_usrp_set_freq;
      device->trx_set_gains_func   = trx_usrp_set_gains;
      device->openair0_cfg = openair0_cfg;
      std::cerr << "USRP device initialized in subframes replay mode for " << u_sf_loops << " loops. Use mmap="
                << use_mmap << std::endl;
    } else {
1040
#endif
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1041 1042
      uhd::set_thread_priority_safe(1.0);
      usrp_state_t *s = (usrp_state_t *)calloc(sizeof(usrp_state_t),1);
1043

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1044 1045
      if (openair0_cfg[0].clock_source==gpsdo)
        s->use_gps =1;
laurent's avatar
laurent committed
1046

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1047 1048
      // Initialize USRP device
      device->openair0_cfg = openair0_cfg;
1049 1050
      std::string args = "type=b200";
      char *addr_args = NULL;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1051

1052 1053
      // Check whether sdr_addrs is set in the config or not
      if (openair0_cfg[0].sdr_addrs != NULL) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1054 1055 1056
        if (strcmp(openair0_cfg[0].sdr_addrs, "0.0.0.0") != 0) {
          // Check whether sdr_addrs contains multiple IP addresses
          // and split and add them to addr_args
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067
          if (strstr(openair0_cfg[0].sdr_addrs, ",") != NULL) {
            char *addr0 = openair0_cfg[0].sdr_addrs;
            // Replace , with \0
            strsep(&openair0_cfg[0].sdr_addrs, ",");
            char *addr1 = openair0_cfg[0].sdr_addrs;
            // Allocate memory for ",addr0=,addr1=\0" and the addresses
            size_t addr_args_len = sizeof(char)*(15 + strlen(addr0) + strlen(addr1));
            addr_args = (char *)malloc(addr_args_len);
            snprintf(addr_args, addr_args_len, ",addr0=%s,addr1=%s", addr0, addr1);
            args += addr_args;
            LOG_D(PHY, "addr_args == '%s'\n", addr_args);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1068
          }
1069 1070 1071 1072
        }
      }

      uhd::device_addrs_t device_adds = uhd::device::find(args);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1073 1074 1075
      int vers=0,subvers=0,subsubvers=0;
      int bw_gain_adjust=0;
#if defined(USRP_REC_PLAY)
laurent's avatar
laurent committed
1076

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1077 1078 1079
      if (u_sf_mode == 1) {
        std::cerr << "USRP device initialized in subframes record mode" << std::endl;
      }
laurent's avatar
laurent committed
1080

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1081 1082 1083 1084
#endif
      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);
laurent's avatar
laurent committed
1085

1086 1087 1088
      if(device_adds.size() == 0)  {
        double usrp_master_clock = 184.32e6;
        std::string args = "type=x300";
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1089 1090

        if (addr_args) {
1091 1092
          args += addr_args;
        }
1093

1094 1095
        // 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);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128
        //    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) {
          args += ",addr=192.168.30.2";
          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
        if (openair0_cfg[0].clock_source == internal)
          s->usrp->set_clock_source("internal");
        else
          s->usrp->set_clock_source("external");

        //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;
#if defined(USRP_REC_PLAY)
        std::cerr << "-- Using calibration table: calib_table_x310" << std::endl; // Bell Labs info
#endif
        LOG_I(PHY,"%s() sample_rate:%u\n", __FUNCTION__, (int)openair0_cfg[0].sample_rate);
laurent's avatar
laurent committed
1129

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1130 1131 1132 1133 1134 1135 1136 1137
        switch ((int)openair0_cfg[0].sample_rate) {
          case 122880000:
            // from usrp_time_offset
            //openair0_cfg[0].samples_per_packet    = 2048;
            openair0_cfg[0].tx_sample_advance     = 15; //to be checked
            openair0_cfg[0].tx_bw                 = 80e6;
            openair0_cfg[0].rx_bw                 = 80e6;
            break;
1138

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1139 1140 1141 1142 1143 1144 1145
          case 61440000:
            // from usrp_time_offset
            //openair0_cfg[0].samples_per_packet    = 2048;
            openair0_cfg[0].tx_sample_advance     = 15;
            openair0_cfg[0].tx_bw                 = 40e6;
            openair0_cfg[0].rx_bw                 = 40e6;
            break;
Massive's avatar
Massive committed
1146

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1147 1148 1149 1150 1151 1152 1153
          case 30720000:
            // from usrp_time_offset
            //openair0_cfg[0].samples_per_packet    = 2048;
            openair0_cfg[0].tx_sample_advance     = 15;
            openair0_cfg[0].tx_bw                 = 20e6;
            openair0_cfg[0].rx_bw                 = 20e6;
            break;
Massive's avatar
Massive committed
1154

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1155 1156 1157 1158 1159 1160
          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;
1161

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1162 1163 1164 1165 1166 1167
          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;
1168

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1169 1170 1171 1172 1173 1174
          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;
            break;
Thomas Laurent's avatar
Thomas Laurent committed
1175

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1176 1177 1178 1179 1180 1181 1182 1183 1184
          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);
1185

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1186 1187
        //  s->usrp->set_rx_subdev_spec(rx_subdev);
        //  s->usrp->set_tx_subdev_spec(tx_subdev);
Cedric Roux's avatar
Cedric Roux committed
1188

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1189 1190 1191 1192
        // 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
1193

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1194 1195 1196
        // lock mboard clocks
        if (openair0_cfg[0].clock_source == internal) {
          s->usrp->set_clock_source("internal");
Thomas Laurent's avatar
Thomas Laurent committed
1197
        } else {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1198 1199 1200
          s->usrp->set_clock_source("external");
          s->usrp->set_time_source("external");
        }
laurent's avatar
laurent committed
1201

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1202
        device->type = USRP_B200_DEV;
laurent's avatar
laurent committed
1203

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1204 1205 1206
        if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
          openair0_cfg[0].rx_gain_calib_table = calib_table_b210;
          bw_gain_adjust=0;
1207
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1208 1209 1210 1211 1212
          std::cerr << "-- Using calibration table: calib_table_b210" << std::endl; // Bell Labs info
#endif
        } else {
          openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
          bw_gain_adjust=1;
1213
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1214 1215 1216
          std::cerr << "-- Using calibration table: calib_table_b210_38" << std::endl; // Bell Labs info
#endif
        }
1217

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262
        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:
            LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
            exit(-1);
            break;
laurent's avatar
laurent committed
1263
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1264
      }
Thomas Laurent's avatar
Thomas Laurent committed
1265

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285
      /* 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
      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());
laurent's avatar
laurent committed
1286
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1287
      }
1288

1289
      LOG_D(PHY, "usrp->get_tx_num_channels() == %zd\n", s->usrp->get_tx_num_channels());
1290
      LOG_D(PHY, "openair0_cfg[0].tx_num_channels == %d\n", openair0_cfg[0].tx_num_channels);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1291

1292 1293
      for(int i=0; i<s->usrp->get_tx_num_channels(); i++) {
        ::uhd::gain_range_t gain_range_tx = s->usrp->get_tx_gain_range(i);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1294

1295 1296 1297 1298
        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(gain_range_tx.stop()-openair0_cfg[0].tx_gain[i],i);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1299
          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
1300
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1301
      }
laurent's avatar
laurent committed
1302

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1303 1304 1305 1306 1307 1308 1309 1310 1311