usrp_lib.cpp 28.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.0  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */
knopp's avatar
 
knopp committed
21
22
23

/** usrp_lib.cpp
 *
24
 * \author: HongliangXU : hong-liang-xu@agilent.com
knopp's avatar
 
knopp committed
25
26
27
28
29
30
31
32
 */

#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <uhd/utils/thread_priority.hpp>
#include <uhd/usrp/multi_usrp.hpp>
33
#include <uhd/version.hpp>
knopp's avatar
 
knopp committed
34
35
36
37
38
39
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include <iostream>
#include <complex>
#include <fstream>
#include <cmath>
40
#include <time.h>
Rohit Gupta's avatar
Rohit Gupta committed
41
#include "UTIL/LOG/log_extern.h"
knopp's avatar
   
knopp committed
42
#include "common_lib.h"
43
44
45
46
47
48
49
#ifdef __SSE4_1__
#  include <smmintrin.h>
#endif
 
#ifdef __AVX2__
#  include <immintrin.h>
#endif
50

51
52
53
54
#ifdef __arm__
#  include <arm_neon.h>
#endif

55
56
57
58
59
/** @addtogroup _USRP_PHY_RF_INTERFACE_
 * @{
 */

/*! \brief USRP Configuration */ 
knopp's avatar
 
knopp committed
60
61
62
63
64
65
typedef struct
{

  // --------------------------------
  // variables for USRP configuration
  // --------------------------------
66
  //! USRP device pointer
knopp's avatar
 
knopp committed
67
68
  uhd::usrp::multi_usrp::sptr usrp;
  //uhd::usrp::multi_usrp::sptr rx_usrp;
69
  
knopp's avatar
 
knopp committed
70
  //create a send streamer and a receive streamer
71
  //! USRP TX Stream
knopp's avatar
 
knopp committed
72
  uhd::tx_streamer::sptr tx_stream;
73
  //! USRP RX Stream
knopp's avatar
 
knopp committed
74
75
  uhd::rx_streamer::sptr rx_stream;

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

81
  //! USRP Timestamp Information
knopp's avatar
 
knopp committed
82
  uhd::time_spec_t tm_spec;
83

knopp's avatar
 
knopp committed
84
  //setup variables and allocate buffer
85
  //! USRP Metadata
knopp's avatar
 
knopp committed
86
87
  uhd::async_metadata_t async_md;

88
  //! Sampling rate
knopp's avatar
 
knopp committed
89
  double sample_rate;
90
91

  //! time offset between transmiter timestamp and receiver timestamp;
knopp's avatar
 
knopp committed
92
  double tdiff;
93
94

  //! TX forward samples. We use usrp_time_offset to get this value
knopp's avatar
 
knopp committed
95
96
97
98
99
100
  int tx_forward_nsamps; //166 for 20Mhz


  // --------------------------------
  // Debug and output control
  // --------------------------------
101
  //! Number of underflows
knopp's avatar
 
knopp committed
102
  int num_underflows;
103
  //! Number of overflows
knopp's avatar
 
knopp committed
104
  int num_overflows;
105
106
  
  //! Number of sequential errors
knopp's avatar
 
knopp committed
107
  int num_seq_errors;
108
  //! tx count
knopp's avatar
 
knopp committed
109
  int64_t tx_count;
110
  //! rx count
knopp's avatar
 
knopp committed
111
  int64_t rx_count;
112
  //! timestamp of RX packet
knopp's avatar
 
knopp committed
113
114
115
116
  openair0_timestamp rx_timestamp;

} usrp_state_t;

117
118
119
/*! \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
*/
knopp's avatar
 
knopp committed
120
121
122
123
124
125
static int trx_usrp_start(openair0_device *device)
{
  usrp_state_t *s = (usrp_state_t*)device->priv;

  // init recv and send streaming
  uhd::stream_cmd_t cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
knopp's avatar
   
knopp committed
126
  cmd.time_spec = s->usrp->get_time_now() + uhd::time_spec_t(0.05);
knopp's avatar
 
knopp committed
127
128
129
  cmd.stream_now = false; // start at constant delay
  s->rx_stream->issue_stream_cmd(cmd);

knopp's avatar
   
knopp committed
130
  s->tx_md.time_spec = cmd.time_spec + uhd::time_spec_t(1-(double)s->tx_forward_nsamps/s->sample_rate);
knopp's avatar
 
knopp committed
131
132
133
134
135
136
137
138
  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;
139
140

  return 0;
knopp's avatar
 
knopp committed
141
}
142
143
144
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources 
 * \param device the hardware to use
 */
knopp's avatar
 
knopp committed
145
146
147
148
149
150
static void trx_usrp_end(openair0_device *device)
{
  usrp_state_t *s = (usrp_state_t*)device->priv;

  s->rx_stream->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);

navid's avatar
navid committed
151
152
153
154
155
  //send a mini EOB packet
  s->tx_md.end_of_burst = true;
  s->tx_stream->send("", 0, s->tx_md);
  s->tx_md.end_of_burst = false;
  
knopp's avatar
 
knopp committed
156
}
157

158
159
160
161
162
163
164
165
/*! \brief Called to send samples to the USRP RF target
      @param device pointer to the device structure specific to the RF hardware target
      @param timestamp The timestamp at whicch the first sample MUST be sent 
      @param buff Buffer which holds the samples
      @param nsamps number of samples to be sent
      @param antenna_id index of the antenna if the device has multiple anteannas
      @param flags flags must be set to TRUE if timestamp parameter needs to be applied
*/ 
166
static int trx_usrp_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int cc, int flags)
knopp's avatar
 
knopp committed
167
{
168
169
170
171
172
173
174
   static long long int loop=0;
   static long time_min=0, time_max=0, time_avg=0;
   struct timespec tp_start, tp_end;
   long time_diff;
   clock_gettime(CLOCK_MONOTONIC_RAW, &tp_start);

  int ret=0, ret_i=0;
knopp's avatar
 
knopp committed
175
  usrp_state_t *s = (usrp_state_t*)device->priv;
knopp's avatar
knopp committed
176

177
  s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
knopp's avatar
knopp committed
178

179
  
knopp's avatar
knopp committed
180
  if(flags>0)
knopp's avatar
 
knopp committed
181
182
183
    s->tx_md.has_time_spec = true;
  else
    s->tx_md.has_time_spec = false;
184

knopp's avatar
knopp committed
185
186
187
188
189
190
191
192
193
  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
194
195
196
    s->tx_md.start_of_burst = true;
    s->tx_md.end_of_burst = true;
  }
knopp's avatar
knopp committed
197
  else if (flags==1) { // middle of burst
198
199
200
    s->tx_md.start_of_burst = false;
    s->tx_md.end_of_burst = false;
  }
201
  
knopp's avatar
knopp committed
202
203
204
  if (cc>1) {
    std::vector<void *> buff_ptrs;
    for (int i=0;i<cc;i++) buff_ptrs.push_back(buff[i]);
205
    ret = (int)s->tx_stream->send(buff_ptrs, nsamps, s->tx_md,1e-3);
knopp's avatar
knopp committed
206
207
  }
  else
208
209
    ret = (int)s->tx_stream->send(buff[0], nsamps, s->tx_md,1e-3);

210

211

212
213
214
  if (ret != nsamps) {
    printf("[xmit] tx samples %d != %d\n",ret,nsamps);
  }
215
216
217
218
219
220
221
222
223
224
225

  clock_gettime(CLOCK_MONOTONIC_RAW, &tp_end);
  time_diff = (tp_end.tv_sec - tp_start.tv_sec) *1E09  + (tp_end.tv_nsec - tp_start.tv_nsec);
  if  (time_min==0 ||loop==1 || time_min > time_diff)
    time_min=time_diff;
  if  (time_max==0 ||loop==1 || time_max < time_diff)
    time_max=time_diff;
  if (time_avg ==0 ||loop==1)
    time_avg= time_diff;
  else
    time_avg=(time_diff+time_avg) /2.0;
Rohit Gupta's avatar
Rohit Gupta committed
226

227
  /*   //prints statics of uhd every 10 seconds
Rohit Gupta's avatar
Rohit Gupta committed
228
229
   if ( loop % (10 * ((int)device->openair0_cfg[0].sample_rate /(int)nsamps )) ==0)
     LOG_I(HW,"usrp_write: min(ns)=%d, max(ns)=%d, avg(ns)=%d\n", (int)time_min, (int)time_max,(int)time_avg);
230
  */
231
   loop++;
232
  return ret;
knopp's avatar
 
knopp committed
233
234
}

235
236
237
238
239
240
241
242
243
244
245
/*! \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
*/
knopp's avatar
   
knopp committed
246
static int trx_usrp_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int cc)
knopp's avatar
 
knopp committed
247
{
248
249
250
251
252
   static long long int loop=0;
   static long time_min=0, time_max=0, time_avg=0;
   struct timespec tp_start, tp_end;
   long time_diff;
   clock_gettime(CLOCK_MONOTONIC_RAW, &tp_start);
253
254
255
256
257
258
   usrp_state_t *s = (usrp_state_t*)device->priv;
   int samples_received=0,i,j;
   int nsamps2;  // aligned to upper 32 or 16 byte boundary
#if defined(__x86_64) || defined(__i386__)
#ifdef __AVX2__
   nsamps2 = (nsamps+7)>>3;
259
   __m256i buff_tmp[2][nsamps2];
260
261
#else
   nsamps2 = (nsamps+3)>>2;
262
   __m128i buff_tmp[2][nsamps2];
263
264
265
#endif
#elif defined(__arm__)
   nsamps2 = (nsamps+3)>>2;
266
   int16x8_t buff_tmp[2][nsamps2];
267
268
#endif

knopp's avatar
 
knopp committed
269

270
  if (device->type == USRP_B200_DEV) {  
271
    if (cc>1) {
gauthier's avatar
gauthier committed
272
    // receive multiple channels (e.g. RF A and RF B)
273
      std::vector<void *> buff_ptrs;
274
 
275
276
277
      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 {
gauthier's avatar
gauthier committed
278
    // receive a single channel (e.g. from connector RF A)
279
280
      samples_received = s->rx_stream->recv(buff_tmp[0], nsamps, s->rx_md);
    }
281

282
  // bring RX data into 12 LSBs for softmodem RX
283
284
    for (int i=0;i<cc;i++) {
      for (int j=0; j<nsamps2; j++) {      
285
286
#if defined(__x86_64__) || defined(__i386__)
#ifdef __AVX2__
287
        ((__m256i *)buff[i])[j] = _mm256_srai_epi16(buff_tmp[i][j],4);
288
#else
289
        ((__m128i *)buff[i])[j] = _mm_srai_epi16(buff_tmp[i][j],4);
290
291
#endif
#elif defined(__arm__)
292
        ((int16x8_t*)buff[i])[j] = vshrq_n_s16(buff_tmp[i][j],4);
293
#endif
294
      }
295
    }
296
  } else if (device->type == USRP_X300_DEV) {
297
    if (cc>1) { 
298
299
300
301
302
303
304
305
    // receive multiple channels (e.g. RF A and RF B)
      std::vector<void *> buff_ptrs;
 
      for (int i=0;i<cc;i++) buff_ptrs.push_back(buff[i]);
      samples_received = s->rx_stream->recv(buff_ptrs, nsamps, s->rx_md);
    } else {
    // receive a single channel (e.g. from connector RF A)
      samples_received = s->rx_stream->recv(buff[0], nsamps, s->rx_md);
navid's avatar
navid committed
306
    }
307
  }
308

knopp's avatar
   
knopp committed
309
310
311
312
  if (samples_received < nsamps) {
    printf("[recv] received %d samples out of %d\n",samples_received,nsamps);
    
  }
navid's avatar
navid committed
313

knopp's avatar
   
knopp committed
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
  //handle the error code
  switch(s->rx_md.error_code){
  case uhd::rx_metadata_t::ERROR_CODE_NONE:
    break;
  case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
    printf("[recv] USRP RX OVERFLOW!\n");
    s->num_overflows++;
    break;
  case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
    printf("[recv] USRP RX TIMEOUT!\n");
    break;
  default:
    printf("[recv] Unexpected error on RX, Error code: 0x%x\n",s->rx_md.error_code);
    break;
  }
knopp's avatar
 
knopp committed
329
330
331
  s->rx_count += nsamps;
  s->rx_timestamp = s->rx_md.time_spec.to_ticks(s->sample_rate);
  *ptimestamp = s->rx_timestamp;
navid's avatar
navid committed
332

Rohit Gupta's avatar
Rohit Gupta committed
333
334
335
336
337
338
339
340
341
342
  clock_gettime(CLOCK_MONOTONIC_RAW, &tp_end);
  time_diff = (tp_end.tv_sec - tp_start.tv_sec) *1E09  + (tp_end.tv_nsec - tp_start.tv_nsec);
  if  (time_min==0 ||loop==1 || time_min > time_diff)
    time_min=time_diff;
  if  (time_max==0 ||loop==1 || time_max < time_diff)
    time_max=time_diff;
  if (time_avg ==0 ||loop==1)
    time_avg= time_diff;
  else
    time_avg=(time_diff+time_avg) /2.0;
343
  /*
Rohit Gupta's avatar
Rohit Gupta committed
344
345
346
347
  //prints statics of uhd every 10 seconds
  if ( loop % (10 * ((int)device->openair0_cfg[0].sample_rate /(int)nsamps )) ==0)
     LOG_I(HW,"usrp_read: min(ns)=%d, max(ns)=%d, avg(ns)=%d\n", (int)time_min, (int)time_max,(int)time_avg);

348
     loop++;*/
Rohit Gupta's avatar
Rohit Gupta committed
349
  return samples_received;
knopp's avatar
 
knopp committed
350
351
}

352
353
354
/*! \brief Get current timestamp of USRP
 * \param device the hardware to use
*/
knopp's avatar
   
knopp committed
355
356
357
358
359
360
361
362
openair0_timestamp get_usrp_time(openair0_device *device) 
{
 
  usrp_state_t *s = (usrp_state_t*)device->priv;
  
  return s->usrp->get_time_now().to_ticks(s->sample_rate);
} 

363
364
365
366
/*! \brief Compares two variables within precision
 * \param a first variable
 * \param b second variable
*/
knopp's avatar
 
knopp committed
367
368
369
370
static bool is_equal(double a, double b)
{
  return std::fabs(a-b) < std::numeric_limits<double>::epsilon();
}
knopp's avatar
   
knopp committed
371

372
373
374
375
376
377
378
379
380
void *freq_thread(void *arg) {
  
  openair0_device *device=(openair0_device *)arg;
  usrp_state_t *s = (usrp_state_t*)device->priv;
  
  s->usrp->set_tx_freq(device->openair0_cfg[0].tx_freq[0]);
  s->usrp->set_rx_freq(device->openair0_cfg[0].rx_freq[0]);
}
/*! \brief Set frequencies (TX/RX). Spawns a thread to handle the frequency change to not block the calling thread
381
382
383
384
385
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \param dummy dummy variable not used
 * \returns 0 in success 
 */
386
int trx_usrp_set_freq(openair0_device* device, openair0_config_t *openair0_cfg, int dont_block) {
knopp's avatar
   
knopp committed
387
388

  usrp_state_t *s = (usrp_state_t*)device->priv;
389
  pthread_t f_thread;
knopp's avatar
   
knopp committed
390

391
  printf("Setting USRP TX Freq %f, RX Freq %f\n",openair0_cfg[0].tx_freq[0],openair0_cfg[0].rx_freq[0]);
392
393
394
395
396
397
398
399

  // 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
400
401
402
403
404

  return(0);
  
}

405
406
407
408
409
/*! \brief Set RX frequencies 
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \returns 0 in success 
 */
knopp's avatar
   
knopp committed
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
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);
  
}

427
428
429
430
431
/*! \brief Set Gains (TX/RX)
 * \param device the hardware to use
 * \param openair0_cfg RF frontend parameters set by application
 * \returns 0 in success 
 */
432
int trx_usrp_set_gains(openair0_device* device, 
433
		       openair0_config_t *openair0_cfg) {
knopp's avatar
   
knopp committed
434
435
436
437

  usrp_state_t *s = (usrp_state_t*)device->priv;

  s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[0]);
438
439
440
441
442
443
444
445
446
  ::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()) {
    
    printf("RX Gain 0 too high, reduce by %f dB\n",
	   openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0] - gain_range.stop());	   
    exit(-1);
  }
  s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[0]-openair0_cfg[0].rx_gain_offset[0]);
447
  printf("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());
448

knopp's avatar
   
knopp committed
449
450
  return(0);
}
451

452
453
454
/*! \brief Stop USRP
 * \param card refers to the hardware index to use
 */
455
int trx_usrp_stop(openair0_device* device) {
456
457
  return(0);
}
458

459
/*! \brief USRPB210 RX calibration table */
460
rx_gain_calib_table_t calib_table_b210[] = {
Raymond Knopp's avatar
Raymond Knopp committed
461
462
463
464
465
  {3500000000.0,44.0},
  {2660000000.0,49.0},
  {2300000000.0,50.0},
  {1880000000.0,53.0},
  {816000000.0,58.0},
466
467
  {-1,0}};

468
/*! \brief USRPB210 RX calibration table */
469
470
471
472
473
474
rx_gain_calib_table_t calib_table_b210_38[] = {
  {3500000000.0,44.0},
  {2660000000.0,49.8},
  {2300000000.0,51.0},
  {1880000000.0,53.0},
  {816000000.0,57.0},
475
476
  {-1,0}};

477
/*! \brief USRPx310 RX calibration table */
478
479
rx_gain_calib_table_t calib_table_x310[] = {
  {3500000000.0,77.0},
480
  {2660000000.0,81.0},
481
482
  {2300000000.0,81.0},
  {1880000000.0,82.0},
483
  {816000000.0,85.0},
484
485
  {-1,0}};

486
487
488
489
490
/*! \brief Set RX gain offset 
 * \param openair0_cfg RF frontend parameters set by application
 * \param chain_index RF chain to apply settings to
 * \returns 0 in success 
 */
491
void set_rx_gain_offset(openair0_config_t *openair0_cfg, int chain_index,int bw_gain_adjust) {
492
493
494

  int i=0;
  // loop through calibration table to find best adjustment factor for RX frequency
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  double min_diff = 6e9,diff,gain_adj=0.0;
  if (bw_gain_adjust==1) {
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:      
      break;
    case 23040000:
      gain_adj=1.25;
      break;
    case 15360000:
      gain_adj=3.0;
      break;
    case 7680000:
      gain_adj=6.0;
      break;
    case 3840000:
      gain_adj=9.0;
      break;
    case 1920000:
      gain_adj=12.0;
      break;
    default:
      printf("unknown sampling rate %d\n",(int)openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
521
522
  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);
523
    printf("cal %d: freq %f, offset %f, diff %f\n",
524
525
526
	   i,
	   openair0_cfg->rx_gain_calib_table[i].freq,
	   openair0_cfg->rx_gain_calib_table[i].offset,diff);
527
528
    if (min_diff > diff) {
      min_diff = diff;
529
      openair0_cfg->rx_gain_offset[chain_index] = openair0_cfg->rx_gain_calib_table[i].offset+gain_adj;
530
531
532
533
534
535
    }
    i++;
  }
  
}

536
537
538
539
/*! \brief print the USRP statistics  
* \param device the hardware to use
* \returns  0 on success
*/
540
int trx_usrp_get_stats(openair0_device* device) {
541

542
543
544
  return(0);

}
545
546
547
548
549

/*! \brief Reset the USRP statistics  
* \param device the hardware to use
* \returns  0 on success
*/
550
int trx_usrp_reset_stats(openair0_device* device) {
551
552
553
554

  return(0);

}
555

556

557

558
extern "C" {
559
560
561
562
/*! \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
*/
563
  int device_init(openair0_device* device, openair0_config_t *openair0_cfg) {
564
    
565
566
567
568
569
    uhd::set_thread_priority_safe(1.0);
    usrp_state_t *s = (usrp_state_t*)malloc(sizeof(usrp_state_t));
    memset(s, 0, sizeof(usrp_state_t));
    
    // Initialize USRP device
knopp's avatar
 
knopp committed
570

571
  device->openair0_cfg = openair0_cfg;
knopp's avatar
   
knopp committed
572

knopp's avatar
   
knopp committed
573
  std::string args = "type=b200";
knopp's avatar
knopp committed
574
575


knopp's avatar
 
knopp committed
576
  uhd::device_addrs_t device_adds = uhd::device::find(args);
knopp's avatar
   
knopp committed
577
  size_t i;
578
579
580
581
582
  
  int vers=0,subvers=0,subsubvers=0;
  int bw_gain_adjust=0;

  sscanf(uhd::get_version_string().c_str(),"%d.%d.%d",&vers,&subvers,&subsubvers);
knopp's avatar
   
knopp committed
583

584
  printf("Checking for USRPs : UHD %s (%d.%d.%d)\n",uhd::get_version_string().c_str(),vers,subvers,subsubvers);
585
  
knopp's avatar
 
knopp committed
586
587
  if(device_adds.size() == 0)
  {
588
589
590
591
592
593
594
    double usrp_master_clock = 184.32e6;

    std::string args = "type=x300";
    
    // workaround for an api problem, master clock has to be set with the constructor not via set_master_clock_rate
    args += boost::str(boost::format(",master_clock_rate=%f") % usrp_master_clock);
    
knopp's avatar
knopp committed
595
//    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=4096, recv_frame_size=4096";
596
    
597
    uhd::device_addrs_t device_adds = uhd::device::find(args);
knopp's avatar
 
knopp committed
598

599
600
601
602
603
    if(device_adds.size() == 0)
    {
      std::cerr<<"No USRP Device Found. " << std::endl;
      free(s);
      return -1;
knopp's avatar
knopp committed
604

605
    }
knopp's avatar
knopp committed
606

607

608
609
610
611
612
613
    printf("Found USRP X300\n");
    s->usrp = uhd::usrp::multi_usrp::make(args);
    //  s->usrp->set_rx_subdev_spec(rx_subdev);
    //  s->usrp->set_tx_subdev_spec(tx_subdev);

    // lock mboard clocks
614
615
616
617
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");
618
    
619
    //Setting device type to USRP X300/X310 
620
    device->type=USRP_X300_DEV;
621

622
623
624
    // this is not working yet, master clock has to be set via constructor
    // set master clock rate and sample rate for tx & rx for streaming
    //s->usrp->set_master_clock_rate(usrp_master_clock);
625
626

    openair0_cfg[0].rx_gain_calib_table = calib_table_x310;
627
    
628
629
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
630
            // from usrp_time_offset
631
      //openair0_cfg[0].samples_per_packet    = 2048;
632
      openair0_cfg[0].tx_sample_advance     = 15;
633
634
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
635
      break;
636
    case 15360000:
637
      //openair0_cfg[0].samples_per_packet    = 2048;
638
      openair0_cfg[0].tx_sample_advance     = 45;
639
640
      openair0_cfg[0].tx_bw                 = 10e6;
      openair0_cfg[0].rx_bw                 = 10e6;
641
      break;
642
    case 7680000:
643
      //openair0_cfg[0].samples_per_packet    = 2048;
644
      openair0_cfg[0].tx_sample_advance     = 50;
645
646
      openair0_cfg[0].tx_bw                 = 5e6;
      openair0_cfg[0].rx_bw                 = 5e6;
647
      break;
648
    case 1920000:
649
      //openair0_cfg[0].samples_per_packet    = 2048;
650
      openair0_cfg[0].tx_sample_advance     = 50;
651
652
      openair0_cfg[0].tx_bw                 = 1.25e6;
      openair0_cfg[0].rx_bw                 = 1.25e6;
653
654
655
656
657
658
659
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }

660
661
  } else {
    printf("Found USRP B200");
662
    args += ",num_send_frames=256,num_recv_frames=256, send_frame_size=15360, recv_frame_size=15360" ; 
663
664
665
666
    s->usrp = uhd::usrp::multi_usrp::make(args);

    //  s->usrp->set_rx_subdev_spec(rx_subdev);
    //  s->usrp->set_tx_subdev_spec(tx_subdev);
667
668
669
670
    
    // do not explicitly set the clock to "internal", because this will disable the gpsdo
    //    // lock mboard clocks
    //    s->usrp->set_clock_source("internal");
671
    // set master clock rate and sample rate for tx & rx for streaming
672

673
674
675
676
677
678
    // lock mboard clocks
    if (openair0_cfg[0].clock_source == internal)
      s->usrp->set_clock_source("internal");
    else
      s->usrp->set_clock_source("external");

679
    device->type = USRP_B200_DEV;
680

knopp's avatar
knopp committed
681

682
683
684
685
686
687
688
689
    if ((vers == 3) && (subvers == 9) && (subsubvers>=2)) {
      openair0_cfg[0].rx_gain_calib_table = calib_table_b210;
      bw_gain_adjust=0;
    }
    else {
      openair0_cfg[0].rx_gain_calib_table = calib_table_b210_38;
      bw_gain_adjust=1;
    }
690

691
692
    switch ((int)openair0_cfg[0].sample_rate) {
    case 30720000:
693
      s->usrp->set_master_clock_rate(30.72e6);
694
      //openair0_cfg[0].samples_per_packet    = 1024;
695
      openair0_cfg[0].tx_sample_advance     = 115;
696
697
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
698
      break;
699
    case 23040000:
700
      s->usrp->set_master_clock_rate(23.04e6); //to be checked
701
      //openair0_cfg[0].samples_per_packet    = 1024;
702
      openair0_cfg[0].tx_sample_advance     = 113;
703
704
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
705
      break;
706
    case 15360000:
707
      s->usrp->set_master_clock_rate(30.72e06);
708
      //openair0_cfg[0].samples_per_packet    = 1024;
709
      openair0_cfg[0].tx_sample_advance     = 103; 
710
711
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
712
      break;
713
    case 7680000:
Rohit Gupta's avatar
Rohit Gupta committed
714
      s->usrp->set_master_clock_rate(30.72e6);
715
      //openair0_cfg[0].samples_per_packet    = 1024;
716
      openair0_cfg[0].tx_sample_advance     = 80;
717
718
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
719
      break;
720
    case 1920000:
721
      s->usrp->set_master_clock_rate(30.72e6);
722
      //openair0_cfg[0].samples_per_packet    = 1024;
723
      openair0_cfg[0].tx_sample_advance     = 40;
724
725
      openair0_cfg[0].tx_bw                 = 20e6;
      openair0_cfg[0].rx_bw                 = 20e6;
726
727
728
729
730
731
732
      break;
    default:
      printf("Error: unknown sampling rate %f\n",openair0_cfg[0].sample_rate);
      exit(-1);
      break;
    }
  }
knopp's avatar
knopp committed
733

734
  /* device specific */
735
736
  //openair0_cfg[0].txlaunch_wait = 1;//manage when TX processing is triggered
  //openair0_cfg[0].txlaunch_wait_slotcount = 1; //manage when TX processing is triggered
737
738
  openair0_cfg[0].iq_txshift = 4;//shift
  openair0_cfg[0].iq_rxrescale = 15;//rescale iqs
739
  
knopp's avatar
   
knopp committed
740
  for(i=0;i<s->usrp->get_rx_num_channels();i++) {
knopp's avatar
   
knopp committed
741
    if (i<openair0_cfg[0].rx_num_channels) {
knopp's avatar
knopp committed
742
      s->usrp->set_rx_rate(openair0_cfg[0].sample_rate,i);
743
744
      //s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
      //printf("Setting rx freq/gain on channel %lu/%lu : BW %f (readback %f)\n",i,s->usrp->get_rx_num_channels(),openair0_cfg[0].rx_bw/1e6,s->usrp->get_rx_bandwidth(i)/1e6);
knopp's avatar
knopp committed
745
      s->usrp->set_rx_freq(openair0_cfg[0].rx_freq[i],i);
746
      set_rx_gain_offset(&openair0_cfg[0],i,bw_gain_adjust);
747
748
749
750
751
752
753
754
755
756
757
758

      ::uhd::gain_range_t gain_range = s->usrp->get_rx_gain_range(i);
      // limit to maximum gain
      if (openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] > gain_range.stop()) {
	
        printf("RX Gain %lu too high, lower by %f dB\n",i,openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i] - gain_range.stop());
	exit(-1);
      }
      s->usrp->set_rx_gain(openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],i);
      printf("RX Gain %lu %f (%f) => %f (max %f)\n",i,
	     openair0_cfg[0].rx_gain[i],openair0_cfg[0].rx_gain_offset[i],
	     openair0_cfg[0].rx_gain[i]-openair0_cfg[0].rx_gain_offset[i],gain_range.stop());
knopp's avatar
   
knopp committed
759
760
    }
  }
knopp's avatar
   
knopp committed
761
  for(i=0;i<s->usrp->get_tx_num_channels();i++) {
knopp's avatar
   
knopp committed
762
    if (i<openair0_cfg[0].tx_num_channels) {
knopp's avatar
knopp committed
763
      s->usrp->set_tx_rate(openair0_cfg[0].sample_rate,i);
764
765
      //s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
      //printf("Setting tx freq/gain on channel %lu/%lu: BW %f (readback %f)\n",i,s->usrp->get_tx_num_channels(),openair0_cfg[0].tx_bw/1e6,s->usrp->get_tx_bandwidth(i)/1e6);
knopp's avatar
knopp committed
766
767
      s->usrp->set_tx_freq(openair0_cfg[0].tx_freq[i],i);
      s->usrp->set_tx_gain(openair0_cfg[0].tx_gain[i],i);
knopp's avatar
   
knopp committed
768
769
    }
  }
knopp's avatar
knopp committed
770
771


knopp's avatar
   
knopp committed
772
773
  // display USRP settings
  std::cout << boost::format("Actual master clock: %fMHz...") % (s->usrp->get_master_clock_rate()/1e6) << std::endl;
774
775
  
  sleep(1);
knopp's avatar
 
knopp committed
776
777

  // create tx & rx streamer
knopp's avatar
   
knopp committed
778
  uhd::stream_args_t stream_args_rx("sc16", "sc16");
779
780
781
782
  int samples=openair0_cfg[0].sample_rate;
  samples/=24000;
  //  stream_args_rx.args["spp"] = str(boost::format("%d") % samples);

knopp's avatar
   
knopp committed
783
  for (i = 0; i<openair0_cfg[0].rx_num_channels; i++)
knopp's avatar
   
knopp committed
784
785
786
787
788
789
790
    stream_args_rx.channels.push_back(i);
  s->rx_stream = s->usrp->get_rx_stream(stream_args_rx);
  std::cout << boost::format("rx_max_num_samps %u") % (s->rx_stream->get_max_num_samps()) << std::endl;
  //openair0_cfg[0].samples_per_packet = s->rx_stream->get_max_num_samps();

  uhd::stream_args_t stream_args_tx("sc16", "sc16");
  //stream_args_tx.args["spp"] = str(boost::format("%d") % 2048);//(openair0_cfg[0].tx_num_channels*openair0_cfg[0].samples_per_packet));
knopp's avatar
   
knopp committed
791
  for (i = 0; i<openair0_cfg[0].tx_num_channels; i++)
knopp's avatar
   
knopp committed
792
793
      stream_args_tx.channels.push_back(i);
  s->tx_stream = s->usrp->get_tx_stream(stream_args_tx);
knopp's avatar
   
knopp committed
794
795
  std::cout << boost::format("tx_max_num_samps %u") % (s->tx_stream->get_max_num_samps()) << std::endl;

knopp's avatar
 
knopp committed
796

797
798
799
800
801
 /* Setting TX/RX BW after streamers are created due to USRP calibration issue */
  for(i=0;i<s->usrp->get_tx_num_channels();i++) {
    if (i<openair0_cfg[0].tx_num_channels) {
      s->usrp->set_tx_bandwidth(openair0_cfg[0].tx_bw,i);
      printf("Setting tx freq/gain on channel %lu/%lu: BW %f (readback %f)\n",i,s->usrp->get_tx_num_channels(),openair0_cfg[0].tx_bw/1e6,s->usrp->get_tx_bandwidth(i)/1e6);
Rohit Gupta's avatar
Rohit Gupta committed
802
803
804
805
806
    }
  }
  for(i=0;i<s->usrp->get_rx_num_channels();i++) {
    if (i<openair0_cfg[0].rx_num_channels) {
      s->usrp->set_rx_bandwidth(openair0_cfg[0].rx_bw,i);
807
808
809
810
      printf("Setting rx freq/gain on channel %lu/%lu : BW %f (readback %f)\n",i,s->usrp->get_rx_num_channels(),openair0_cfg[0].rx_bw/1e6,s->usrp->get_rx_bandwidth(i)/1e6);
    }
  }

knopp's avatar
 
knopp committed
811
  s->usrp->set_time_now(uhd::time_spec_t(0.0));
812
 
813

knopp's avatar
knopp committed
814
815
  for (i=0;i<openair0_cfg[0].rx_num_channels;i++) {
    if (i<openair0_cfg[0].rx_num_channels) {
knopp's avatar
   
knopp committed
816
      printf("RX Channel %lu\n",i);
knopp's avatar
knopp committed
817
818
819
820
821
822
823
824
825
826
827
      std::cout << boost::format("Actual RX sample rate: %fMSps...") % (s->usrp->get_rx_rate(i)/1e6) << std::endl;
      std::cout << boost::format("Actual RX frequency: %fGHz...") % (s->usrp->get_rx_freq(i)/1e9) << std::endl;
      std::cout << boost::format("Actual RX gain: %f...") % (s->usrp->get_rx_gain(i)) << std::endl;
      std::cout << boost::format("Actual RX bandwidth: %fM...") % (s->usrp->get_rx_bandwidth(i)/1e6) << std::endl;
      std::cout << boost::format("Actual RX antenna: %s...") % (s->usrp->get_rx_antenna(i)) << std::endl;
    }
  }

  for (i=0;i<openair0_cfg[0].tx_num_channels;i++) {

    if (i<openair0_cfg[0].tx_num_channels) { 
knopp's avatar
   
knopp committed
828
      printf("TX Channel %lu\n",i);
knopp's avatar
knopp committed
829
830
831
832
833
834
835
836
      std::cout << std::endl<<boost::format("Actual TX sample rate: %fMSps...") % (s->usrp->get_tx_rate(i)/1e6) << std::endl;
      std::cout << boost::format("Actual TX frequency: %fGHz...") % (s->usrp->get_tx_freq(i)/1e9) << std::endl;
      std::cout << boost::format("Actual TX gain: %f...") % (s->usrp->get_tx_gain(i)) << std::endl;
      std::cout << boost::format("Actual TX bandwidth: %fM...") % (s->usrp->get_tx_bandwidth(i)/1e6) << std::endl;
      std::cout << boost::format("Actual TX antenna: %s...") % (s->usrp->get_tx_antenna(i)) << std::endl;
    }
  }

knopp's avatar
   
knopp committed
837
  std::cout << boost::format("Device timestamp: %f...") % (s->usrp->get_time_now().get_real_secs()) << std::endl;
knopp's avatar
 
knopp committed
838
839
840
841

  device->priv = s;
  device->trx_start_func = trx_usrp_start;
  device->trx_write_func = trx_usrp_write;
842
843
844
845
846
847
848
  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;
849
850
  device->openair0_cfg = openair0_cfg;

knopp's avatar
knopp committed
851
  s->sample_rate = openair0_cfg[0].sample_rate;
knopp's avatar
 
knopp committed
852
853
854
855
856
857
858
859
860
  // TODO:
  // init tx_forward_nsamps based usrp_time_offset ex
  if(is_equal(s->sample_rate, (double)30.72e6))
    s->tx_forward_nsamps  = 176;
  if(is_equal(s->sample_rate, (double)15.36e6))
    s->tx_forward_nsamps = 90;
  if(is_equal(s->sample_rate, (double)7.68e6))
    s->tx_forward_nsamps = 50;
  return 0;
861
  }
knopp's avatar
 
knopp committed
862
}
863
/*@}*/