blob: af844f60cfd7dfc861d8c8e35956f998c00ffc65 [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 Tsou5cd70dc2016-03-06 01:28:40 -080083#define B2XX_TIMING_4_4SPS 5.89578e-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 Tsou5cd70dc2016-03-06 01:28:40 -080087#define B2XX_TIMING_4_4SPS 4.19034e-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 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800100static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
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" },
119};
120
121struct uhd_dev_offset edge_offset = {
122 .type = B200,
123 .tx_sps = 4,
124 .rx_sps = 4,
125 .offset = B2XX_TIMING_4_4SPS,
126 .desc = "B200/B210 EDGE mode (4 SPS TX/RX)",
Thomas Tsoue3e88142013-04-05 20:42:41 -0400127};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000128
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500129/*
130 * Offset handling for special cases. Currently used for UmTRX dual channel
131 * diversity receiver only.
132 */
133static struct uhd_dev_offset special_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800134 { UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
135 { UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500136};
137
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800138static double get_dev_offset(enum uhd_dev_type type, int tx_sps,
139 bool edge = false, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -0400140{
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800141 struct uhd_dev_offset *offset = NULL;
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500142
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500143 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -0400144 if (type == USRP1) {
145 LOG(ERR) << "Invalid device type";
146 return 0.0;
147 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000148
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800149 if (edge && diversity) {
150 LOG(ERR) << "Unsupported configuration";
151 return 0.0;
152 }
153
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500154 /* Special cases (e.g. diversity receiver) */
155 if (diversity) {
156 if (type != UMTRX) {
157 LOG(ALERT) << "Diversity on UmTRX only";
158 return 0.0;
159 }
160
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800161 switch (tx_sps) {
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500162 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500163 offset = &special_offsets[0];
164 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500165 case 4:
166 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500167 offset = &special_offsets[1];
168 }
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800169 } else if (edge) {
170 if ((type != B200) && (type != B210)) {
171 LOG(ALERT) << "EDGE support on B200/B210 only";
172 return 0.0;
173 }
174 if (tx_sps != 4) {
175 LOG(ALERT) << "Invalid device configuration";
176 return 0.0;
177 }
178
179 offset = &edge_offset;
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500180 } else {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800181 /* Search for matching offset value */
182 for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
183 if ((type == uhd_offsets[i].type) &&
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800184 (tx_sps == uhd_offsets[i].tx_sps)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800185 offset = &uhd_offsets[i];
186 break;
187 }
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500188 }
189 }
190
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800191 if (!offset) {
192 LOG(ERR) << "Invalid device configuration";
193 return 0.0;
194 }
195
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500196 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000197
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500198 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400199}
kurtis.heimerlce317332011-11-26 03:18:39 +0000200
Thomas Tsoucb69f082013-04-08 14:18:26 -0400201/*
202 * Select sample rate based on device type and requested samples-per-symbol.
203 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
204 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800205 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500206static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400207{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500208 if (diversity && (type == UMTRX)) {
209 return GSMRATE * 4;
210 } else if (diversity) {
211 LOG(ALERT) << "Diversity supported on UmTRX only";
212 return -9999.99;
213 }
214
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800215 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400216 return -9999.99;
217
218 switch (type) {
219 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800220 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400221 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400222 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400223 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500224 case B200:
225 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100226 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800227 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400228 case UMTRX:
229 return GSMRATE * sps;
230 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400231 break;
232 }
233
234 LOG(ALERT) << "Unknown device type " << type;
235 return -9999.99;
236}
237
kurtis.heimerl965e7572011-11-26 03:16:54 +0000238/*
239 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
240 or UHD style timestamps. Time conversions are handled
241 internally or accessable through the static convert calls.
242*/
243class smpl_buf {
244public:
245 /** Sample buffer constructor
246 @param len number of 32-bit samples the buffer should hold
247 @param rate sample clockrate
248 @param timestamp
249 */
250 smpl_buf(size_t len, double rate);
251 ~smpl_buf();
252
253 /** Query number of samples available for reading
254 @param timestamp time of first sample
255 @return number of available samples or error
256 */
257 ssize_t avail_smpls(TIMESTAMP timestamp) const;
258 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
259
260 /** Read and write
261 @param buf pointer to buffer
262 @param len number of samples desired to read or write
263 @param timestamp time of first stample
264 @return number of actual samples read or written or error
265 */
266 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
267 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
268 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
269 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
270
271 /** Buffer status string
272 @return a formatted string describing internal buffer state
273 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800274 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000275
276 /** Formatted error string
277 @param code an error code
278 @return a formatted error string
279 */
280 static std::string str_code(ssize_t code);
281
282 enum err_code {
283 ERROR_TIMESTAMP = -1,
284 ERROR_READ = -2,
285 ERROR_WRITE = -3,
286 ERROR_OVERFLOW = -4
287 };
288
289private:
290 uint32_t *data;
291 size_t buf_len;
292
293 double clk_rt;
294
295 TIMESTAMP time_start;
296 TIMESTAMP time_end;
297
298 size_t data_start;
299 size_t data_end;
300};
301
302/*
303 uhd_device - UHD implementation of the Device interface. Timestamped samples
304 are sent to and received from the device. An intermediate buffer
305 on the receive side collects and aligns packets of samples.
306 Events and errors such as underruns are reported asynchronously
307 by the device and received in a separate thread.
308*/
309class uhd_device : public RadioDevice {
310public:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800311 uhd_device(size_t tx_sps, size_t rx_sps,
312 size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000313 ~uhd_device();
314
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400315 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000316 bool start();
317 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800318 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500319 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400320 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000321
Thomas Tsou204a9f12013-10-29 18:34:16 -0400322 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000323 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
324
Thomas Tsou204a9f12013-10-29 18:34:16 -0400325 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326 TIMESTAMP timestamp, bool isControl);
327
328 bool updateAlignment(TIMESTAMP timestamp);
329
Thomas Tsou204a9f12013-10-29 18:34:16 -0400330 bool setTxFreq(double wFreq, size_t chan);
331 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000332
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800333 inline TIMESTAMP initialWriteTimestamp();
334 inline TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000335
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300336 double fullScaleInputValue();
337 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000338
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 double setRxGain(double db, size_t chan);
340 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000341 double maxRxGain(void) { return rx_gain_max; }
342 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000343
Thomas Tsou204a9f12013-10-29 18:34:16 -0400344 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000345 double maxTxGain(void) { return tx_gain_max; }
346 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347
Thomas Tsou204a9f12013-10-29 18:34:16 -0400348 double getTxFreq(size_t chan);
349 double getRxFreq(size_t chan);
350 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000351
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400352 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000353 inline double numberRead() { return rx_pkt_cnt; }
354 inline double numberWritten() { return 0; }
355
356 /** Receive and process asynchronous message
357 @return true if message received or false on timeout or error
358 */
359 bool recv_async_msg();
360
kurtis.heimerld4be0742011-11-26 03:17:46 +0000361 enum err_code {
362 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800363 ERROR_TIMEOUT = -2,
364 ERROR_UNRECOVERABLE = -3,
365 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000366 };
367
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000369 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400370 uhd::tx_streamer::sptr tx_stream;
371 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400372 enum TxWindowType tx_window;
373 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000374
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800375 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400376 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000377
Thomas Tsou204a9f12013-10-29 18:34:16 -0400378 double tx_gain_min, tx_gain_max;
379 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500380 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000381
Thomas Tsou204a9f12013-10-29 18:34:16 -0400382 std::vector<double> tx_gains, rx_gains;
383 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000384 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000385
386 bool started;
387 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000388
389 size_t rx_pkt_cnt;
390 size_t drop_cnt;
391 uhd::time_spec_t prev_ts;
392
Thomas Tsou18d3b832014-02-13 14:55:23 -0500393 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400394 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000395
kurtis.heimerl02d04052011-11-26 03:17:10 +0000396 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400397 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400398 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000399 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000400 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000401 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000402
kurtis.heimerl965e7572011-11-26 03:16:54 +0000403 std::string str_code(uhd::rx_metadata_t metadata);
404 std::string str_code(uhd::async_metadata_t metadata);
405
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500406 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
407 bool set_freq(double freq, size_t chan, bool tx);
408
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800409 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500410 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800411 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000412};
413
414void *async_event_loop(uhd_device *dev)
415{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500416 dev->setPriority(0.43);
417
kurtis.heimerl965e7572011-11-26 03:16:54 +0000418 while (1) {
419 dev->recv_async_msg();
420 pthread_testcancel();
421 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500422
423 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000424}
425
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000426/*
427 Catch and drop underrun 'U' and overrun 'O' messages from stdout
428 since we already report using the logging facility. Direct
429 everything else appropriately.
430 */
431void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
432{
433 switch (type) {
434 case uhd::msg::status:
435 LOG(INFO) << msg;
436 break;
437 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000438 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000439 break;
440 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000441 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000442 break;
443 case uhd::msg::fastpath:
444 break;
445 }
446}
447
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800448static void thread_enable_cancel(bool cancel)
449{
450 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
451 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
452}
453
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800454uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
455 size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400456 : tx_gain_min(0.0), tx_gain_max(0.0),
457 rx_gain_min(0.0), rx_gain_max(0.0),
458 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000459 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500460 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000461{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800462 this->tx_sps = tx_sps;
463 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400464 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500465 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500466 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000467}
468
469uhd_device::~uhd_device()
470{
471 stop();
472
Thomas Tsou204a9f12013-10-29 18:34:16 -0400473 for (size_t i = 0; i < rx_buffers.size(); i++)
474 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000475}
476
kurtis.heimerl24481de2011-11-26 03:17:18 +0000477void uhd_device::init_gains()
478{
479 uhd::gain_range_t range;
480
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400481 if (dev_type == UMTRX) {
482 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
483 if (gain_stages[0] == "VGA") {
484 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
485 }
486 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
487 range = usrp_dev->get_tx_gain_range();
488 tx_gain_min = range.start();
489 tx_gain_max = range.stop();
490 } else {
491 range = usrp_dev->get_tx_gain_range("VGA2");
492 tx_gain_min = UMTRX_VGA1_DEF + range.start();
493 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
494 }
495 } else {
496 range = usrp_dev->get_tx_gain_range();
497 tx_gain_min = range.start();
498 tx_gain_max = range.stop();
499 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400500 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000501
502 range = usrp_dev->get_rx_gain_range();
503 rx_gain_min = range.start();
504 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400505 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000506
Thomas Tsou204a9f12013-10-29 18:34:16 -0400507 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400508 double gain = (tx_gain_min + tx_gain_max) / 2;
509 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
510 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400511 tx_gains[i] = usrp_dev->get_tx_gain(i);
512 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000513
Thomas Tsou204a9f12013-10-29 18:34:16 -0400514 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400515 double gain = (rx_gain_min + rx_gain_max) / 2;
516 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
517 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400518 rx_gains[i] = usrp_dev->get_rx_gain(i);
519 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000520
521 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400522
kurtis.heimerl24481de2011-11-26 03:17:18 +0000523}
524
Thomas Tsou02d88d12013-04-05 15:36:30 -0400525int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000526{
Thomas Tsou092f7572013-04-04 17:04:39 -0400527 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000528
Thomas Tsou7e068062013-04-08 19:39:37 -0400529 try {
530 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400531 } catch (const std::exception &ex) {
532 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
533 LOG(ALERT) << ex.what();
534 return -1;
535 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000536
Thomas Tsou092f7572013-04-04 17:04:39 -0400537 actual = usrp_dev->get_master_clock_rate();
538 offset = fabs(clk_rate - actual);
539
540 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000541 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400542 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400543 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400544 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000545 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400546
547 return 0;
548}
549
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400550int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400551{
Thomas Tsou092f7572013-04-04 17:04:39 -0400552 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400553 double tx_offset, rx_offset;
554
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100555 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700556 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400557 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400558 return -1;
559 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100560 else if (dev_type == E1XX) {
561 if (set_master_clk(E1XX_CLK_RT) < 0)
562 return -1;
563 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000564
565 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400566 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400567 usrp_dev->set_tx_rate(tx_rate);
568 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400569 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400570 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400571 LOG(ALERT) << ex.what();
572 return -1;
573 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400574 this->tx_rate = usrp_dev->get_tx_rate();
575 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000576
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400577 tx_offset = fabs(this->tx_rate - tx_rate);
578 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400579 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000580 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400581 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
582 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400583 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000584 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000585
Thomas Tsou02d88d12013-04-05 15:36:30 -0400586 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000587}
588
Thomas Tsou204a9f12013-10-29 18:34:16 -0400589double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000590{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400591 if (chan >= tx_gains.size()) {
592 LOG(ALERT) << "Requested non-existent channel" << chan;
593 return 0.0f;
594 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000595
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400596 if (dev_type == UMTRX) {
597 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
598 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
599 usrp_dev->set_tx_gain(db, chan);
600 } else {
601 // New UHD versions support split configuration of
602 // Tx gain stages. We utilize this to set the gain
603 // configuration, optimal for the Tx signal quality.
604 // From our measurements, VGA1 must be 18dB plus-minus
605 // one and VGA2 is the best when 23dB or lower.
606 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
607 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
608 }
609 } else {
610 usrp_dev->set_tx_gain(db, chan);
611 }
612
Thomas Tsou204a9f12013-10-29 18:34:16 -0400613 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000614
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400615 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400616
617 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000618}
619
Thomas Tsou204a9f12013-10-29 18:34:16 -0400620double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000621{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400622 if (chan >= rx_gains.size()) {
623 LOG(ALERT) << "Requested non-existent channel " << chan;
624 return 0.0f;
625 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000626
Thomas Tsou204a9f12013-10-29 18:34:16 -0400627 usrp_dev->set_rx_gain(db, chan);
628 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000629
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400630 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400631
632 return rx_gains[chan];
633}
634
635double uhd_device::getRxGain(size_t chan)
636{
637 if (chan >= rx_gains.size()) {
638 LOG(ALERT) << "Requested non-existent channel " << chan;
639 return 0.0f;
640 }
641
642 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000643}
644
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000645/*
646 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400647 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000648 deal with the transport latency. Reject the USRP1 because UHD doesn't
649 support timestamped samples with it.
650 */
651bool uhd_device::parse_dev_type()
652{
653 std::string mboard_str, dev_str;
654 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800655 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
Tom Tsou283b22d2015-10-21 17:10:23 -0700656 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000657
658 prop_tree = usrp_dev->get_device()->get_tree();
659 dev_str = prop_tree->access<std::string>("/name").get();
660 mboard_str = usrp_dev->get_mboard_name();
661
662 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400663 usrp2_str = dev_str.find("USRP2");
664 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400665 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400666 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100667 e100_str = mboard_str.find("E100");
668 e110_str = mboard_str.find("E110");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800669 e310_str = mboard_str.find("E310");
670 x300_str = mboard_str.find("X300");
671 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400672 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000673
674 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000675 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
676 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400677 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000678 return false;
679 }
680
Thomas Tsoucb69f082013-04-08 14:18:26 -0400681 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400682 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400683 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400684 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500685 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500686 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400687 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500688 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500689 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100690 } else if (e100_str != std::string::npos) {
691 tx_window = TX_WINDOW_FIXED;
692 dev_type = E1XX;
693 } else if (e110_str != std::string::npos) {
694 tx_window = TX_WINDOW_FIXED;
695 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400696 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500697 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400698 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800699 } else if (e310_str != std::string::npos) {
700 tx_window = TX_WINDOW_FIXED;
701 dev_type = E3XX;
702 } else if (x300_str != std::string::npos) {
703 tx_window = TX_WINDOW_FIXED;
704 dev_type = X3XX;
705 } else if (x310_str != std::string::npos) {
706 tx_window = TX_WINDOW_FIXED;
707 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400708 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500709 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400710 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400711 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400712 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400713 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000714 }
715
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500716 if (tx_window == TX_WINDOW_USRP1) {
717 LOG(INFO) << "Using USRP1 type transmit window for "
718 << dev_str << " " << mboard_str;
719 } else {
720 LOG(INFO) << "Using fixed transmit window for "
721 << dev_str << " " << mboard_str;
722 }
723
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000724 return true;
725}
726
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400727int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000728{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000729 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000730 uhd::device_addr_t addr(args);
731 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000732 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000733 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400734 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000735 }
736
737 // Use the first found device
738 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000739 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700740 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000741 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700742 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400743 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000744 }
745
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000746 // Check for a valid device type and set bus type
747 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400748 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000749
Thomas Tsou204a9f12013-10-29 18:34:16 -0400750 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500751 if ((dev_type == B210) && (chans == 2)) {
752 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400753 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400754 usrp_dev->set_tx_subdev_spec(subdev_spec);
755 usrp_dev->set_rx_subdev_spec(subdev_spec);
756 } else if (chans != 1) {
757 LOG(ALERT) << "Invalid channel combination for device";
758 return -1;
759 }
760
761 tx_freqs.resize(chans);
762 rx_freqs.resize(chans);
763 tx_gains.resize(chans);
764 rx_gains.resize(chans);
765 rx_buffers.resize(chans);
766
Thomas Tsou010fff72013-10-16 00:31:18 -0400767 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400768 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400769
kurtis.heimerl965e7572011-11-26 03:16:54 +0000770 // Set rates
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800771 double _tx_rate = select_rate(dev_type, tx_sps);
772 double _rx_rate = select_rate(dev_type, rx_sps, diversity);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500773
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500774 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
775 return -1;
776 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400777 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778
Alexander Chemerise1714252015-04-28 23:09:02 -0400779 // Set RF frontend bandwidth
780 if (dev_type == UMTRX) {
781 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
782 for (size_t i = 0; i < chans; i++) {
783 usrp_dev->set_tx_bandwidth(500*1000*2, i);
784 if (!diversity)
785 usrp_dev->set_rx_bandwidth(500*1000*2, i);
786 }
787 }
788
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400789 /* Create TX and RX streamers */
790 uhd::stream_args_t stream_args("sc16");
791 for (size_t i = 0; i < chans; i++)
792 stream_args.channels.push_back(i);
793
794 tx_stream = usrp_dev->get_tx_stream(stream_args);
795 rx_stream = usrp_dev->get_rx_stream(stream_args);
796
797 /* Number of samples per over-the-wire packet */
798 tx_spp = tx_stream->get_max_num_samps();
799 rx_spp = rx_stream->get_max_num_samps();
800
kurtis.heimerl965e7572011-11-26 03:16:54 +0000801 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400802 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400803 for (size_t i = 0; i < rx_buffers.size(); i++)
804 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000805
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800806 // Set receive chain sample offset. Trigger the EDGE offset
807 // table by checking for 4 SPS on the receive path. No other
808 // configuration supports using 4 SPS.
809 bool edge = false;
810 if (rx_sps == 4)
811 edge = true;
812
813 double offset = get_dev_offset(dev_type, tx_sps, edge, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400814 if (offset == 0.0) {
815 LOG(ERR) << "Unsupported configuration, no correction applied";
816 ts_offset = 0;
817 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400818 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400819 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000820
kurtis.heimerl02d04052011-11-26 03:17:10 +0000821 // Initialize and shadow gain values
822 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000823
kurtis.heimerl965e7572011-11-26 03:16:54 +0000824 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000825 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000826
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500827 if (diversity)
828 return DIVERSITY;
829
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400830 switch (dev_type) {
831 case B100:
832 return RESAMP_64M;
833 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800834 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400835 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500836 case B200:
837 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100838 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800839 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500840 default:
841 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400842 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400843
844 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000845}
846
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000847bool uhd_device::flush_recv(size_t num_pkts)
848{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000849 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000850 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800851 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000852
Thomas Tsou18d3b832014-02-13 14:55:23 -0500853 std::vector<std::vector<short> >
854 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000855
Thomas Tsou18d3b832014-02-13 14:55:23 -0500856 std::vector<short *> pkt_ptrs;
857 for (size_t i = 0; i < pkt_bufs.size(); i++)
858 pkt_ptrs.push_back(&pkt_bufs[i].front());
859
860 ts_initial = 0;
861 while (!ts_initial || (num_pkts-- > 0)) {
862 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400863 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000864 if (!num_smpls) {
865 switch (md.error_code) {
866 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800867 LOG(ALERT) << "Device timed out";
868 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000869 default:
870 continue;
871 }
872 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500873
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700874 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000875 }
876
Thomas Tsou18d3b832014-02-13 14:55:23 -0500877 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
878
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000879 return true;
880}
881
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800882bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000883{
Thomas Tsouc2839162014-03-27 13:52:58 -0400884 /* Allow 100 ms delay to align multi-channel streams */
885 double delay = 0.1;
886
kurtis.heimerl68292102011-11-26 03:17:28 +0000887 aligned = false;
888
Thomas Tsouc2839162014-03-27 13:52:58 -0400889 uhd::time_spec_t current = usrp_dev->get_time_now();
890
Thomas Tsou204a9f12013-10-29 18:34:16 -0400891 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400892 cmd.stream_now = false;
893 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400894
kurtis.heimerl68292102011-11-26 03:17:28 +0000895 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500896
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800897 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000898}
899
kurtis.heimerl965e7572011-11-26 03:16:54 +0000900bool uhd_device::start()
901{
902 LOG(INFO) << "Starting USRP...";
903
904 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000905 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000906 return false;
907 }
908
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400909 // Register msg handler
910 uhd::msg::register_handler(&uhd_msg_handler);
911
kurtis.heimerl965e7572011-11-26 03:16:54 +0000912 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800913 async_event_thrd = new Thread();
914 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000915
916 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800917 if (!restart())
918 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000919
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920 // Display usrp time
921 double time_now = usrp_dev->get_time_now().get_real_secs();
922 LOG(INFO) << "The current time is " << time_now << " seconds";
923
924 started = true;
925 return true;
926}
927
928bool uhd_device::stop()
929{
Thomas Tsou2c791102013-11-15 23:00:28 -0500930 if (!started)
931 return false;
932
933 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000934 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
935
936 usrp_dev->issue_stream_cmd(stream_cmd);
937
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800938 async_event_thrd->cancel();
939 async_event_thrd->join();
940 delete async_event_thrd;
941
kurtis.heimerl965e7572011-11-26 03:16:54 +0000942 started = false;
943 return true;
944}
945
Thomas Tsou7553aa92013-11-08 12:50:03 -0500946void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500948 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000949 return;
950}
951
kurtis.heimerld4be0742011-11-26 03:17:46 +0000952int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000953{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000954 uhd::time_spec_t ts;
955
kurtis.heimerld4be0742011-11-26 03:17:46 +0000956 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000957 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000958
959 switch (md.error_code) {
960 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000961 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800962 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000963 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
964 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
965 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
966 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
967 default:
968 return ERROR_UNHANDLED;
969 }
970 }
971
kurtis.heimerl965e7572011-11-26 03:16:54 +0000972 // Missing timestamp
973 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000974 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000975 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000976 }
977
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000978 ts = md.time_spec;
979
kurtis.heimerl965e7572011-11-26 03:16:54 +0000980 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000981 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000982 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000983 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
984 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000985 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000986 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000987 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988 }
989
990 return 0;
991}
992
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
994 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000995{
996 ssize_t rc;
997 uhd::time_spec_t ts;
998 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000999
Thomas Tsou204a9f12013-10-29 18:34:16 -04001000 if (bufs.size() != chans) {
1001 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1002 return -1;
1003 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001005 *overrun = false;
1006 *underrun = false;
1007
kurtis.heimerl965e7572011-11-26 03:16:54 +00001008 // Shift read time with respect to transmit clock
1009 timestamp += ts_offset;
1010
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001011 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001012 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001013
1014 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001015 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001016 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001017 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001018 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001019 return 0;
1020 }
1021
Thomas Tsou204a9f12013-10-29 18:34:16 -04001022 // Create vector buffer
1023 std::vector<std::vector<short> >
1024 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1025
1026 std::vector<short *> pkt_ptrs;
1027 for (size_t i = 0; i < pkt_bufs.size(); i++)
1028 pkt_ptrs.push_back(&pkt_bufs[i].front());
1029
kurtis.heimerl965e7572011-11-26 03:16:54 +00001030 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001031 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001032 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001033 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1034 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001035 thread_enable_cancel(true);
1036
kurtis.heimerl965e7572011-11-26 03:16:54 +00001037 rx_pkt_cnt++;
1038
kurtis.heimerld4be0742011-11-26 03:17:46 +00001039 // Check for errors
1040 rc = check_rx_md_err(metadata, num_smpls);
1041 switch (rc) {
1042 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001043 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1044 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001045 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001046 case ERROR_TIMEOUT:
1047 // Assume stopping condition
1048 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001049 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001050 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001051 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001052 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001053 }
1054
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001056 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001057
Thomas Tsou204a9f12013-10-29 18:34:16 -04001058 for (size_t i = 0; i < rx_buffers.size(); i++) {
1059 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1060 num_smpls,
1061 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001062
Thomas Tsou204a9f12013-10-29 18:34:16 -04001063 // Continue on local overrun, exit on other errors
1064 if ((rc < 0)) {
1065 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001066 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067 if (rc != smpl_buf::ERROR_OVERFLOW)
1068 return 0;
1069 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001070 }
1071 }
1072
1073 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001074 for (size_t i = 0; i < rx_buffers.size(); i++) {
1075 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1076 if ((rc < 0) || (rc != len)) {
1077 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001078 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079 return 0;
1080 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001081 }
1082
1083 return len;
1084}
1085
Thomas Tsou204a9f12013-10-29 18:34:16 -04001086int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1087 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001088{
1089 uhd::tx_metadata_t metadata;
1090 metadata.has_time_spec = true;
1091 metadata.start_of_burst = false;
1092 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001093 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001094
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001095 *underrun = false;
1096
kurtis.heimerl965e7572011-11-26 03:16:54 +00001097 // No control packets
1098 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001099 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001100 return 0;
1101 }
1102
Thomas Tsou204a9f12013-10-29 18:34:16 -04001103 if (bufs.size() != chans) {
1104 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1105 return -1;
1106 }
1107
kurtis.heimerl965e7572011-11-26 03:16:54 +00001108 // Drop a fixed number of packets (magic value)
1109 if (!aligned) {
1110 drop_cnt++;
1111
1112 if (drop_cnt == 1) {
1113 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001114 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001115 metadata.end_of_burst = true;
1116 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001117 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001118 return len;
1119 } else {
1120 LOG(DEBUG) << "Aligning transmitter: start burst";
1121 metadata.start_of_burst = true;
1122 aligned = true;
1123 drop_cnt = 0;
1124 }
1125 }
1126
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001127 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001128 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001129 thread_enable_cancel(true);
1130
ttsoub371ed52012-01-09 18:11:34 +00001131 if (num_smpls != (unsigned) len) {
1132 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001133 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001134
1135 return num_smpls;
1136}
1137
1138bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1139{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001140 return true;
1141}
1142
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001143uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1144{
1145 double rf_spread, rf_freq;
1146 std::vector<double> freqs;
1147 uhd::tune_request_t treq(freq);
1148
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001149 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001150 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001151 return uhd::tune_request_t(freq, offset);
1152
1153 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1154 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1155 // only distort the signal (because cordic is not ideal).
1156 treq.target_freq = freq;
1157 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1158 treq.rf_freq = freq;
1159 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1160 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001161 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001162 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001163 if (offset == 0.0)
1164 return treq;
1165
1166 return uhd::tune_request_t(freq, offset);
1167 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001168 LOG(ALERT) << chans << " channels unsupported";
1169 return treq;
1170 }
1171
1172 if (tx)
1173 freqs = tx_freqs;
1174 else
1175 freqs = rx_freqs;
1176
1177 /* Tune directly if other channel isn't tuned */
1178 if (freqs[!chan] < 10.0)
1179 return treq;
1180
1181 /* Find center frequency between channels */
1182 rf_spread = fabs(freqs[!chan] - freq);
1183 if (rf_spread > B2XX_CLK_RT) {
1184 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1185 return treq;
1186 }
1187
1188 rf_freq = (freqs[!chan] + freq) / 2.0f;
1189
1190 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1191 treq.target_freq = freq;
1192 treq.rf_freq = rf_freq;
1193
1194 return treq;
1195}
1196
1197bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1198{
1199 std::vector<double> freqs;
1200 uhd::tune_result_t tres;
1201 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1202
1203 if (tx) {
1204 tres = usrp_dev->set_tx_freq(treq, chan);
1205 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1206 } else {
1207 tres = usrp_dev->set_rx_freq(treq, chan);
1208 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1209 }
1210 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1211
Thomas Tsou8e17df72014-03-06 14:16:11 -05001212 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1213 return true;
1214
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001215 /* Manual RF policy means we intentionally tuned with a baseband
1216 * offset for dual-channel purposes. Now retune the other channel
1217 * with the opposite corresponding frequency offset
1218 */
1219 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1220 if (tx) {
1221 treq = select_freq(tx_freqs[!chan], !chan, true);
1222 tres = usrp_dev->set_tx_freq(treq, !chan);
1223 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1224 } else {
1225 treq = select_freq(rx_freqs[!chan], !chan, false);
1226 tres = usrp_dev->set_rx_freq(treq, !chan);
1227 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1228
1229 }
1230 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1231 }
1232
1233 return true;
1234}
1235
Thomas Tsou204a9f12013-10-29 18:34:16 -04001236bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001237{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001238 if (chan >= tx_freqs.size()) {
1239 LOG(ALERT) << "Requested non-existent channel " << chan;
1240 return false;
1241 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001242 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001243
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001244 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001245}
1246
Thomas Tsou204a9f12013-10-29 18:34:16 -04001247bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001248{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001249 if (chan >= rx_freqs.size()) {
1250 LOG(ALERT) << "Requested non-existent channel " << chan;
1251 return false;
1252 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001253 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001254
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001255 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001256}
1257
Thomas Tsou204a9f12013-10-29 18:34:16 -04001258double uhd_device::getTxFreq(size_t chan)
1259{
1260 if (chan >= tx_freqs.size()) {
1261 LOG(ALERT) << "Requested non-existent channel " << chan;
1262 return 0.0;
1263 }
1264
1265 return tx_freqs[chan];
1266}
1267
1268double uhd_device::getRxFreq(size_t chan)
1269{
1270 if (chan >= rx_freqs.size()) {
1271 LOG(ALERT) << "Requested non-existent channel " << chan;
1272 return 0.0;
1273 }
1274
1275 return rx_freqs[chan];
1276}
1277
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001278/*
1279 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1280 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1281 * combination.
1282 */
1283TIMESTAMP uhd_device::initialWriteTimestamp()
1284{
1285 if (rx_sps == tx_sps)
1286 return ts_initial;
1287 else
1288 return ts_initial * tx_sps;
1289}
1290
1291TIMESTAMP uhd_device::initialReadTimestamp()
1292{
1293 return ts_initial;
1294}
1295
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001296double uhd_device::fullScaleInputValue()
1297{
1298 if (dev_type == UMTRX)
1299 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1300 else
1301 return (double) SHRT_MAX * USRP_TX_AMPL;
1302}
1303
1304double uhd_device::fullScaleOutputValue()
1305{
1306 return (double) SHRT_MAX;
1307}
1308
kurtis.heimerl965e7572011-11-26 03:16:54 +00001309bool uhd_device::recv_async_msg()
1310{
ttsou60dc4c92012-08-08 23:30:23 +00001311 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001312
1313 thread_enable_cancel(false);
1314 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1315 thread_enable_cancel(true);
1316 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001317 return false;
1318
1319 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001320 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001321 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001322
1323 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1324 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1325 LOG(ERR) << str_code(md);
1326 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001327 }
1328
1329 return true;
1330}
1331
1332std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1333{
1334 std::ostringstream ost("UHD: ");
1335
1336 switch (metadata.error_code) {
1337 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1338 ost << "No error";
1339 break;
1340 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1341 ost << "No packet received, implementation timed-out";
1342 break;
1343 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1344 ost << "A stream command was issued in the past";
1345 break;
1346 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1347 ost << "Expected another stream command";
1348 break;
1349 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1350 ost << "An internal receive buffer has filled";
1351 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001352 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1353 ost << "Multi-channel alignment failed";
1354 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001355 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1356 ost << "The packet could not be parsed";
1357 break;
1358 default:
1359 ost << "Unknown error " << metadata.error_code;
1360 }
1361
1362 if (metadata.has_time_spec)
1363 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1364
1365 return ost.str();
1366}
1367
1368std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1369{
1370 std::ostringstream ost("UHD: ");
1371
1372 switch (metadata.event_code) {
1373 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1374 ost << "A packet was successfully transmitted";
1375 break;
1376 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1377 ost << "An internal send buffer has emptied";
1378 break;
1379 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1380 ost << "Packet loss between host and device";
1381 break;
1382 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1383 ost << "Packet time was too late or too early";
1384 break;
1385 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1386 ost << "Underflow occurred inside a packet";
1387 break;
1388 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1389 ost << "Packet loss within a burst";
1390 break;
1391 default:
1392 ost << "Unknown error " << metadata.event_code;
1393 }
1394
1395 if (metadata.has_time_spec)
1396 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1397
1398 return ost.str();
1399}
1400
1401smpl_buf::smpl_buf(size_t len, double rate)
1402 : buf_len(len), clk_rt(rate),
1403 time_start(0), time_end(0), data_start(0), data_end(0)
1404{
1405 data = new uint32_t[len];
1406}
1407
1408smpl_buf::~smpl_buf()
1409{
1410 delete[] data;
1411}
1412
1413ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1414{
1415 if (timestamp < time_start)
1416 return ERROR_TIMESTAMP;
1417 else if (timestamp >= time_end)
1418 return 0;
1419 else
1420 return time_end - timestamp;
1421}
1422
1423ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1424{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001425 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001426}
1427
1428ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1429{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001430 int type_sz = 2 * sizeof(short);
1431
kurtis.heimerl965e7572011-11-26 03:16:54 +00001432 // Check for valid read
1433 if (timestamp < time_start)
1434 return ERROR_TIMESTAMP;
1435 if (timestamp >= time_end)
1436 return 0;
1437 if (len >= buf_len)
1438 return ERROR_READ;
1439
1440 // How many samples should be copied
1441 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001442 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001443 num_smpls = len;
1444
1445 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001446 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001447
1448 // Read it
1449 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001450 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001451 memcpy(buf, data + read_start, numBytes);
1452 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001453 size_t first_cp = (buf_len - read_start) * type_sz;
1454 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001455
1456 memcpy(buf, data + read_start, first_cp);
1457 memcpy((char*) buf + first_cp, data, second_cp);
1458 }
1459
1460 data_start = (read_start + len) % buf_len;
1461 time_start = timestamp + len;
1462
1463 if (time_start > time_end)
1464 return ERROR_READ;
1465 else
1466 return num_smpls;
1467}
1468
1469ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1470{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001471 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001472}
1473
1474ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1475{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001476 int type_sz = 2 * sizeof(short);
1477
kurtis.heimerl965e7572011-11-26 03:16:54 +00001478 // Check for valid write
1479 if ((len == 0) || (len >= buf_len))
1480 return ERROR_WRITE;
1481 if ((timestamp + len) <= time_end)
1482 return ERROR_TIMESTAMP;
1483
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001484 if (timestamp < time_end) {
1485 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001486 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1487 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 -04001488 // Do not return error here, because it's a rounding error and is not fatal
1489 }
1490 if (timestamp > time_end && time_end != 0) {
1491 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001492 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1493 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 -04001494 // Do not return error here, because it's a rounding error and is not fatal
1495 }
1496
kurtis.heimerl965e7572011-11-26 03:16:54 +00001497 // Starting index
1498 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1499
1500 // Write it
1501 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001502 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001503 memcpy(data + write_start, buf, numBytes);
1504 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001505 size_t first_cp = (buf_len - write_start) * type_sz;
1506 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001507
1508 memcpy(data + write_start, buf, first_cp);
1509 memcpy(data, (char*) buf + first_cp, second_cp);
1510 }
1511
1512 data_end = (write_start + len) % buf_len;
1513 time_end = timestamp + len;
1514
Thomas Tsou18d3b832014-02-13 14:55:23 -05001515 if (!data_start)
1516 data_start = write_start;
1517
kurtis.heimerl965e7572011-11-26 03:16:54 +00001518 if (((write_start + len) > buf_len) && (data_end > data_start))
1519 return ERROR_OVERFLOW;
1520 else if (time_end <= time_start)
1521 return ERROR_WRITE;
1522 else
1523 return len;
1524}
1525
1526ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1527{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001528 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001529}
1530
Tom Tsou1ae25562014-12-05 12:56:34 -08001531std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001532{
1533 std::ostringstream ost("Sample buffer: ");
1534
Tom Tsou1ae25562014-12-05 12:56:34 -08001535 ost << "timestamp = " << ts;
1536 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001537 ost << ", time_start = " << time_start;
1538 ost << ", time_end = " << time_end;
1539 ost << ", data_start = " << data_start;
1540 ost << ", data_end = " << data_end;
1541
1542 return ost.str();
1543}
1544
1545std::string smpl_buf::str_code(ssize_t code)
1546{
1547 switch (code) {
1548 case ERROR_TIMESTAMP:
1549 return "Sample buffer: Requested timestamp is not valid";
1550 case ERROR_READ:
1551 return "Sample buffer: Read error";
1552 case ERROR_WRITE:
1553 return "Sample buffer: Write error";
1554 case ERROR_OVERFLOW:
1555 return "Sample buffer: Overrun";
1556 default:
1557 return "Sample buffer: Unknown error";
1558 }
1559}
1560
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001561RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
1562 size_t chans, bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001563{
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001564 return new uhd_device(tx_sps, rx_sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001565}