usrp_lib.cpp 52 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
31
32
 */

#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/usrp/multi_usrp.hpp>
33
#include <uhd/version.hpp>
knopp's avatar
   
knopp committed
34
35
#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>
knopp's avatar
   
knopp committed
38
39
40
41
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
42
#include <time.h>
43
#include "common/utils/LOG/log.h"
knopp's avatar
   
knopp committed
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 {
knopp's avatar
   
knopp committed
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;
knopp's avatar
   
knopp committed
103

laurent's avatar
laurent committed
104
} usrp_state_t;
knopp's avatar
   
knopp committed
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));
          }
        }
knopp's avatar
   
knopp committed
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);
  }
knopp's avatar
   
knopp committed
251

Florian Kaltenberger's avatar
Florian Kaltenberger committed
252
  return EXIT_SUCCESS;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
253
}
knopp's avatar
   
knopp committed
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
  if (u_sf_mode != 2) { // not replay mode
#endif
Thomas Laurent's avatar
Thomas Laurent committed
302
    uhd::set_thread_priority_safe(1.0);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
303
304
305
    usrp_state_t *s = (usrp_state_t *)device->priv;
    // setup GPIO for TDD, GPIO(4) = ATR_RX
    //set data direction register (DDR) to output
306
    s->usrp->set_gpio_attr("FP0", "DDR", 0x1f, 0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
307
    //set control register to ATR
308
    s->usrp->set_gpio_attr("FP0", "CTRL", 0x1f,0x1f);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
309
    //set ATR register
310
    s->usrp->set_gpio_attr("FP0", "ATR_RX", 1<<4, 0x1f);
laurent's avatar
laurent committed
311
312
    // 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
313
314
    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));
315
316
317

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

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

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

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

348
  done = 1;
knopp's avatar
   
knopp committed
349

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

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

  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);
382
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
383
384
385

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

  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 {
408
      if (ms_sample != NULL) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
409
410
        free(ms_sample);
        ms_sample = NULL;
411
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
412
413
414
415

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

#endif
knopp's avatar
   
knopp committed
421
}
422

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

435
  if (u_sf_mode != 2) { // not replay mode
Florian Kaltenberger's avatar
Florian Kaltenberger committed
436
437
#endif
    usrp_state_t *s = (usrp_state_t *)device->priv;
438

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

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

468
    boolean_t first_packet_state=false,last_packet_state=false,first_packet_has_timespec=false;
469

Florian Kaltenberger's avatar
Florian Kaltenberger committed
470
    if (flags == 2) { // start of burst
471
472
473
474
475
      //      s->tx_md.start_of_burst = true;
      //      s->tx_md.end_of_burst = false;
      first_packet_state = true;
      last_packet_state = false;
      first_packet_has_timespec=true;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
476
    } else if (flags == 3) { // end of burst
477
478
479
480
      //s->tx_md.start_of_burst = false;
      //s->tx_md.end_of_burst = true;
      first_packet_state = false;
      last_packet_state - true;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
481
    } else if (flags == 4) { // start and end
482
483
484
485
486
    //  s->tx_md.start_of_burst = true;
    //  s->tx_md.end_of_burst = true;
      first_packet_state = true;
      last_packet_state = true;
      first_packet_has_timespec=true;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
487
    } else if (flags==1) { // middle of burst
488
489
490
491
    //  s->tx_md.start_of_burst = false;
    //  s->tx_md.end_of_burst = false;
      first_packet_state = false;
      last_packet_state = false;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
492
    }
493
494
495
496
497
498
499
    else 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;
     first_packet_state=false;
     last_packet_state=true;
    }
500
501
502
503
    s->tx_md.has_time_spec  = first_packet_has_timespec;
    s->tx_md.start_of_burst = first_packet_state; 
    s->tx_md.end_of_burst   = last_packet_state;
    s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
504
505

    if (cc>1) {
506
       std::vector<void *> buff_ptrs;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
507

508
509
       for (int i=0; i<cc; i++)
         buff_ptrs.push_back(&(((int16_t*)buff_tx[i])[0]));
Florian Kaltenberger's avatar
Florian Kaltenberger committed
510

511
512
       ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md);
    } else ret = (int)s->tx_stream->send(&(((int16_t *)buff_tx[0])[0]), nsamps, s->tx_md);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
513

514
515
     if (ret != nsamps) LOG_E(PHY,"[xmit] tx samples %d != %d\n",ret,nsamps);
 
516
517
518
519
520
521
522
523
#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;
  }
524

Florian Kaltenberger's avatar
Florian Kaltenberger committed
525
#endif
526
  return ret;
knopp's avatar
   
knopp committed
527
528
}

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

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

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

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
607
608
        for (int i=0; i<cc; i++) buff_ptrs.push_back(buff[i]);

609
        samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md,1.0);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
610
611
      } else {
        // receive a single channel (e.g. from connector RF A)
612
        samples_received = s->rx_stream->recv(buff[0], nsamps, s->rx_md,1.0);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
613
      }
navid's avatar
navid committed
614
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
615

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

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

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

#endif
629
#if defined(USRP_REC_PLAY)
Florian Kaltenberger's avatar
Florian Kaltenberger committed
630

631
632
633
  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
634
      (ms_sample+nb_samples)->header = BELL_LABS_IQ_HEADER;
635
636
637
638
639
640
641
642
      (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
643

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

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

Mongazon's avatar
Mongazon committed
651
      if (!use_mmap) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
652
653
654
655
656
        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
657
      }
658
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
659

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

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

704
    struct timespec req;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
705

706
    req.tv_sec = 0;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
707

708
    req.tv_nsec = u_sf_read_delay * 1000;
Florian Kaltenberger's avatar
Florian Kaltenberger committed
709

710
    nanosleep(&req, NULL);
Florian Kaltenberger's avatar
Florian Kaltenberger committed
711

712
713
    return nsamps;
  }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
714

715
716
#endif
  return samples_received;
knopp's avatar
   
knopp committed
717
718
}

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

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
752
  return(0);
knopp's avatar
   
knopp committed
753
754
}

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

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
791
792
793
794
795
  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
796
}
797

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

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

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

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

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

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

  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
887
    }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
888
889
890

    i++;
  }
891
892
}

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

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

909
910
#if defined(USRP_REC_PLAY)
extern "C" {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
911
912
913
914
915
  /*! \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) {
916
917
918
919
    // --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
920
    usrp_recplay_params[0].strptr=(char **)&u_sf_filename;
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
970
971
972
    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
973
  }
974
975
976
}
#endif

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
991
992
993
    if (done == 1) {
      return 0;
    } // prevent from multiple init
994

Florian Kaltenberger's avatar
Florian Kaltenberger committed
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
1046
1047
1048
    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 {
1049
#endif
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1050
      usrp_state_t *s = (usrp_state_t *)calloc(sizeof(usrp_state_t),1);
1051

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1055
1056
1057
1058
1059
      // 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
1060

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1065
1066
#endif
      sscanf(uhd::get_version_string().c_str(),"%d.%d.%d",&vers,&subvers,&subsubvers);
1067
      LOG_I(PHY,"UHD version %s (%d.%d.%d)\n",
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1068
            uhd::get_version_string().c_str(),vers,subvers,subsubvers);
laurent's avatar
laurent committed
1069

1070
1071
1072
1073
1074
      std::string args;
      if (openair0_cfg[0].sdr_addrs == NULL) {
        args = "type=b200";
      } else {
        args = openair0_cfg[0].sdr_addrs;
1075
	LOG_I(PHY,"Checking for USRP with args %s\n",openair0_cfg[0].sdr_addrs);
1076
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1077

1078
      uhd::device_addrs_t device_adds = uhd::device::find(args);
1079

1080
      if (device_adds.size() == 0) {
1081
        std::cerr<<"No USRP Device Found. " << args << std::endl;
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
        free(s);
        return -1;
      } else if (device_adds.size() > 1) {
        std::cerr<<"More than one USRP Device Found. Please specify device more precisely in config file." << std::endl;
	free(s);
	return -1;
      }

      std::cerr << "Found USRP " << device_adds[0].get("type") << "\n";
      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;
1097
        args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
1098
        args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=7680, recv_frame_size=7680" ;
1099
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1100

1101
1102
1103
1104
1105
      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);
knopp's avatar
knopp committed
1106
        //args += ", send_buff_size=33554432";
1107
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1108

1109
1110
1111
1112
1113
      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);
Thomas Laurent's avatar
Thomas Laurent committed
1114
1115
1116
	// USRP recommended: https://files.ettus.com/manual/page_usrp_x3x0_config.html
	if ( 0 != system("sysctl -w net.core.rmem_max=33554432 net.core.wmem_max=33554432") )
		LOG_W(HW,"Can't set kernel paramters for X3xx\n");
1117
      }
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1118

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

1121
1122
1123
1124
1125
      // 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
1126

knopp's avatar
knopp committed
1127
      
1128
      if (device->type==USRP_X300_DEV) {
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1129
1130
1131
1132
1133
        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
1134

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

1144
1145
1146
1147
1148
1149
1150
1151
1152
          case 92160000:
            // 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;
            
	  case 61440000:
Florian Kaltenberger's avatar
Florian Kaltenberger committed
1153
1154
1155
1156
1157
1158
            // 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
1159

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

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

Florian Kaltenberger's avatar
Florian Kaltenberger committed
1175
1176
1177
1178
1179
1180
          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;