blob: f63aa565dfcc40ede2026eb578d10a0335b555bb [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 Tsou5cd70dc2016-03-06 01:28:40 -080071 int tx_sps;
72 int 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" },
113 { E3XX, 1, 1, 1.5000e-4, "E3XX 1 SPS" },
114 { E3XX, 4, 1, 1.2740e-4, "E3XX 4 SPS" },
115 { 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
Alexander Chemeris871b8782016-03-20 20:08:57 +0300134static double get_dev_offset(enum uhd_dev_type type, int tx_sps, int rx_sps,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800135 bool edge = false, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -0400136{
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800137 struct uhd_dev_offset *offset = NULL;
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500138
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500139 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -0400140 if (type == USRP1) {
141 LOG(ERR) << "Invalid device type";
142 return 0.0;
143 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000144
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800145 if (edge && diversity) {
146 LOG(ERR) << "Unsupported configuration";
147 return 0.0;
148 }
149
Alexander Chemeris871b8782016-03-20 20:08:57 +0300150 if (edge && (type != B200) && (type != B210) && (type != UMTRX)) {
151 LOG(ALERT) << "EDGE is supported on B200/B210 and UmTRX only";
152 return 0.0;
153 }
154
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500155 /* Special cases (e.g. diversity receiver) */
156 if (diversity) {
157 if (type != UMTRX) {
158 LOG(ALERT) << "Diversity on UmTRX only";
159 return 0.0;
160 }
161
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800162 switch (tx_sps) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500163 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500164 offset = &special_offsets[0];
165 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500166 case 4:
167 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500168 offset = &special_offsets[1];
169 }
170 } else {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800171 /* Search for matching offset value */
Alexander Chemeris871b8782016-03-20 20:08:57 +0300172 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800173 if ((type == uhd_offsets[i].type) &&
Alexander Chemeris871b8782016-03-20 20:08:57 +0300174 (tx_sps == uhd_offsets[i].tx_sps) &&
175 (rx_sps == uhd_offsets[i].rx_sps)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800176 offset = &uhd_offsets[i];
177 break;
178 }
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500179 }
180 }
181
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800182 if (!offset) {
183 LOG(ERR) << "Invalid device configuration";
184 return 0.0;
185 }
186
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500187 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000188
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500189 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400190}
kurtis.heimerlce317332011-11-26 03:18:39 +0000191
Thomas Tsoucb69f082013-04-08 14:18:26 -0400192/*
193 * Select sample rate based on device type and requested samples-per-symbol.
194 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
195 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800196 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500197static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400198{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500199 if (diversity && (type == UMTRX)) {
200 return GSMRATE * 4;
201 } else if (diversity) {
202 LOG(ALERT) << "Diversity supported on UmTRX only";
203 return -9999.99;
204 }
205
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800206 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400207 return -9999.99;
208
209 switch (type) {
210 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800211 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400212 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400213 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400214 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500215 case B200:
216 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100217 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800218 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400219 case UMTRX:
220 return GSMRATE * sps;
221 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400222 break;
223 }
224
225 LOG(ALERT) << "Unknown device type " << type;
226 return -9999.99;
227}
228
kurtis.heimerl965e7572011-11-26 03:16:54 +0000229/*
230 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
231 or UHD style timestamps. Time conversions are handled
232 internally or accessable through the static convert calls.
233*/
234class smpl_buf {
235public:
236 /** Sample buffer constructor
237 @param len number of 32-bit samples the buffer should hold
238 @param rate sample clockrate
239 @param timestamp
240 */
241 smpl_buf(size_t len, double rate);
242 ~smpl_buf();
243
244 /** Query number of samples available for reading
245 @param timestamp time of first sample
246 @return number of available samples or error
247 */
248 ssize_t avail_smpls(TIMESTAMP timestamp) const;
249 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
250
251 /** Read and write
252 @param buf pointer to buffer
253 @param len number of samples desired to read or write
254 @param timestamp time of first stample
255 @return number of actual samples read or written or error
256 */
257 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
258 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
259 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
260 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
261
262 /** Buffer status string
263 @return a formatted string describing internal buffer state
264 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800265 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000266
267 /** Formatted error string
268 @param code an error code
269 @return a formatted error string
270 */
271 static std::string str_code(ssize_t code);
272
273 enum err_code {
274 ERROR_TIMESTAMP = -1,
275 ERROR_READ = -2,
276 ERROR_WRITE = -3,
277 ERROR_OVERFLOW = -4
278 };
279
280private:
281 uint32_t *data;
282 size_t buf_len;
283
284 double clk_rt;
285
286 TIMESTAMP time_start;
287 TIMESTAMP time_end;
288
289 size_t data_start;
290 size_t data_end;
291};
292
293/*
294 uhd_device - UHD implementation of the Device interface. Timestamped samples
295 are sent to and received from the device. An intermediate buffer
296 on the receive side collects and aligns packets of samples.
297 Events and errors such as underruns are reported asynchronously
298 by the device and received in a separate thread.
299*/
300class uhd_device : public RadioDevice {
301public:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800302 uhd_device(size_t tx_sps, size_t rx_sps,
303 size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000304 ~uhd_device();
305
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400306 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000307 bool start();
308 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800309 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500310 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400311 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000312
Thomas Tsou204a9f12013-10-29 18:34:16 -0400313 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000314 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
315
Thomas Tsou204a9f12013-10-29 18:34:16 -0400316 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000317 TIMESTAMP timestamp, bool isControl);
318
319 bool updateAlignment(TIMESTAMP timestamp);
320
Thomas Tsou204a9f12013-10-29 18:34:16 -0400321 bool setTxFreq(double wFreq, size_t chan);
322 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000323
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800324 inline TIMESTAMP initialWriteTimestamp();
325 inline TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300327 double fullScaleInputValue();
328 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329
Thomas Tsou204a9f12013-10-29 18:34:16 -0400330 double setRxGain(double db, size_t chan);
331 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000332 double maxRxGain(void) { return rx_gain_max; }
333 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000334
Thomas Tsou204a9f12013-10-29 18:34:16 -0400335 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000336 double maxTxGain(void) { return tx_gain_max; }
337 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000338
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 double getTxFreq(size_t chan);
340 double getRxFreq(size_t chan);
341 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400343 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000344 inline double numberRead() { return rx_pkt_cnt; }
345 inline double numberWritten() { return 0; }
346
347 /** Receive and process asynchronous message
348 @return true if message received or false on timeout or error
349 */
350 bool recv_async_msg();
351
kurtis.heimerld4be0742011-11-26 03:17:46 +0000352 enum err_code {
353 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800354 ERROR_TIMEOUT = -2,
355 ERROR_UNRECOVERABLE = -3,
356 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000357 };
358
kurtis.heimerl965e7572011-11-26 03:16:54 +0000359private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000360 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400361 uhd::tx_streamer::sptr tx_stream;
362 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400363 enum TxWindowType tx_window;
364 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000365
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800366 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400367 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368
Thomas Tsou204a9f12013-10-29 18:34:16 -0400369 double tx_gain_min, tx_gain_max;
370 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500371 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000372
Thomas Tsou204a9f12013-10-29 18:34:16 -0400373 std::vector<double> tx_gains, rx_gains;
374 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000375 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376
377 bool started;
378 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000379
380 size_t rx_pkt_cnt;
381 size_t drop_cnt;
382 uhd::time_spec_t prev_ts;
383
Thomas Tsou18d3b832014-02-13 14:55:23 -0500384 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400385 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000386
kurtis.heimerl02d04052011-11-26 03:17:10 +0000387 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400388 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400389 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000390 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000391 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000392 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000393
kurtis.heimerl965e7572011-11-26 03:16:54 +0000394 std::string str_code(uhd::rx_metadata_t metadata);
395 std::string str_code(uhd::async_metadata_t metadata);
396
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500397 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
398 bool set_freq(double freq, size_t chan, bool tx);
399
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800400 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500401 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800402 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000403};
404
405void *async_event_loop(uhd_device *dev)
406{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500407 dev->setPriority(0.43);
408
kurtis.heimerl965e7572011-11-26 03:16:54 +0000409 while (1) {
410 dev->recv_async_msg();
411 pthread_testcancel();
412 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500413
414 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000415}
416
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000417/*
418 Catch and drop underrun 'U' and overrun 'O' messages from stdout
419 since we already report using the logging facility. Direct
420 everything else appropriately.
421 */
422void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
423{
424 switch (type) {
425 case uhd::msg::status:
426 LOG(INFO) << msg;
427 break;
428 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000429 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000430 break;
431 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000432 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000433 break;
434 case uhd::msg::fastpath:
435 break;
436 }
437}
438
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800439static void thread_enable_cancel(bool cancel)
440{
441 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
442 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
443}
444
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800445uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
446 size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400447 : tx_gain_min(0.0), tx_gain_max(0.0),
448 rx_gain_min(0.0), rx_gain_max(0.0),
449 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000450 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500451 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000452{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800453 this->tx_sps = tx_sps;
454 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500456 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500457 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000458}
459
460uhd_device::~uhd_device()
461{
462 stop();
463
Thomas Tsou204a9f12013-10-29 18:34:16 -0400464 for (size_t i = 0; i < rx_buffers.size(); i++)
465 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000466}
467
kurtis.heimerl24481de2011-11-26 03:17:18 +0000468void uhd_device::init_gains()
469{
470 uhd::gain_range_t range;
471
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400472 if (dev_type == UMTRX) {
473 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
474 if (gain_stages[0] == "VGA") {
475 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
476 }
477 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
478 range = usrp_dev->get_tx_gain_range();
479 tx_gain_min = range.start();
480 tx_gain_max = range.stop();
481 } else {
482 range = usrp_dev->get_tx_gain_range("VGA2");
483 tx_gain_min = UMTRX_VGA1_DEF + range.start();
484 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
485 }
486 } else {
487 range = usrp_dev->get_tx_gain_range();
488 tx_gain_min = range.start();
489 tx_gain_max = range.stop();
490 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400491 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000492
493 range = usrp_dev->get_rx_gain_range();
494 rx_gain_min = range.start();
495 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400496 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000497
Thomas Tsou204a9f12013-10-29 18:34:16 -0400498 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400499 double gain = (tx_gain_min + tx_gain_max) / 2;
500 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
501 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400502 tx_gains[i] = usrp_dev->get_tx_gain(i);
503 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000504
Thomas Tsou204a9f12013-10-29 18:34:16 -0400505 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400506 double gain = (rx_gain_min + rx_gain_max) / 2;
507 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
508 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400509 rx_gains[i] = usrp_dev->get_rx_gain(i);
510 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000511
512 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400513
kurtis.heimerl24481de2011-11-26 03:17:18 +0000514}
515
Thomas Tsou02d88d12013-04-05 15:36:30 -0400516int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000517{
Thomas Tsou092f7572013-04-04 17:04:39 -0400518 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000519
Thomas Tsou7e068062013-04-08 19:39:37 -0400520 try {
521 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400522 } catch (const std::exception &ex) {
523 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
524 LOG(ALERT) << ex.what();
525 return -1;
526 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000527
Thomas Tsou092f7572013-04-04 17:04:39 -0400528 actual = usrp_dev->get_master_clock_rate();
529 offset = fabs(clk_rate - actual);
530
531 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000532 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400533 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400534 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400535 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000536 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400537
538 return 0;
539}
540
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400541int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400542{
Thomas Tsou092f7572013-04-04 17:04:39 -0400543 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400544 double tx_offset, rx_offset;
545
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100546 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700547 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400548 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400549 return -1;
550 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100551 else if (dev_type == E1XX) {
552 if (set_master_clk(E1XX_CLK_RT) < 0)
553 return -1;
554 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000555
556 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400557 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400558 usrp_dev->set_tx_rate(tx_rate);
559 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400560 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400561 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400562 LOG(ALERT) << ex.what();
563 return -1;
564 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400565 this->tx_rate = usrp_dev->get_tx_rate();
566 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000567
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400568 tx_offset = fabs(this->tx_rate - tx_rate);
569 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400570 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000571 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400572 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
573 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400574 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000575 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000576
Thomas Tsou02d88d12013-04-05 15:36:30 -0400577 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000578}
579
Thomas Tsou204a9f12013-10-29 18:34:16 -0400580double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000581{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400582 if (chan >= tx_gains.size()) {
583 LOG(ALERT) << "Requested non-existent channel" << chan;
584 return 0.0f;
585 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000586
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400587 if (dev_type == UMTRX) {
588 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
589 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
590 usrp_dev->set_tx_gain(db, chan);
591 } else {
592 // New UHD versions support split configuration of
593 // Tx gain stages. We utilize this to set the gain
594 // configuration, optimal for the Tx signal quality.
595 // From our measurements, VGA1 must be 18dB plus-minus
596 // one and VGA2 is the best when 23dB or lower.
597 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
598 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
599 }
600 } else {
601 usrp_dev->set_tx_gain(db, chan);
602 }
603
Thomas Tsou204a9f12013-10-29 18:34:16 -0400604 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000605
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400606 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400607
608 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000609}
610
Thomas Tsou204a9f12013-10-29 18:34:16 -0400611double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000612{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400613 if (chan >= rx_gains.size()) {
614 LOG(ALERT) << "Requested non-existent channel " << chan;
615 return 0.0f;
616 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000617
Thomas Tsou204a9f12013-10-29 18:34:16 -0400618 usrp_dev->set_rx_gain(db, chan);
619 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000620
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400621 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400622
623 return rx_gains[chan];
624}
625
626double uhd_device::getRxGain(size_t chan)
627{
628 if (chan >= rx_gains.size()) {
629 LOG(ALERT) << "Requested non-existent channel " << chan;
630 return 0.0f;
631 }
632
633 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000634}
635
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000636/*
637 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400638 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000639 deal with the transport latency. Reject the USRP1 because UHD doesn't
640 support timestamped samples with it.
641 */
642bool uhd_device::parse_dev_type()
643{
644 std::string mboard_str, dev_str;
645 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800646 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
Tom Tsou283b22d2015-10-21 17:10:23 -0700647 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000648
649 prop_tree = usrp_dev->get_device()->get_tree();
650 dev_str = prop_tree->access<std::string>("/name").get();
651 mboard_str = usrp_dev->get_mboard_name();
652
653 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400654 usrp2_str = dev_str.find("USRP2");
655 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400656 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400657 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100658 e100_str = mboard_str.find("E100");
659 e110_str = mboard_str.find("E110");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800660 e310_str = mboard_str.find("E310");
661 x300_str = mboard_str.find("X300");
662 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400663 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000664
665 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000666 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
667 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400668 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000669 return false;
670 }
671
Thomas Tsoucb69f082013-04-08 14:18:26 -0400672 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400673 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400674 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400675 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500676 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500677 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400678 } else if (b210_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 = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100681 } else if (e100_str != std::string::npos) {
682 tx_window = TX_WINDOW_FIXED;
683 dev_type = E1XX;
684 } else if (e110_str != std::string::npos) {
685 tx_window = TX_WINDOW_FIXED;
686 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400687 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500688 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400689 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800690 } else if (e310_str != std::string::npos) {
691 tx_window = TX_WINDOW_FIXED;
692 dev_type = E3XX;
693 } else if (x300_str != std::string::npos) {
694 tx_window = TX_WINDOW_FIXED;
695 dev_type = X3XX;
696 } else if (x310_str != std::string::npos) {
697 tx_window = TX_WINDOW_FIXED;
698 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400699 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500700 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400701 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400702 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400703 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400704 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000705 }
706
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500707 if (tx_window == TX_WINDOW_USRP1) {
708 LOG(INFO) << "Using USRP1 type transmit window for "
709 << dev_str << " " << mboard_str;
710 } else {
711 LOG(INFO) << "Using fixed transmit window for "
712 << dev_str << " " << mboard_str;
713 }
714
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000715 return true;
716}
717
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400718int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000719{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000720 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000721 uhd::device_addr_t addr(args);
722 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000723 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000724 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400725 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000726 }
727
728 // Use the first found device
729 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700731 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000732 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700733 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400734 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000735 }
736
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000737 // Check for a valid device type and set bus type
738 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400739 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000740
Thomas Tsou204a9f12013-10-29 18:34:16 -0400741 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500742 if ((dev_type == B210) && (chans == 2)) {
743 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400744 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400745 usrp_dev->set_tx_subdev_spec(subdev_spec);
746 usrp_dev->set_rx_subdev_spec(subdev_spec);
747 } else if (chans != 1) {
748 LOG(ALERT) << "Invalid channel combination for device";
749 return -1;
750 }
751
752 tx_freqs.resize(chans);
753 rx_freqs.resize(chans);
754 tx_gains.resize(chans);
755 rx_gains.resize(chans);
756 rx_buffers.resize(chans);
757
Thomas Tsou010fff72013-10-16 00:31:18 -0400758 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400759 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400760
kurtis.heimerl965e7572011-11-26 03:16:54 +0000761 // Set rates
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800762 double _tx_rate = select_rate(dev_type, tx_sps);
763 double _rx_rate = select_rate(dev_type, rx_sps, diversity);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500764
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500765 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
766 return -1;
767 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400768 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000769
Alexander Chemerise1714252015-04-28 23:09:02 -0400770 // Set RF frontend bandwidth
771 if (dev_type == UMTRX) {
772 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
773 for (size_t i = 0; i < chans; i++) {
774 usrp_dev->set_tx_bandwidth(500*1000*2, i);
775 if (!diversity)
776 usrp_dev->set_rx_bandwidth(500*1000*2, i);
777 }
778 }
779
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400780 /* Create TX and RX streamers */
781 uhd::stream_args_t stream_args("sc16");
782 for (size_t i = 0; i < chans; i++)
783 stream_args.channels.push_back(i);
784
785 tx_stream = usrp_dev->get_tx_stream(stream_args);
786 rx_stream = usrp_dev->get_rx_stream(stream_args);
787
788 /* Number of samples per over-the-wire packet */
789 tx_spp = tx_stream->get_max_num_samps();
790 rx_spp = rx_stream->get_max_num_samps();
791
kurtis.heimerl965e7572011-11-26 03:16:54 +0000792 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400793 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400794 for (size_t i = 0; i < rx_buffers.size(); i++)
795 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000796
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800797 // Set receive chain sample offset. Trigger the EDGE offset
798 // table by checking for 4 SPS on the receive path. No other
799 // configuration supports using 4 SPS.
800 bool edge = false;
801 if (rx_sps == 4)
802 edge = true;
803
Alexander Chemeris871b8782016-03-20 20:08:57 +0300804 double offset = get_dev_offset(dev_type, tx_sps, rx_sps, edge, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400805 if (offset == 0.0) {
806 LOG(ERR) << "Unsupported configuration, no correction applied";
807 ts_offset = 0;
808 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400809 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400810 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000811
kurtis.heimerl02d04052011-11-26 03:17:10 +0000812 // Initialize and shadow gain values
813 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000814
kurtis.heimerl965e7572011-11-26 03:16:54 +0000815 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000816 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000817
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500818 if (diversity)
819 return DIVERSITY;
820
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400821 switch (dev_type) {
822 case B100:
823 return RESAMP_64M;
824 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800825 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400826 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500827 case B200:
828 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100829 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800830 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500831 default:
832 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400833 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400834
835 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000836}
837
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000838bool uhd_device::flush_recv(size_t num_pkts)
839{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000840 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000841 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800842 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000843
Thomas Tsou18d3b832014-02-13 14:55:23 -0500844 std::vector<std::vector<short> >
845 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000846
Thomas Tsou18d3b832014-02-13 14:55:23 -0500847 std::vector<short *> pkt_ptrs;
848 for (size_t i = 0; i < pkt_bufs.size(); i++)
849 pkt_ptrs.push_back(&pkt_bufs[i].front());
850
851 ts_initial = 0;
852 while (!ts_initial || (num_pkts-- > 0)) {
853 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400854 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000855 if (!num_smpls) {
856 switch (md.error_code) {
857 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800858 LOG(ALERT) << "Device timed out";
859 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000860 default:
861 continue;
862 }
863 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500864
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700865 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000866 }
867
Thomas Tsou18d3b832014-02-13 14:55:23 -0500868 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
869
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000870 return true;
871}
872
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800873bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000874{
Thomas Tsouc2839162014-03-27 13:52:58 -0400875 /* Allow 100 ms delay to align multi-channel streams */
876 double delay = 0.1;
877
kurtis.heimerl68292102011-11-26 03:17:28 +0000878 aligned = false;
879
Thomas Tsouc2839162014-03-27 13:52:58 -0400880 uhd::time_spec_t current = usrp_dev->get_time_now();
881
Thomas Tsou204a9f12013-10-29 18:34:16 -0400882 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400883 cmd.stream_now = false;
884 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400885
kurtis.heimerl68292102011-11-26 03:17:28 +0000886 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500887
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800888 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000889}
890
kurtis.heimerl965e7572011-11-26 03:16:54 +0000891bool uhd_device::start()
892{
893 LOG(INFO) << "Starting USRP...";
894
895 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000896 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000897 return false;
898 }
899
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400900 // Register msg handler
901 uhd::msg::register_handler(&uhd_msg_handler);
902
kurtis.heimerl965e7572011-11-26 03:16:54 +0000903 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800904 async_event_thrd = new Thread();
905 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000906
907 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800908 if (!restart())
909 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000910
kurtis.heimerl965e7572011-11-26 03:16:54 +0000911 // Display usrp time
912 double time_now = usrp_dev->get_time_now().get_real_secs();
913 LOG(INFO) << "The current time is " << time_now << " seconds";
914
915 started = true;
916 return true;
917}
918
919bool uhd_device::stop()
920{
Thomas Tsou2c791102013-11-15 23:00:28 -0500921 if (!started)
922 return false;
923
924 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000925 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
926
927 usrp_dev->issue_stream_cmd(stream_cmd);
928
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800929 async_event_thrd->cancel();
930 async_event_thrd->join();
931 delete async_event_thrd;
932
kurtis.heimerl965e7572011-11-26 03:16:54 +0000933 started = false;
934 return true;
935}
936
Thomas Tsou7553aa92013-11-08 12:50:03 -0500937void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000938{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500939 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000940 return;
941}
942
kurtis.heimerld4be0742011-11-26 03:17:46 +0000943int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000944{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000945 uhd::time_spec_t ts;
946
kurtis.heimerld4be0742011-11-26 03:17:46 +0000947 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000948 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000949
950 switch (md.error_code) {
951 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000952 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800953 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000954 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
955 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
956 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
957 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
958 default:
959 return ERROR_UNHANDLED;
960 }
961 }
962
kurtis.heimerl965e7572011-11-26 03:16:54 +0000963 // Missing timestamp
964 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000965 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000966 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967 }
968
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000969 ts = md.time_spec;
970
kurtis.heimerl965e7572011-11-26 03:16:54 +0000971 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000972 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000973 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000974 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
975 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000976 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000977 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000978 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000979 }
980
981 return 0;
982}
983
Thomas Tsou204a9f12013-10-29 18:34:16 -0400984int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
985 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000986{
987 ssize_t rc;
988 uhd::time_spec_t ts;
989 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000990
Thomas Tsou204a9f12013-10-29 18:34:16 -0400991 if (bufs.size() != chans) {
992 LOG(ALERT) << "Invalid channel combination " << bufs.size();
993 return -1;
994 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000995
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000996 *overrun = false;
997 *underrun = false;
998
kurtis.heimerl965e7572011-11-26 03:16:54 +0000999 // Shift read time with respect to transmit clock
1000 timestamp += ts_offset;
1001
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001002 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001003 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004
1005 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001006 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001008 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001009 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001010 return 0;
1011 }
1012
Thomas Tsou204a9f12013-10-29 18:34:16 -04001013 // Create vector buffer
1014 std::vector<std::vector<short> >
1015 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1016
1017 std::vector<short *> pkt_ptrs;
1018 for (size_t i = 0; i < pkt_bufs.size(); i++)
1019 pkt_ptrs.push_back(&pkt_bufs[i].front());
1020
kurtis.heimerl965e7572011-11-26 03:16:54 +00001021 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001022 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001023 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001024 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1025 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001026 thread_enable_cancel(true);
1027
kurtis.heimerl965e7572011-11-26 03:16:54 +00001028 rx_pkt_cnt++;
1029
kurtis.heimerld4be0742011-11-26 03:17:46 +00001030 // Check for errors
1031 rc = check_rx_md_err(metadata, num_smpls);
1032 switch (rc) {
1033 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001034 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1035 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001036 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001037 case ERROR_TIMEOUT:
1038 // Assume stopping condition
1039 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001040 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001041 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001042 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001043 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001044 }
1045
kurtis.heimerl965e7572011-11-26 03:16:54 +00001046 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001047 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001048
Thomas Tsou204a9f12013-10-29 18:34:16 -04001049 for (size_t i = 0; i < rx_buffers.size(); i++) {
1050 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1051 num_smpls,
1052 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001053
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054 // Continue on local overrun, exit on other errors
1055 if ((rc < 0)) {
1056 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001057 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001058 if (rc != smpl_buf::ERROR_OVERFLOW)
1059 return 0;
1060 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001061 }
1062 }
1063
1064 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001065 for (size_t i = 0; i < rx_buffers.size(); i++) {
1066 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1067 if ((rc < 0) || (rc != len)) {
1068 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001069 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001070 return 0;
1071 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001072 }
1073
1074 return len;
1075}
1076
Thomas Tsou204a9f12013-10-29 18:34:16 -04001077int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1078 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001079{
1080 uhd::tx_metadata_t metadata;
1081 metadata.has_time_spec = true;
1082 metadata.start_of_burst = false;
1083 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001084 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001085
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001086 *underrun = false;
1087
kurtis.heimerl965e7572011-11-26 03:16:54 +00001088 // No control packets
1089 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001090 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001091 return 0;
1092 }
1093
Thomas Tsou204a9f12013-10-29 18:34:16 -04001094 if (bufs.size() != chans) {
1095 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1096 return -1;
1097 }
1098
kurtis.heimerl965e7572011-11-26 03:16:54 +00001099 // Drop a fixed number of packets (magic value)
1100 if (!aligned) {
1101 drop_cnt++;
1102
1103 if (drop_cnt == 1) {
1104 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001105 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001106 metadata.end_of_burst = true;
1107 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001108 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001109 return len;
1110 } else {
1111 LOG(DEBUG) << "Aligning transmitter: start burst";
1112 metadata.start_of_burst = true;
1113 aligned = true;
1114 drop_cnt = 0;
1115 }
1116 }
1117
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001118 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001119 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001120 thread_enable_cancel(true);
1121
ttsoub371ed52012-01-09 18:11:34 +00001122 if (num_smpls != (unsigned) len) {
1123 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001124 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001125
1126 return num_smpls;
1127}
1128
1129bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1130{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001131 return true;
1132}
1133
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001134uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1135{
1136 double rf_spread, rf_freq;
1137 std::vector<double> freqs;
1138 uhd::tune_request_t treq(freq);
1139
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001140 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001141 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001142 return uhd::tune_request_t(freq, offset);
1143
1144 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1145 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1146 // only distort the signal (because cordic is not ideal).
1147 treq.target_freq = freq;
1148 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1149 treq.rf_freq = freq;
1150 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1151 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001152 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001153 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001154 if (offset == 0.0)
1155 return treq;
1156
1157 return uhd::tune_request_t(freq, offset);
1158 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001159 LOG(ALERT) << chans << " channels unsupported";
1160 return treq;
1161 }
1162
1163 if (tx)
1164 freqs = tx_freqs;
1165 else
1166 freqs = rx_freqs;
1167
1168 /* Tune directly if other channel isn't tuned */
1169 if (freqs[!chan] < 10.0)
1170 return treq;
1171
1172 /* Find center frequency between channels */
1173 rf_spread = fabs(freqs[!chan] - freq);
1174 if (rf_spread > B2XX_CLK_RT) {
1175 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1176 return treq;
1177 }
1178
1179 rf_freq = (freqs[!chan] + freq) / 2.0f;
1180
1181 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1182 treq.target_freq = freq;
1183 treq.rf_freq = rf_freq;
1184
1185 return treq;
1186}
1187
1188bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1189{
1190 std::vector<double> freqs;
1191 uhd::tune_result_t tres;
1192 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1193
1194 if (tx) {
1195 tres = usrp_dev->set_tx_freq(treq, chan);
1196 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1197 } else {
1198 tres = usrp_dev->set_rx_freq(treq, chan);
1199 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1200 }
1201 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1202
Thomas Tsou8e17df72014-03-06 14:16:11 -05001203 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1204 return true;
1205
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001206 /* Manual RF policy means we intentionally tuned with a baseband
1207 * offset for dual-channel purposes. Now retune the other channel
1208 * with the opposite corresponding frequency offset
1209 */
1210 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1211 if (tx) {
1212 treq = select_freq(tx_freqs[!chan], !chan, true);
1213 tres = usrp_dev->set_tx_freq(treq, !chan);
1214 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1215 } else {
1216 treq = select_freq(rx_freqs[!chan], !chan, false);
1217 tres = usrp_dev->set_rx_freq(treq, !chan);
1218 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1219
1220 }
1221 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1222 }
1223
1224 return true;
1225}
1226
Thomas Tsou204a9f12013-10-29 18:34:16 -04001227bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001228{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001229 if (chan >= tx_freqs.size()) {
1230 LOG(ALERT) << "Requested non-existent channel " << chan;
1231 return false;
1232 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001233 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001234
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001235 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001236}
1237
Thomas Tsou204a9f12013-10-29 18:34:16 -04001238bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001239{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001240 if (chan >= rx_freqs.size()) {
1241 LOG(ALERT) << "Requested non-existent channel " << chan;
1242 return false;
1243 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001244 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001245
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001246 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001247}
1248
Thomas Tsou204a9f12013-10-29 18:34:16 -04001249double uhd_device::getTxFreq(size_t chan)
1250{
1251 if (chan >= tx_freqs.size()) {
1252 LOG(ALERT) << "Requested non-existent channel " << chan;
1253 return 0.0;
1254 }
1255
1256 return tx_freqs[chan];
1257}
1258
1259double uhd_device::getRxFreq(size_t chan)
1260{
1261 if (chan >= rx_freqs.size()) {
1262 LOG(ALERT) << "Requested non-existent channel " << chan;
1263 return 0.0;
1264 }
1265
1266 return rx_freqs[chan];
1267}
1268
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001269/*
1270 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1271 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1272 * combination.
1273 */
1274TIMESTAMP uhd_device::initialWriteTimestamp()
1275{
1276 if (rx_sps == tx_sps)
1277 return ts_initial;
1278 else
1279 return ts_initial * tx_sps;
1280}
1281
1282TIMESTAMP uhd_device::initialReadTimestamp()
1283{
1284 return ts_initial;
1285}
1286
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001287double uhd_device::fullScaleInputValue()
1288{
1289 if (dev_type == UMTRX)
1290 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1291 else
1292 return (double) SHRT_MAX * USRP_TX_AMPL;
1293}
1294
1295double uhd_device::fullScaleOutputValue()
1296{
1297 return (double) SHRT_MAX;
1298}
1299
kurtis.heimerl965e7572011-11-26 03:16:54 +00001300bool uhd_device::recv_async_msg()
1301{
ttsou60dc4c92012-08-08 23:30:23 +00001302 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001303
1304 thread_enable_cancel(false);
1305 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1306 thread_enable_cancel(true);
1307 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001308 return false;
1309
1310 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001311 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001312 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001313
1314 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1315 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1316 LOG(ERR) << str_code(md);
1317 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001318 }
1319
1320 return true;
1321}
1322
1323std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1324{
1325 std::ostringstream ost("UHD: ");
1326
1327 switch (metadata.error_code) {
1328 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1329 ost << "No error";
1330 break;
1331 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1332 ost << "No packet received, implementation timed-out";
1333 break;
1334 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1335 ost << "A stream command was issued in the past";
1336 break;
1337 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1338 ost << "Expected another stream command";
1339 break;
1340 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1341 ost << "An internal receive buffer has filled";
1342 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001343 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1344 ost << "Multi-channel alignment failed";
1345 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001346 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1347 ost << "The packet could not be parsed";
1348 break;
1349 default:
1350 ost << "Unknown error " << metadata.error_code;
1351 }
1352
1353 if (metadata.has_time_spec)
1354 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1355
1356 return ost.str();
1357}
1358
1359std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1360{
1361 std::ostringstream ost("UHD: ");
1362
1363 switch (metadata.event_code) {
1364 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1365 ost << "A packet was successfully transmitted";
1366 break;
1367 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1368 ost << "An internal send buffer has emptied";
1369 break;
1370 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1371 ost << "Packet loss between host and device";
1372 break;
1373 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1374 ost << "Packet time was too late or too early";
1375 break;
1376 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1377 ost << "Underflow occurred inside a packet";
1378 break;
1379 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1380 ost << "Packet loss within a burst";
1381 break;
1382 default:
1383 ost << "Unknown error " << metadata.event_code;
1384 }
1385
1386 if (metadata.has_time_spec)
1387 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1388
1389 return ost.str();
1390}
1391
1392smpl_buf::smpl_buf(size_t len, double rate)
1393 : buf_len(len), clk_rt(rate),
1394 time_start(0), time_end(0), data_start(0), data_end(0)
1395{
1396 data = new uint32_t[len];
1397}
1398
1399smpl_buf::~smpl_buf()
1400{
1401 delete[] data;
1402}
1403
1404ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1405{
1406 if (timestamp < time_start)
1407 return ERROR_TIMESTAMP;
1408 else if (timestamp >= time_end)
1409 return 0;
1410 else
1411 return time_end - timestamp;
1412}
1413
1414ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1415{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001416 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001417}
1418
1419ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1420{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001421 int type_sz = 2 * sizeof(short);
1422
kurtis.heimerl965e7572011-11-26 03:16:54 +00001423 // Check for valid read
1424 if (timestamp < time_start)
1425 return ERROR_TIMESTAMP;
1426 if (timestamp >= time_end)
1427 return 0;
1428 if (len >= buf_len)
1429 return ERROR_READ;
1430
1431 // How many samples should be copied
1432 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001433 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001434 num_smpls = len;
1435
1436 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001437 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001438
1439 // Read it
1440 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001441 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001442 memcpy(buf, data + read_start, numBytes);
1443 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001444 size_t first_cp = (buf_len - read_start) * type_sz;
1445 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001446
1447 memcpy(buf, data + read_start, first_cp);
1448 memcpy((char*) buf + first_cp, data, second_cp);
1449 }
1450
1451 data_start = (read_start + len) % buf_len;
1452 time_start = timestamp + len;
1453
1454 if (time_start > time_end)
1455 return ERROR_READ;
1456 else
1457 return num_smpls;
1458}
1459
1460ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1461{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001462 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001463}
1464
1465ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1466{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001467 int type_sz = 2 * sizeof(short);
1468
kurtis.heimerl965e7572011-11-26 03:16:54 +00001469 // Check for valid write
1470 if ((len == 0) || (len >= buf_len))
1471 return ERROR_WRITE;
1472 if ((timestamp + len) <= time_end)
1473 return ERROR_TIMESTAMP;
1474
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001475 if (timestamp < time_end) {
1476 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001477 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1478 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 -04001479 // Do not return error here, because it's a rounding error and is not fatal
1480 }
1481 if (timestamp > time_end && time_end != 0) {
1482 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001483 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1484 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 -04001485 // Do not return error here, because it's a rounding error and is not fatal
1486 }
1487
kurtis.heimerl965e7572011-11-26 03:16:54 +00001488 // Starting index
1489 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1490
1491 // Write it
1492 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001493 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001494 memcpy(data + write_start, buf, numBytes);
1495 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001496 size_t first_cp = (buf_len - write_start) * type_sz;
1497 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001498
1499 memcpy(data + write_start, buf, first_cp);
1500 memcpy(data, (char*) buf + first_cp, second_cp);
1501 }
1502
1503 data_end = (write_start + len) % buf_len;
1504 time_end = timestamp + len;
1505
Thomas Tsou18d3b832014-02-13 14:55:23 -05001506 if (!data_start)
1507 data_start = write_start;
1508
kurtis.heimerl965e7572011-11-26 03:16:54 +00001509 if (((write_start + len) > buf_len) && (data_end > data_start))
1510 return ERROR_OVERFLOW;
1511 else if (time_end <= time_start)
1512 return ERROR_WRITE;
1513 else
1514 return len;
1515}
1516
1517ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1518{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001519 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001520}
1521
Tom Tsou1ae25562014-12-05 12:56:34 -08001522std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001523{
1524 std::ostringstream ost("Sample buffer: ");
1525
Tom Tsou1ae25562014-12-05 12:56:34 -08001526 ost << "timestamp = " << ts;
1527 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001528 ost << ", time_start = " << time_start;
1529 ost << ", time_end = " << time_end;
1530 ost << ", data_start = " << data_start;
1531 ost << ", data_end = " << data_end;
1532
1533 return ost.str();
1534}
1535
1536std::string smpl_buf::str_code(ssize_t code)
1537{
1538 switch (code) {
1539 case ERROR_TIMESTAMP:
1540 return "Sample buffer: Requested timestamp is not valid";
1541 case ERROR_READ:
1542 return "Sample buffer: Read error";
1543 case ERROR_WRITE:
1544 return "Sample buffer: Write error";
1545 case ERROR_OVERFLOW:
1546 return "Sample buffer: Overrun";
1547 default:
1548 return "Sample buffer: Unknown error";
1549 }
1550}
1551
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001552RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
1553 size_t chans, bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001554{
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001555 return new uhd_device(tx_sps, rx_sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001556}