blob: b3d65a422ce2c4c65d4fc3a43401e2814a82c1c4 [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00002 * Device support for Ettus Research UHD driver
3 * Written by Thomas Tsou <ttsou@vt.edu>
4 *
5 * Copyright 2010,2011 Free Software Foundation, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000021
22#include "radioDevice.h"
23#include "Threads.h"
24#include "Logger.h"
ttsoub371ed52012-01-09 18:11:34 +000025#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000026#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000027#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000028#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000029#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000030
kurtis.heimerlce317332011-11-26 03:18:39 +000031#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
Thomas Tsou635f1bf2014-02-13 14:34:10 -050035#define B2XX_CLK_RT 26e6
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010036#define E1XX_CLK_RT 52e6
Thomas Tsoufe269fe2013-10-14 23:56:51 -040037#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040038#define USRP2_BASE_RT 390625
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030039#define USRP_TX_AMPL 0.3
40#define UMTRX_TX_AMPL 0.7
Thomas Tsoue3e88142013-04-05 20:42:41 -040041#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040042
Tom Tsoueb54bdd2014-11-25 16:06:32 -080043/*
44 * UHD timeout value on streaming (re)start
45 *
46 * Allow some time for streaming to commence after the start command is issued,
47 * but consider a wait beyond one second to be a definite error condition.
48 */
49#define UHD_RESTART_TIMEOUT 1.0
50
Alexander Chemeris4d029d82014-04-19 17:25:00 +040051/*
52 * UmTRX specific settings
53 */
54#define UMTRX_VGA1_DEF -18
55
Thomas Tsou02d88d12013-04-05 15:36:30 -040056enum uhd_dev_type {
57 USRP1,
58 USRP2,
59 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050060 B200,
61 B210,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010062 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080063 E3XX,
64 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040065 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040066 NUM_USRP_TYPES,
67};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000068
Thomas Tsoue3e88142013-04-05 20:42:41 -040069struct uhd_dev_offset {
70 enum uhd_dev_type type;
Tom Tsou3f4a13f2016-05-03 19:02:24 -070071 size_t tx_sps;
72 size_t rx_sps;
Thomas Tsoue3e88142013-04-05 20:42:41 -040073 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050074 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040075};
76
kurtis.heimerl965e7572011-11-26 03:16:54 +000077/*
Tom Tsouc3129052015-08-21 18:28:52 -070078 * USRP version dependent device timings
79 */
80#ifdef USE_UHD_3_9
81#define B2XX_TIMING_1SPS 1.7153e-4
82#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070083#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsouc3129052015-08-21 18:28:52 -070084#else
85#define B2XX_TIMING_1SPS 9.9692e-5
86#define B2XX_TIMING_4SPS 6.9248e-5
Tom Tsoud2b07032016-04-26 19:28:59 -070087#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsouc3129052015-08-21 18:28:52 -070088#endif
89
90/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040091 * Tx / Rx sample offset values. In a perfect world, there is no group delay
92 * though analog components, and behaviour through digital filters exactly
93 * matches calculated values. In reality, there are unaccounted factors,
94 * which are captured in these empirically measured (using a loopback test)
95 * timing correction values.
96 *
97 * Notes:
98 * USRP1 with timestamps is not supported by UHD.
99 */
Alexander Chemeris871b8782016-03-20 20:08:57 +0300100static struct uhd_dev_offset uhd_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800101 { USRP1, 1, 1, 0.0, "USRP1 not supported" },
102 { USRP1, 4, 1, 0.0, "USRP1 not supported"},
103 { USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
104 { USRP2, 4, 1, 8.0230e-5, "N2XX 4 SPS" },
105 { B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
106 { B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
107 { B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
108 { B200, 4, 1, B2XX_TIMING_4SPS, "B200 4 SPS" },
109 { B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
110 { B210, 4, 1, B2XX_TIMING_4SPS, "B210 4 SPS" },
111 { E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
112 { E1XX, 4, 1, 6.5571e-5, "E1XX 4 SPS" },
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700113 { E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
114 { E3XX, 4, 1, 1.29231e-4, "E3XX 4 SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800115 { X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
116 { X3XX, 4, 1, 1.1264e-4, "X3XX 4 SPS"},
117 { UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
118 { UMTRX, 4, 1, 7.3846e-5, "UmTRX 4 SPS" },
Alexander Chemeris871b8782016-03-20 20:08:57 +0300119 { B200, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
Tom Tsou9bd649e2016-03-23 17:20:08 -0700120 { B210, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
Alexander Chemeris871b8782016-03-20 20:08:57 +0300121 { UMTRX, 4, 4, 5.1503e-5, "UmTRX EDGE mode (4 SPS TX/RX)" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800122};
Alexander Chemeris871b8782016-03-20 20:08:57 +0300123#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000124
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500125/*
126 * Offset handling for special cases. Currently used for UmTRX dual channel
127 * diversity receiver only.
128 */
129static struct uhd_dev_offset special_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800130 { UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
131 { UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500132};
133
Tom Tsou3b093bb2016-05-03 19:04:15 -0700134
Thomas Tsoucb69f082013-04-08 14:18:26 -0400135/*
136 * Select sample rate based on device type and requested samples-per-symbol.
137 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
138 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800139 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500140static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400141{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500142 if (diversity && (type == UMTRX)) {
143 return GSMRATE * 4;
144 } else if (diversity) {
145 LOG(ALERT) << "Diversity supported on UmTRX only";
146 return -9999.99;
147 }
148
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800149 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400150 return -9999.99;
151
152 switch (type) {
153 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800154 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400155 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400156 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400157 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500158 case B200:
159 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100160 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800161 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400162 case UMTRX:
163 return GSMRATE * sps;
164 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400165 break;
166 }
167
168 LOG(ALERT) << "Unknown device type " << type;
169 return -9999.99;
170}
171
kurtis.heimerl965e7572011-11-26 03:16:54 +0000172/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300173 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000174 or UHD style timestamps. Time conversions are handled
175 internally or accessable through the static convert calls.
176*/
177class smpl_buf {
178public:
179 /** Sample buffer constructor
180 @param len number of 32-bit samples the buffer should hold
181 @param rate sample clockrate
182 @param timestamp
183 */
184 smpl_buf(size_t len, double rate);
185 ~smpl_buf();
186
187 /** Query number of samples available for reading
188 @param timestamp time of first sample
189 @return number of available samples or error
190 */
191 ssize_t avail_smpls(TIMESTAMP timestamp) const;
192 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
193
194 /** Read and write
195 @param buf pointer to buffer
196 @param len number of samples desired to read or write
197 @param timestamp time of first stample
198 @return number of actual samples read or written or error
199 */
200 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
201 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
202 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
203 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
204
205 /** Buffer status string
206 @return a formatted string describing internal buffer state
207 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800208 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000209
210 /** Formatted error string
211 @param code an error code
212 @return a formatted error string
213 */
214 static std::string str_code(ssize_t code);
215
216 enum err_code {
217 ERROR_TIMESTAMP = -1,
218 ERROR_READ = -2,
219 ERROR_WRITE = -3,
220 ERROR_OVERFLOW = -4
221 };
222
223private:
224 uint32_t *data;
225 size_t buf_len;
226
227 double clk_rt;
228
229 TIMESTAMP time_start;
230 TIMESTAMP time_end;
231
232 size_t data_start;
233 size_t data_end;
234};
235
236/*
237 uhd_device - UHD implementation of the Device interface. Timestamped samples
238 are sent to and received from the device. An intermediate buffer
239 on the receive side collects and aligns packets of samples.
240 Events and errors such as underruns are reported asynchronously
241 by the device and received in a separate thread.
242*/
243class uhd_device : public RadioDevice {
244public:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800245 uhd_device(size_t tx_sps, size_t rx_sps,
246 size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000247 ~uhd_device();
248
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400249 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000250 bool start();
251 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800252 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500253 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400254 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000255
Thomas Tsou204a9f12013-10-29 18:34:16 -0400256 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000257 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
258
Thomas Tsou204a9f12013-10-29 18:34:16 -0400259 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000260 TIMESTAMP timestamp, bool isControl);
261
262 bool updateAlignment(TIMESTAMP timestamp);
263
Thomas Tsou204a9f12013-10-29 18:34:16 -0400264 bool setTxFreq(double wFreq, size_t chan);
265 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000266
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800267 inline TIMESTAMP initialWriteTimestamp();
268 inline TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000269
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300270 double fullScaleInputValue();
271 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000272
Thomas Tsou204a9f12013-10-29 18:34:16 -0400273 double setRxGain(double db, size_t chan);
274 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000275 double maxRxGain(void) { return rx_gain_max; }
276 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277
Thomas Tsou204a9f12013-10-29 18:34:16 -0400278 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000279 double maxTxGain(void) { return tx_gain_max; }
280 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000281
Thomas Tsou204a9f12013-10-29 18:34:16 -0400282 double getTxFreq(size_t chan);
283 double getRxFreq(size_t chan);
284 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000285
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400286 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000287 inline double numberRead() { return rx_pkt_cnt; }
288 inline double numberWritten() { return 0; }
289
290 /** Receive and process asynchronous message
291 @return true if message received or false on timeout or error
292 */
293 bool recv_async_msg();
294
kurtis.heimerld4be0742011-11-26 03:17:46 +0000295 enum err_code {
296 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800297 ERROR_TIMEOUT = -2,
298 ERROR_UNRECOVERABLE = -3,
299 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000300 };
301
kurtis.heimerl965e7572011-11-26 03:16:54 +0000302private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000303 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400304 uhd::tx_streamer::sptr tx_stream;
305 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400306 enum TxWindowType tx_window;
307 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000308
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800309 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400310 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311
Thomas Tsou204a9f12013-10-29 18:34:16 -0400312 double tx_gain_min, tx_gain_max;
313 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500314 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000315
Thomas Tsou204a9f12013-10-29 18:34:16 -0400316 std::vector<double> tx_gains, rx_gains;
317 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000318 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000319
320 bool started;
321 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000322
323 size_t rx_pkt_cnt;
324 size_t drop_cnt;
325 uhd::time_spec_t prev_ts;
326
Thomas Tsou18d3b832014-02-13 14:55:23 -0500327 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400328 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329
kurtis.heimerl02d04052011-11-26 03:17:10 +0000330 void init_gains();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700331 double get_dev_offset(bool edge, bool diversity);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400332 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400333 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000334 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000335 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000336 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000337
kurtis.heimerl965e7572011-11-26 03:16:54 +0000338 std::string str_code(uhd::rx_metadata_t metadata);
339 std::string str_code(uhd::async_metadata_t metadata);
340
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500341 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
342 bool set_freq(double freq, size_t chan, bool tx);
343
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800344 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500345 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800346 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347};
348
349void *async_event_loop(uhd_device *dev)
350{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500351 dev->setPriority(0.43);
352
kurtis.heimerl965e7572011-11-26 03:16:54 +0000353 while (1) {
354 dev->recv_async_msg();
355 pthread_testcancel();
356 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500357
358 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000359}
360
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000361/*
362 Catch and drop underrun 'U' and overrun 'O' messages from stdout
363 since we already report using the logging facility. Direct
364 everything else appropriately.
365 */
366void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
367{
368 switch (type) {
369 case uhd::msg::status:
370 LOG(INFO) << msg;
371 break;
372 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000373 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000374 break;
375 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000376 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000377 break;
378 case uhd::msg::fastpath:
379 break;
380 }
381}
382
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800383static void thread_enable_cancel(bool cancel)
384{
385 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
386 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
387}
388
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800389uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
390 size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400391 : tx_gain_min(0.0), tx_gain_max(0.0),
392 rx_gain_min(0.0), rx_gain_max(0.0),
393 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000394 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500395 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000396{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800397 this->tx_sps = tx_sps;
398 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400399 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500400 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500401 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000402}
403
404uhd_device::~uhd_device()
405{
406 stop();
407
Thomas Tsou204a9f12013-10-29 18:34:16 -0400408 for (size_t i = 0; i < rx_buffers.size(); i++)
409 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000410}
411
kurtis.heimerl24481de2011-11-26 03:17:18 +0000412void uhd_device::init_gains()
413{
414 uhd::gain_range_t range;
415
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400416 if (dev_type == UMTRX) {
417 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
418 if (gain_stages[0] == "VGA") {
419 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
420 }
421 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
422 range = usrp_dev->get_tx_gain_range();
423 tx_gain_min = range.start();
424 tx_gain_max = range.stop();
425 } else {
426 range = usrp_dev->get_tx_gain_range("VGA2");
427 tx_gain_min = UMTRX_VGA1_DEF + range.start();
428 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
429 }
430 } else {
431 range = usrp_dev->get_tx_gain_range();
432 tx_gain_min = range.start();
433 tx_gain_max = range.stop();
434 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400435 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000436
437 range = usrp_dev->get_rx_gain_range();
438 rx_gain_min = range.start();
439 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400440 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000441
Thomas Tsou204a9f12013-10-29 18:34:16 -0400442 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400443 double gain = (tx_gain_min + tx_gain_max) / 2;
444 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
445 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400446 tx_gains[i] = usrp_dev->get_tx_gain(i);
447 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000448
Thomas Tsou204a9f12013-10-29 18:34:16 -0400449 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400450 double gain = (rx_gain_min + rx_gain_max) / 2;
451 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
452 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400453 rx_gains[i] = usrp_dev->get_rx_gain(i);
454 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000455
456 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400457
kurtis.heimerl24481de2011-11-26 03:17:18 +0000458}
459
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700460double uhd_device::get_dev_offset(bool edge, bool diversity)
461{
462 struct uhd_dev_offset *offset = NULL;
463
464 /* Reject USRP1 */
465 if (dev_type == USRP1) {
466 LOG(ERR) << "Invalid device type";
467 return 0.0;
468 }
469
470 if (edge && diversity) {
471 LOG(ERR) << "Unsupported configuration";
472 return 0.0;
473 }
474
475 if (edge && (dev_type != B200) &&
476 (dev_type != B210) && (dev_type != UMTRX)) {
477 LOG(ALERT) << "EDGE is supported on B200/B210 and UmTRX only";
478 return 0.0;
479 }
480
481 /* Special cases (e.g. diversity receiver) */
482 if (diversity) {
483 if (dev_type != UMTRX) {
484 LOG(ALERT) << "Diversity on UmTRX only";
485 return 0.0;
486 }
487
488 switch (tx_sps) {
489 case 1:
490 offset = &special_offsets[0];
491 break;
492 case 4:
493 default:
494 offset = &special_offsets[1];
495 }
496 } else {
497 /* Search for matching offset value */
498 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
499 if ((dev_type == uhd_offsets[i].type) &&
500 (tx_sps == uhd_offsets[i].tx_sps) &&
501 (rx_sps == uhd_offsets[i].rx_sps)) {
502 offset = &uhd_offsets[i];
503 break;
504 }
505 }
506 }
507
508 if (!offset) {
509 LOG(ERR) << "Invalid device configuration";
510 return 0.0;
511 }
512
513 std::cout << "-- Setting " << offset->desc << std::endl;
514
515 return offset->offset;
516}
517
Thomas Tsou02d88d12013-04-05 15:36:30 -0400518int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000519{
Thomas Tsou092f7572013-04-04 17:04:39 -0400520 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000521
Thomas Tsou7e068062013-04-08 19:39:37 -0400522 try {
523 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400524 } catch (const std::exception &ex) {
525 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
526 LOG(ALERT) << ex.what();
527 return -1;
528 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000529
Thomas Tsou092f7572013-04-04 17:04:39 -0400530 actual = usrp_dev->get_master_clock_rate();
531 offset = fabs(clk_rate - actual);
532
533 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000534 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400535 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400536 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400537 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000538 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400539
540 return 0;
541}
542
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400543int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400544{
Thomas Tsou092f7572013-04-04 17:04:39 -0400545 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400546 double tx_offset, rx_offset;
547
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100548 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700549 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400550 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400551 return -1;
552 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100553 else if (dev_type == E1XX) {
554 if (set_master_clk(E1XX_CLK_RT) < 0)
555 return -1;
556 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000557
558 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400559 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400560 usrp_dev->set_tx_rate(tx_rate);
561 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400562 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400563 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400564 LOG(ALERT) << ex.what();
565 return -1;
566 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400567 this->tx_rate = usrp_dev->get_tx_rate();
568 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000569
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400570 tx_offset = fabs(this->tx_rate - tx_rate);
571 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400572 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000573 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400574 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
575 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400576 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000577 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000578
Thomas Tsou02d88d12013-04-05 15:36:30 -0400579 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000580}
581
Thomas Tsou204a9f12013-10-29 18:34:16 -0400582double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000583{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400584 if (chan >= tx_gains.size()) {
585 LOG(ALERT) << "Requested non-existent channel" << chan;
586 return 0.0f;
587 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000588
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400589 if (dev_type == UMTRX) {
590 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
591 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
592 usrp_dev->set_tx_gain(db, chan);
593 } else {
594 // New UHD versions support split configuration of
595 // Tx gain stages. We utilize this to set the gain
596 // configuration, optimal for the Tx signal quality.
597 // From our measurements, VGA1 must be 18dB plus-minus
598 // one and VGA2 is the best when 23dB or lower.
599 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
600 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
601 }
602 } else {
603 usrp_dev->set_tx_gain(db, chan);
604 }
605
Thomas Tsou204a9f12013-10-29 18:34:16 -0400606 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000607
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400608 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400609
610 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000611}
612
Thomas Tsou204a9f12013-10-29 18:34:16 -0400613double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000614{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400615 if (chan >= rx_gains.size()) {
616 LOG(ALERT) << "Requested non-existent channel " << chan;
617 return 0.0f;
618 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000619
Thomas Tsou204a9f12013-10-29 18:34:16 -0400620 usrp_dev->set_rx_gain(db, chan);
621 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000622
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400623 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400624
625 return rx_gains[chan];
626}
627
628double uhd_device::getRxGain(size_t chan)
629{
630 if (chan >= rx_gains.size()) {
631 LOG(ALERT) << "Requested non-existent channel " << chan;
632 return 0.0f;
633 }
634
635 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000636}
637
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000638/*
639 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400640 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000641 deal with the transport latency. Reject the USRP1 because UHD doesn't
642 support timestamped samples with it.
643 */
644bool uhd_device::parse_dev_type()
645{
646 std::string mboard_str, dev_str;
647 uhd::property_tree::sptr prop_tree;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700648 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
Tom Tsou283b22d2015-10-21 17:10:23 -0700649 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000650
651 prop_tree = usrp_dev->get_device()->get_tree();
652 dev_str = prop_tree->access<std::string>("/name").get();
653 mboard_str = usrp_dev->get_mboard_name();
654
655 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400656 usrp2_str = dev_str.find("USRP2");
657 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400658 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400659 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100660 e100_str = mboard_str.find("E100");
661 e110_str = mboard_str.find("E110");
Tom Tsou3b093bb2016-05-03 19:04:15 -0700662 e310_str = mboard_str.find("E310");
663 e3xx_str = mboard_str.find("E3XX");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800664 x300_str = mboard_str.find("X300");
665 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400666 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000667
668 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000669 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
670 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400671 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000672 return false;
673 }
674
Thomas Tsoucb69f082013-04-08 14:18:26 -0400675 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400676 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400677 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400678 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500679 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500680 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400681 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500682 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500683 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100684 } else if (e100_str != std::string::npos) {
685 tx_window = TX_WINDOW_FIXED;
686 dev_type = E1XX;
687 } else if (e110_str != std::string::npos) {
688 tx_window = TX_WINDOW_FIXED;
689 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400690 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500691 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400692 dev_type = USRP2;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700693 } else if ((e310_str != std::string::npos) ||
694 (e3xx_str != std::string::npos)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800695 tx_window = TX_WINDOW_FIXED;
696 dev_type = E3XX;
697 } else if (x300_str != std::string::npos) {
698 tx_window = TX_WINDOW_FIXED;
699 dev_type = X3XX;
700 } else if (x310_str != std::string::npos) {
701 tx_window = TX_WINDOW_FIXED;
702 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400703 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500704 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400705 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400706 } else {
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700707 LOG(ALERT) << "Unknown UHD device type "
708 << dev_str << " " << mboard_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400709 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000710 }
711
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500712 if (tx_window == TX_WINDOW_USRP1) {
713 LOG(INFO) << "Using USRP1 type transmit window for "
714 << dev_str << " " << mboard_str;
715 } else {
716 LOG(INFO) << "Using fixed transmit window for "
717 << dev_str << " " << mboard_str;
718 }
719
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000720 return true;
721}
722
Tom Tsou3b093bb2016-05-03 19:04:15 -0700723/*
724 * Check for UHD version > 3.9.0 for E3XX support
725 */
726static bool uhd_e3xx_version_chk()
727{
728 std::string ver = uhd::get_version_string();
729 std::string major_str(ver.begin(), ver.begin() + 3);
730 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
731
732 int major_val = atoi(major_str.c_str());
733 int minor_val = atoi(minor_str.c_str());
734
735 if (major_val < 3)
736 return false;
737 if (minor_val < 9)
738 return false;
739
740 return true;
741}
742
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400743int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000744{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000745 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000746 uhd::device_addr_t addr(args);
747 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000748 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000749 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400750 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000751 }
752
753 // Use the first found device
754 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000755 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700756 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000757 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700758 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400759 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000760 }
761
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000762 // Check for a valid device type and set bus type
763 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400764 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000765
Tom Tsou3b093bb2016-05-03 19:04:15 -0700766 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
767 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
768 return -1;
769 }
770
Thomas Tsou204a9f12013-10-29 18:34:16 -0400771 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500772 if ((dev_type == B210) && (chans == 2)) {
773 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400774 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400775 usrp_dev->set_tx_subdev_spec(subdev_spec);
776 usrp_dev->set_rx_subdev_spec(subdev_spec);
777 } else if (chans != 1) {
778 LOG(ALERT) << "Invalid channel combination for device";
779 return -1;
780 }
781
782 tx_freqs.resize(chans);
783 rx_freqs.resize(chans);
784 tx_gains.resize(chans);
785 rx_gains.resize(chans);
786 rx_buffers.resize(chans);
787
Thomas Tsou010fff72013-10-16 00:31:18 -0400788 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400789 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400790
kurtis.heimerl965e7572011-11-26 03:16:54 +0000791 // Set rates
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800792 double _tx_rate = select_rate(dev_type, tx_sps);
793 double _rx_rate = select_rate(dev_type, rx_sps, diversity);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500794
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500795 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
796 return -1;
797 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400798 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000799
Alexander Chemerise1714252015-04-28 23:09:02 -0400800 // Set RF frontend bandwidth
801 if (dev_type == UMTRX) {
802 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
803 for (size_t i = 0; i < chans; i++) {
804 usrp_dev->set_tx_bandwidth(500*1000*2, i);
805 if (!diversity)
806 usrp_dev->set_rx_bandwidth(500*1000*2, i);
807 }
808 }
809
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400810 /* Create TX and RX streamers */
811 uhd::stream_args_t stream_args("sc16");
812 for (size_t i = 0; i < chans; i++)
813 stream_args.channels.push_back(i);
814
815 tx_stream = usrp_dev->get_tx_stream(stream_args);
816 rx_stream = usrp_dev->get_rx_stream(stream_args);
817
818 /* Number of samples per over-the-wire packet */
819 tx_spp = tx_stream->get_max_num_samps();
820 rx_spp = rx_stream->get_max_num_samps();
821
kurtis.heimerl965e7572011-11-26 03:16:54 +0000822 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400823 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400824 for (size_t i = 0; i < rx_buffers.size(); i++)
825 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000826
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800827 // Set receive chain sample offset. Trigger the EDGE offset
828 // table by checking for 4 SPS on the receive path. No other
829 // configuration supports using 4 SPS.
830 bool edge = false;
831 if (rx_sps == 4)
832 edge = true;
833
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700834 double offset = get_dev_offset(edge, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400835 if (offset == 0.0) {
836 LOG(ERR) << "Unsupported configuration, no correction applied";
837 ts_offset = 0;
838 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400839 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400840 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841
kurtis.heimerl02d04052011-11-26 03:17:10 +0000842 // Initialize and shadow gain values
843 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000844
kurtis.heimerl965e7572011-11-26 03:16:54 +0000845 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000846 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000847
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500848 if (diversity)
849 return DIVERSITY;
850
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400851 switch (dev_type) {
852 case B100:
853 return RESAMP_64M;
854 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800855 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400856 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500857 case B200:
858 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100859 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800860 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500861 default:
862 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400863 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400864
865 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000866}
867
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000868bool uhd_device::flush_recv(size_t num_pkts)
869{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000870 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000871 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800872 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000873
Thomas Tsou18d3b832014-02-13 14:55:23 -0500874 std::vector<std::vector<short> >
875 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000876
Thomas Tsou18d3b832014-02-13 14:55:23 -0500877 std::vector<short *> pkt_ptrs;
878 for (size_t i = 0; i < pkt_bufs.size(); i++)
879 pkt_ptrs.push_back(&pkt_bufs[i].front());
880
881 ts_initial = 0;
882 while (!ts_initial || (num_pkts-- > 0)) {
883 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400884 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000885 if (!num_smpls) {
886 switch (md.error_code) {
887 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800888 LOG(ALERT) << "Device timed out";
889 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000890 default:
891 continue;
892 }
893 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500894
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700895 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000896 }
897
Thomas Tsou18d3b832014-02-13 14:55:23 -0500898 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
899
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000900 return true;
901}
902
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800903bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000904{
Thomas Tsouc2839162014-03-27 13:52:58 -0400905 /* Allow 100 ms delay to align multi-channel streams */
906 double delay = 0.1;
907
kurtis.heimerl68292102011-11-26 03:17:28 +0000908 aligned = false;
909
Thomas Tsouc2839162014-03-27 13:52:58 -0400910 uhd::time_spec_t current = usrp_dev->get_time_now();
911
Thomas Tsou204a9f12013-10-29 18:34:16 -0400912 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400913 cmd.stream_now = false;
914 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400915
kurtis.heimerl68292102011-11-26 03:17:28 +0000916 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500917
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800918 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000919}
920
kurtis.heimerl965e7572011-11-26 03:16:54 +0000921bool uhd_device::start()
922{
923 LOG(INFO) << "Starting USRP...";
924
925 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000926 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927 return false;
928 }
929
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400930 // Register msg handler
931 uhd::msg::register_handler(&uhd_msg_handler);
932
kurtis.heimerl965e7572011-11-26 03:16:54 +0000933 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800934 async_event_thrd = new Thread();
935 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000936
937 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800938 if (!restart())
939 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000940
kurtis.heimerl965e7572011-11-26 03:16:54 +0000941 // Display usrp time
942 double time_now = usrp_dev->get_time_now().get_real_secs();
943 LOG(INFO) << "The current time is " << time_now << " seconds";
944
945 started = true;
946 return true;
947}
948
949bool uhd_device::stop()
950{
Thomas Tsou2c791102013-11-15 23:00:28 -0500951 if (!started)
952 return false;
953
954 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000955 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
956
957 usrp_dev->issue_stream_cmd(stream_cmd);
958
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800959 async_event_thrd->cancel();
960 async_event_thrd->join();
961 delete async_event_thrd;
962
kurtis.heimerl965e7572011-11-26 03:16:54 +0000963 started = false;
964 return true;
965}
966
Thomas Tsou7553aa92013-11-08 12:50:03 -0500967void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000968{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500969 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970 return;
971}
972
kurtis.heimerld4be0742011-11-26 03:17:46 +0000973int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000974{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000975 uhd::time_spec_t ts;
976
kurtis.heimerld4be0742011-11-26 03:17:46 +0000977 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000978 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000979
980 switch (md.error_code) {
981 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000982 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800983 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000984 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
985 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
986 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
987 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
988 default:
989 return ERROR_UNHANDLED;
990 }
991 }
992
kurtis.heimerl965e7572011-11-26 03:16:54 +0000993 // Missing timestamp
994 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000995 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000996 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997 }
998
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000999 ts = md.time_spec;
1000
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001002 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +00001003 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +00001004 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
1005 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001006 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001008 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001009 }
1010
1011 return 0;
1012}
1013
Thomas Tsou204a9f12013-10-29 18:34:16 -04001014int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
1015 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001016{
1017 ssize_t rc;
1018 uhd::time_spec_t ts;
1019 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001020
Thomas Tsou204a9f12013-10-29 18:34:16 -04001021 if (bufs.size() != chans) {
1022 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1023 return -1;
1024 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001025
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001026 *overrun = false;
1027 *underrun = false;
1028
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029 // Shift read time with respect to transmit clock
1030 timestamp += ts_offset;
1031
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001032 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001033 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001034
1035 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001036 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001037 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001038 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001039 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001040 return 0;
1041 }
1042
Thomas Tsou204a9f12013-10-29 18:34:16 -04001043 // Create vector buffer
1044 std::vector<std::vector<short> >
1045 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1046
1047 std::vector<short *> pkt_ptrs;
1048 for (size_t i = 0; i < pkt_bufs.size(); i++)
1049 pkt_ptrs.push_back(&pkt_bufs[i].front());
1050
kurtis.heimerl965e7572011-11-26 03:16:54 +00001051 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001052 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001053 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1055 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001056 thread_enable_cancel(true);
1057
kurtis.heimerl965e7572011-11-26 03:16:54 +00001058 rx_pkt_cnt++;
1059
kurtis.heimerld4be0742011-11-26 03:17:46 +00001060 // Check for errors
1061 rc = check_rx_md_err(metadata, num_smpls);
1062 switch (rc) {
1063 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001064 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1065 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001066 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001067 case ERROR_TIMEOUT:
1068 // Assume stopping condition
1069 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001070 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001071 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001072 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001073 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001074 }
1075
kurtis.heimerl965e7572011-11-26 03:16:54 +00001076 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001077 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001078
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079 for (size_t i = 0; i < rx_buffers.size(); i++) {
1080 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1081 num_smpls,
1082 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001083
Thomas Tsou204a9f12013-10-29 18:34:16 -04001084 // Continue on local overrun, exit on other errors
1085 if ((rc < 0)) {
1086 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001087 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001088 if (rc != smpl_buf::ERROR_OVERFLOW)
1089 return 0;
1090 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001091 }
1092 }
1093
1094 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001095 for (size_t i = 0; i < rx_buffers.size(); i++) {
1096 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1097 if ((rc < 0) || (rc != len)) {
1098 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001099 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001100 return 0;
1101 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001102 }
1103
1104 return len;
1105}
1106
Thomas Tsou204a9f12013-10-29 18:34:16 -04001107int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1108 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001109{
1110 uhd::tx_metadata_t metadata;
1111 metadata.has_time_spec = true;
1112 metadata.start_of_burst = false;
1113 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001114 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001115
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001116 *underrun = false;
1117
kurtis.heimerl965e7572011-11-26 03:16:54 +00001118 // No control packets
1119 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001120 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001121 return 0;
1122 }
1123
Thomas Tsou204a9f12013-10-29 18:34:16 -04001124 if (bufs.size() != chans) {
1125 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1126 return -1;
1127 }
1128
kurtis.heimerl965e7572011-11-26 03:16:54 +00001129 // Drop a fixed number of packets (magic value)
1130 if (!aligned) {
1131 drop_cnt++;
1132
1133 if (drop_cnt == 1) {
1134 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001135 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001136 metadata.end_of_burst = true;
1137 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001138 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001139 return len;
1140 } else {
1141 LOG(DEBUG) << "Aligning transmitter: start burst";
1142 metadata.start_of_burst = true;
1143 aligned = true;
1144 drop_cnt = 0;
1145 }
1146 }
1147
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001148 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001149 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001150 thread_enable_cancel(true);
1151
ttsoub371ed52012-01-09 18:11:34 +00001152 if (num_smpls != (unsigned) len) {
1153 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001154 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001155
1156 return num_smpls;
1157}
1158
1159bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1160{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001161 return true;
1162}
1163
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001164uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1165{
1166 double rf_spread, rf_freq;
1167 std::vector<double> freqs;
1168 uhd::tune_request_t treq(freq);
1169
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001170 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001171 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001172 return uhd::tune_request_t(freq, offset);
1173
1174 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1175 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1176 // only distort the signal (because cordic is not ideal).
1177 treq.target_freq = freq;
1178 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1179 treq.rf_freq = freq;
1180 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1181 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001182 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001183 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001184 if (offset == 0.0)
1185 return treq;
1186
1187 return uhd::tune_request_t(freq, offset);
1188 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001189 LOG(ALERT) << chans << " channels unsupported";
1190 return treq;
1191 }
1192
1193 if (tx)
1194 freqs = tx_freqs;
1195 else
1196 freqs = rx_freqs;
1197
1198 /* Tune directly if other channel isn't tuned */
1199 if (freqs[!chan] < 10.0)
1200 return treq;
1201
1202 /* Find center frequency between channels */
1203 rf_spread = fabs(freqs[!chan] - freq);
1204 if (rf_spread > B2XX_CLK_RT) {
1205 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1206 return treq;
1207 }
1208
1209 rf_freq = (freqs[!chan] + freq) / 2.0f;
1210
1211 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1212 treq.target_freq = freq;
1213 treq.rf_freq = rf_freq;
1214
1215 return treq;
1216}
1217
1218bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1219{
1220 std::vector<double> freqs;
1221 uhd::tune_result_t tres;
1222 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1223
1224 if (tx) {
1225 tres = usrp_dev->set_tx_freq(treq, chan);
1226 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1227 } else {
1228 tres = usrp_dev->set_rx_freq(treq, chan);
1229 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1230 }
1231 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1232
Thomas Tsou8e17df72014-03-06 14:16:11 -05001233 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1234 return true;
1235
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001236 /* Manual RF policy means we intentionally tuned with a baseband
1237 * offset for dual-channel purposes. Now retune the other channel
1238 * with the opposite corresponding frequency offset
1239 */
1240 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1241 if (tx) {
1242 treq = select_freq(tx_freqs[!chan], !chan, true);
1243 tres = usrp_dev->set_tx_freq(treq, !chan);
1244 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1245 } else {
1246 treq = select_freq(rx_freqs[!chan], !chan, false);
1247 tres = usrp_dev->set_rx_freq(treq, !chan);
1248 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1249
1250 }
1251 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1252 }
1253
1254 return true;
1255}
1256
Thomas Tsou204a9f12013-10-29 18:34:16 -04001257bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001258{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001259 if (chan >= tx_freqs.size()) {
1260 LOG(ALERT) << "Requested non-existent channel " << chan;
1261 return false;
1262 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001263 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001264
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001265 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001266}
1267
Thomas Tsou204a9f12013-10-29 18:34:16 -04001268bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001269{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001270 if (chan >= rx_freqs.size()) {
1271 LOG(ALERT) << "Requested non-existent channel " << chan;
1272 return false;
1273 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001274 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001275
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001276 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001277}
1278
Thomas Tsou204a9f12013-10-29 18:34:16 -04001279double uhd_device::getTxFreq(size_t chan)
1280{
1281 if (chan >= tx_freqs.size()) {
1282 LOG(ALERT) << "Requested non-existent channel " << chan;
1283 return 0.0;
1284 }
1285
1286 return tx_freqs[chan];
1287}
1288
1289double uhd_device::getRxFreq(size_t chan)
1290{
1291 if (chan >= rx_freqs.size()) {
1292 LOG(ALERT) << "Requested non-existent channel " << chan;
1293 return 0.0;
1294 }
1295
1296 return rx_freqs[chan];
1297}
1298
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001299/*
1300 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1301 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1302 * combination.
1303 */
1304TIMESTAMP uhd_device::initialWriteTimestamp()
1305{
1306 if (rx_sps == tx_sps)
1307 return ts_initial;
1308 else
1309 return ts_initial * tx_sps;
1310}
1311
1312TIMESTAMP uhd_device::initialReadTimestamp()
1313{
1314 return ts_initial;
1315}
1316
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001317double uhd_device::fullScaleInputValue()
1318{
1319 if (dev_type == UMTRX)
1320 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1321 else
1322 return (double) SHRT_MAX * USRP_TX_AMPL;
1323}
1324
1325double uhd_device::fullScaleOutputValue()
1326{
1327 return (double) SHRT_MAX;
1328}
1329
kurtis.heimerl965e7572011-11-26 03:16:54 +00001330bool uhd_device::recv_async_msg()
1331{
ttsou60dc4c92012-08-08 23:30:23 +00001332 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001333
1334 thread_enable_cancel(false);
1335 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1336 thread_enable_cancel(true);
1337 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001338 return false;
1339
1340 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001341 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001342 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001343
1344 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1345 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1346 LOG(ERR) << str_code(md);
1347 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001348 }
1349
1350 return true;
1351}
1352
1353std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1354{
1355 std::ostringstream ost("UHD: ");
1356
1357 switch (metadata.error_code) {
1358 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1359 ost << "No error";
1360 break;
1361 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1362 ost << "No packet received, implementation timed-out";
1363 break;
1364 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1365 ost << "A stream command was issued in the past";
1366 break;
1367 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1368 ost << "Expected another stream command";
1369 break;
1370 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1371 ost << "An internal receive buffer has filled";
1372 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001373 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1374 ost << "Multi-channel alignment failed";
1375 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001376 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1377 ost << "The packet could not be parsed";
1378 break;
1379 default:
1380 ost << "Unknown error " << metadata.error_code;
1381 }
1382
1383 if (metadata.has_time_spec)
1384 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1385
1386 return ost.str();
1387}
1388
1389std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1390{
1391 std::ostringstream ost("UHD: ");
1392
1393 switch (metadata.event_code) {
1394 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1395 ost << "A packet was successfully transmitted";
1396 break;
1397 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1398 ost << "An internal send buffer has emptied";
1399 break;
1400 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1401 ost << "Packet loss between host and device";
1402 break;
1403 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1404 ost << "Packet time was too late or too early";
1405 break;
1406 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1407 ost << "Underflow occurred inside a packet";
1408 break;
1409 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1410 ost << "Packet loss within a burst";
1411 break;
1412 default:
1413 ost << "Unknown error " << metadata.event_code;
1414 }
1415
1416 if (metadata.has_time_spec)
1417 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1418
1419 return ost.str();
1420}
1421
1422smpl_buf::smpl_buf(size_t len, double rate)
1423 : buf_len(len), clk_rt(rate),
1424 time_start(0), time_end(0), data_start(0), data_end(0)
1425{
1426 data = new uint32_t[len];
1427}
1428
1429smpl_buf::~smpl_buf()
1430{
1431 delete[] data;
1432}
1433
1434ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1435{
1436 if (timestamp < time_start)
1437 return ERROR_TIMESTAMP;
1438 else if (timestamp >= time_end)
1439 return 0;
1440 else
1441 return time_end - timestamp;
1442}
1443
1444ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1445{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001446 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001447}
1448
1449ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1450{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001451 int type_sz = 2 * sizeof(short);
1452
kurtis.heimerl965e7572011-11-26 03:16:54 +00001453 // Check for valid read
1454 if (timestamp < time_start)
1455 return ERROR_TIMESTAMP;
1456 if (timestamp >= time_end)
1457 return 0;
1458 if (len >= buf_len)
1459 return ERROR_READ;
1460
1461 // How many samples should be copied
1462 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001463 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001464 num_smpls = len;
1465
1466 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001467 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001468
1469 // Read it
1470 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001471 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001472 memcpy(buf, data + read_start, numBytes);
1473 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001474 size_t first_cp = (buf_len - read_start) * type_sz;
1475 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001476
1477 memcpy(buf, data + read_start, first_cp);
1478 memcpy((char*) buf + first_cp, data, second_cp);
1479 }
1480
1481 data_start = (read_start + len) % buf_len;
1482 time_start = timestamp + len;
1483
1484 if (time_start > time_end)
1485 return ERROR_READ;
1486 else
1487 return num_smpls;
1488}
1489
1490ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1491{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001492 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001493}
1494
1495ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1496{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001497 int type_sz = 2 * sizeof(short);
1498
kurtis.heimerl965e7572011-11-26 03:16:54 +00001499 // Check for valid write
1500 if ((len == 0) || (len >= buf_len))
1501 return ERROR_WRITE;
1502 if ((timestamp + len) <= time_end)
1503 return ERROR_TIMESTAMP;
1504
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001505 if (timestamp < time_end) {
1506 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001507 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1508 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001509 // Do not return error here, because it's a rounding error and is not fatal
1510 }
1511 if (timestamp > time_end && time_end != 0) {
1512 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001513 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1514 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001515 // Do not return error here, because it's a rounding error and is not fatal
1516 }
1517
kurtis.heimerl965e7572011-11-26 03:16:54 +00001518 // Starting index
1519 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1520
1521 // Write it
1522 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001523 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001524 memcpy(data + write_start, buf, numBytes);
1525 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001526 size_t first_cp = (buf_len - write_start) * type_sz;
1527 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001528
1529 memcpy(data + write_start, buf, first_cp);
1530 memcpy(data, (char*) buf + first_cp, second_cp);
1531 }
1532
1533 data_end = (write_start + len) % buf_len;
1534 time_end = timestamp + len;
1535
Thomas Tsou18d3b832014-02-13 14:55:23 -05001536 if (!data_start)
1537 data_start = write_start;
1538
kurtis.heimerl965e7572011-11-26 03:16:54 +00001539 if (((write_start + len) > buf_len) && (data_end > data_start))
1540 return ERROR_OVERFLOW;
1541 else if (time_end <= time_start)
1542 return ERROR_WRITE;
1543 else
1544 return len;
1545}
1546
1547ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1548{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001549 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001550}
1551
Tom Tsou1ae25562014-12-05 12:56:34 -08001552std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001553{
1554 std::ostringstream ost("Sample buffer: ");
1555
Tom Tsou1ae25562014-12-05 12:56:34 -08001556 ost << "timestamp = " << ts;
1557 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001558 ost << ", time_start = " << time_start;
1559 ost << ", time_end = " << time_end;
1560 ost << ", data_start = " << data_start;
1561 ost << ", data_end = " << data_end;
1562
1563 return ost.str();
1564}
1565
1566std::string smpl_buf::str_code(ssize_t code)
1567{
1568 switch (code) {
1569 case ERROR_TIMESTAMP:
1570 return "Sample buffer: Requested timestamp is not valid";
1571 case ERROR_READ:
1572 return "Sample buffer: Read error";
1573 case ERROR_WRITE:
1574 return "Sample buffer: Write error";
1575 case ERROR_OVERFLOW:
1576 return "Sample buffer: Overrun";
1577 default:
1578 return "Sample buffer: Unknown error";
1579 }
1580}
1581
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001582RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
1583 size_t chans, bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001584{
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001585 return new uhd_device(tx_sps, rx_sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001586}