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

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

#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
31
32
#include <uhd/version.hpp>
#if UHD_VERSION < 3110000
knopp's avatar
 
knopp committed
33
#include <uhd/utils/thread_priority.hpp>
34
35
36
#else
#include <uhd/utils/thread.hpp>
#endif
knopp's avatar
 
knopp committed
37
#include <uhd/usrp/multi_usrp.hpp>
38
#include <uhd/version.hpp>
knopp's avatar
 
knopp committed
39
40
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
Florian Kaltenberger's avatar
Florian Kaltenberger committed
41
42
#include <boost/thread.hpp>
#include <boost/format.hpp>
knopp's avatar
 
knopp committed
43
44
45
46
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
47
#include <time.h>
48
#include "common/utils/LOG/log.h"
knopp's avatar
   
knopp committed
49
#include "common_lib.h"
laurent's avatar
laurent committed
50
#include "assertions.h"
Mongazon's avatar
Mongazon committed
51
52
#include <sys/sysinfo.h>
#include <sys/resource.h>
laurent's avatar
laurent committed
53

54
55
56
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
laurent's avatar
laurent committed
57

58
59
60
#ifdef __AVX2__
#  include <immintrin.h>
#endif
61

62
63
64
65
#ifdef __arm__
#  include <arm_neon.h>
#endif

66
67
68
69
/** @addtogroup _USRP_PHY_RF_INTERFACE_
 * @{
 */

laurent's avatar
laurent committed
70
71
/*! \brief USRP Configuration */
typedef struct {
knopp's avatar
 
knopp committed
72

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
103
104
105
106
107
  // --------------------------------
  // 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;
knopp's avatar
 
knopp committed
108

laurent's avatar
laurent committed
109
} usrp_state_t;
knopp's avatar
 
knopp committed
110

Florian Kaltenberger's avatar
Florian Kaltenberger committed
111
112
//void print_notes(void)
//{
Florian Kaltenberger's avatar
Florian Kaltenberger committed
113
114
115
116
117
118
// 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
119
120
//}

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
167
168
169
170
171
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));
          }
        }
knopp's avatar
 
knopp committed
172

Florian Kaltenberger's avatar
Florian Kaltenberger committed
173
174
175
176
177
178
        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
179
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
180
181
182
      } else {
        std::cout << boost::format("ref_locked sensor not present on this board.\n");
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
183

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
187
188
189
190
      if(gps_locked) {
        num_gps_locked++;
        std::cout << boost::format("GPS Locked\n");
      } else {
191
        LOG_W(HW,"WARNING:  GPS not locked - time will not be accurate until locked\n");
Florian Kaltenberger's avatar
Florian Kaltenberger committed
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
      }

      //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
212
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
241
242
243
244
245

    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
246
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
247
248
249
250
251
252
253
254
255
  } 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);
  }
knopp's avatar
 
knopp committed
256

Florian Kaltenberger's avatar
Florian Kaltenberger committed
257
  return EXIT_SUCCESS;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
258
}
knopp's avatar
 
knopp committed
259

260
261
262
263
#if defined(USRP_REC_PLAY)
#include "usrp_lib.h"
static FILE    *pFile = NULL;
int             mmapfd = 0;
Mongazon's avatar
Mongazon committed
264
265
int             iqfd = 0;
int             use_mmap = 1; // default is to use mmap
266
267
268
269
270
271
272
273
274
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
275
char            u_sf_filename[1024] = "";              // subframes file path
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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

299
300
301
/*! \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
302
static int trx_usrp_start(openair0_device *device) {
303
#if defined(USRP_REC_PLAY)
304

Florian Kaltenberger's avatar
Florian Kaltenberger committed
305
306
307
308
309
  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
310
    s->usrp->set_gpio_attr("FP0", "DDR", 0x1f, 0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
311
    //set control register to ATR
312
    s->usrp->set_gpio_attr("FP0", "CTRL", 0x1f,0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
313
    //set ATR register
314
    s->usrp->set_gpio_attr("FP0", "ATR_RX", 1<<4, 0x1f);
laurent's avatar
laurent committed
315
316
    // 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
317
318
    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));
319
320
321

    if (s->use_gps == 1) {
      s->wait_for_first_pps = 1;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
322
323
324
      cmd.time_spec = s->usrp->get_time_last_pps() + uhd::time_spec_t(1.0);
    } else {
      s->wait_for_first_pps = 0;
325
326
327
      cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
    }

laurent's avatar
laurent committed
328
    cmd.stream_now = false; // start at constant delay
laurent's avatar
laurent committed
329
330
331
332
333
334
335
336
    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;
337
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
338
339
340
341
  }

#endif
  return 0;
knopp's avatar
 
knopp committed
342
}
laurent's avatar
laurent committed
343
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
344
345
 * \param device the hardware to use
 */
laurent's avatar
laurent committed
346
static void trx_usrp_end(openair0_device *device) {
347
348
#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
349

350
  if (done == 1) return;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
351

352
  done = 1;
knopp's avatar
 
knopp committed
353

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

366
367
#endif
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385

  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);
386
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
387
388
389

      fclose (pFile);
      std::cerr << "File " << u_sf_filename << " closed." << std::endl;
390
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
  }

  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 {
412
      if (ms_sample != NULL) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
413
414
        free(ms_sample);
        ms_sample = NULL;
415
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
416
417
418
419

      if (iqfd != 0) {
        close(iqfd);
        iqfd = 0;
420
421
      }
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
422
423
424
  }

#endif
knopp's avatar
 
knopp committed
425
}
426

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

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

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
507
508
509
510
511
    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);

512
513
514
515
516
517
518
519
#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;
  }
520

Florian Kaltenberger's avatar
Florian Kaltenberger committed
521
#endif
522
  return ret;
knopp's avatar
 
knopp committed
523
524
}

525
526
527
528
529
530
531
532
533
534
535
/*! \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
536
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
537
  usrp_state_t *s = (usrp_state_t *)device->priv;
538
539
540
  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
541

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

laurent's avatar
laurent committed
557
    if (device->type == USRP_B200_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
      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
579
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
580
581
582
583
584
585
586

        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++) {
587
588
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
Florian Kaltenberger's avatar
Florian Kaltenberger committed
589
          ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
590
#else
Florian Kaltenberger's avatar
Florian Kaltenberger committed
591
          ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
592
593
#endif
#elif defined(__arm__)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
594
          ((int16x8_t *)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
595
#endif
laurent's avatar
laurent committed
596
        }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
597
      }
laurent's avatar
laurent committed
598
    } else if (device->type == USRP_X300_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
599
600
601
      if (cc>1) {
        // receive multiple channels (e.g. RF A and RF B)
        std::vector<void *> buff_ptrs;
laurent's avatar
laurent committed
602

Florian Kaltenberger's avatar
Florian Kaltenberger committed
603
604
605
606
607
608
609
        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);
      }
navid's avatar
navid committed
610
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
611

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

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

laurent's avatar
laurent committed
618
619
620
    s->rx_count += nsamps;
    s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
    *ptimestamp = s->rx_timestamp;
621
622
#if defined (USRP_REC_PLAY)
  }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
623
624

#endif
625
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
626

627
628
629
  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
630
      (ms_sample+nb_samples)->header = BELL_LABS_IQ_HEADER;
631
632
633
634
635
636
637
638
      (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
639

640
      if (wrap_count == u_sf_loops) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
641
642
        std::cerr << "USRP device terminating subframes replay mode after " << u_sf_loops << " loops." << std::endl;
        return 0; // should make calling process exit
643
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
644

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

Mongazon's avatar
Mongazon committed
647
      if (!use_mmap) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
648
649
650
651
652
        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
653
      }
654
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
655

Mongazon's avatar
Mongazon committed
656
657
    if (use_mmap) {
      if (cur_samples < nb_samples) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
658
659
660
661
662
663
664
665
666
        *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
667
668
669
670
      }
    } else {
      // read sample from file
      if (read(iqfd, ms_sample, sizeof(iqrec_t)) != sizeof(iqrec_t)) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
671
672
673
674
675
676
        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
677
678
679
      }

      if (cur_samples < nb_samples) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
        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);
697
698
      }
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
699

700
    struct timespec req;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
701

702
    req.tv_sec = 0;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
703

704
    req.tv_nsec = u_sf_read_delay * 1000;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
705

706
    nanosleep(&req, NULL);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
707

708
709
    return nsamps;
  }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
710

711
712
#endif
  return samples_received;
knopp's avatar
 
knopp committed
713
714
}

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

723
void *freq_thread(void *arg) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
724
725
726
727
  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]);
728
  return NULL;
729
730
}
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
731
732
733
 * \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
734
 * \returns 0 in success
735
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
736
737
738
739
740
741
742
743
744
745
746
747
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]);
  }
knopp's avatar
   
knopp committed
748

Florian Kaltenberger's avatar
Florian Kaltenberger committed
749
  return(0);
knopp's avatar
   
knopp committed
750
751
}

laurent's avatar
laurent committed
752
/*! \brief Set RX frequencies
753
754
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
laurent's avatar
laurent committed
755
 * \returns 0 in success
756
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
757
758
759
760
761
762
763
764
765
766
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);
knopp's avatar
   
knopp committed
767
768
}

769
770
771
/*! \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
772
 * \returns 0 in success
773
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
774
int trx_usrp_set_gains(openair0_device *device,
laurent's avatar
laurent committed
775
                       openair0_config_t *openair0_cfg) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
776
777
778
779
780
781
782
783
784
785
786
  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
787

Florian Kaltenberger's avatar
Florian Kaltenberger committed
788
789
790
791
792
  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);
knopp's avatar
   
knopp committed
793
}
794

795
796
797
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
798
799
int trx_usrp_stop(openair0_device *device) {
  return(0);
800
}
801

802
/*! \brief USRPB210 RX calibration table */
803
rx_gain_calib_table_t calib_table_b210[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
804
805
806
807
808
809
  {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
810
};
811

812
/*! \brief USRPB210 RX calibration table */
813
rx_gain_calib_table_t calib_table_b210_38[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
814
815
816
817
818
819
  {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
820
};
821

822
/*! \brief USRPx310 RX calibration table */
823
rx_gain_calib_table_t calib_table_x310[] = {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
824
825
826
827
828
829
  {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
830
831
832
};

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
  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;
871
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
872
873
874
875
876
877
878
879
880
881
882
883
  }

  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
884
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
885
886
887

    i++;
  }
888
889
}

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

laurent's avatar
laurent committed
898
899
900
901
/*! \brief Reset the USRP statistics
 * \param device the hardware to use
 * \returns  0 on success
 */
Florian Kaltenberger's avatar
Florian Kaltenberger committed
902
903
int trx_usrp_reset_stats(openair0_device *device) {
  return(0);
904
}
905

906
907
#if defined(USRP_REC_PLAY)
extern "C" {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
908
909
910
911
912
  /*! \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) {
913
914
915
916
    // --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
917
    usrp_recplay_params[0].strptr=(char **)&u_sf_filename;
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
964
965
966
967
968
969
    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
970
  }
971
972
973
}
#endif

974
extern "C" {
975
976
977
978
  /*! \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
979
  int device_init(openair0_device *device, openair0_config_t *openair0_cfg) {
980
981
    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);
982
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
983
984
985
986
    paramdef_t usrp_recplay_params[7];
    struct sysinfo systeminfo;
    // to check
    static int done = 0;
987

Florian Kaltenberger's avatar
Florian Kaltenberger committed
988
989
990
    if (done == 1) {
      return 0;
    } // prevent from multiple init
991

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
1040
1041
1042
1043
1044
1045
    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 {
1046
#endif
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1047
1048
      uhd::set_thread_priority_safe(1.0);
      usrp_state_t *s = (usrp_state_t *)calloc(sizeof(usrp_state_t),1);
1049

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1050
1051
      if (openair0_cfg[0].clock_source==gpsdo)
        s->use_gps =1;
laurent's avatar
laurent committed
1052

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1053
1054
1055
1056
1057
      // Initialize USRP device
      device->openair0_cfg = openair0_cfg;
      int vers=0,subvers=0,subsubvers=0;
      int bw_gain_adjust=0;
#if defined(USRP_REC_PLAY)
laurent's avatar
laurent committed
1058

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1059
1060
1061
      if (u_sf_mode == 1) {
        std::cerr << "USRP device initialized in subframes record mode" << std::endl;
      }
laurent's avatar
laurent committed
1062

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1063
1064
1065
1066
#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
1067

1068
1069
1070
1071
1072
1073
      std::string args;
      if (openair0_cfg[0].sdr_addrs == NULL) {
        args = "type=b200";
      } else {
        args = openair0_cfg[0].sdr_addrs;
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1074

1075
      uhd::device_addrs_t device_adds = uhd::device::find(args);
1076

1077
      if (device_adds.size() == 0) {
1078
        LOG_E(HW,"No USRP Device Found.\n ");
1079
1080
1081
        free(s);
        return -1;
      } else if (device_adds.size() > 1) {
1082
        LOG_E(HW,"More than one USRP Device Found. Please specify device more precisely in config file.\n");
1083
1084
1085
1086
	free(s);
	return -1;
      }

1087
      LOG_I(HW,"Found USRP %s\n", device_adds[0].get("type").c_str());
1088
1089
1090
1091
1092
1093
      double usrp_master_clock;

      if (device_adds[0].get("type") == "b200") {
        printf("Found USRP b200\n");
        device->type = USRP_B200_DEV;
        usrp_master_clock = 30.72e6;
1094
        args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
1095
        args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=7680, recv_frame_size=7680" ;
1096
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1097

1098
1099
1100
1101
1102
1103
      if (device_adds[0].get("type") == "n3xx") {
        printf("Found USRP n300\n");
        device->type=USRP_X300_DEV; //treat it as X300 for now
        usrp_master_clock = 122.88e6;
        args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1104

1105
1106
1107
1108
1109
1110
      if (device_adds[0].get("type") == "x300") {
        printf("Found USRP x300\n");
        device->type=USRP_X300_DEV;
        usrp_master_clock = 184.32e6;
        args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1111

1112
      s->usrp = uhd::usrp::multi_usrp::make(args);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1113

1114
1115
1116
1117
1118
      // lock mboard clocks
      if (openair0_cfg[0].clock_source == internal)
        s->usrp->set_clock_source("internal");
      else
        s->usrp->set_clock_source("external");
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1119

1120
      if (device->type==USRP_X300_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1121
1122
1123
1124
1125
        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
1126

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1127
1128
1129
1130
1131
1132
1133
1134
        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;
1135

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1136
1137
1138
1139
1140
1141
1142
          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
1143

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1144
1145
1146
1147
1148
1149
1150
          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
1151

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1152
1153
1154
1155
1156
1157
          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;
1158

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1159
1160
1161
1162
1163
1164
          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;
1165

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1166
1167
1168
1169
1170
1171
          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
1172

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1173
1174
1175
1176
1177
          default:
            LOG_E(PHY,"Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
            exit(-1);
            break;
        }
1178
      }
laurent's avatar
laurent committed
1179

1180
      if (device->type == USRP_B200_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1181
1182
1183
        if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
          openair0_cfg[0].rx_gain_calib_table = calib_table_b210;
          bw_gain_adjust=0;
1184
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1185
1186
1187
1188
1189
          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;
1190
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1191
1192
1193
          std::cerr << "-- Using calibration table: calib_table_b210_38" << std::endl; // Bell Labs info
#endif
        }