blob: 9038bc19eda0e996df76e6090060f5a4ae682d3e [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;
71 int sps;
72 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050073 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040074};
75
kurtis.heimerl965e7572011-11-26 03:16:54 +000076/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040077 * Tx / Rx sample offset values. In a perfect world, there is no group delay
78 * though analog components, and behaviour through digital filters exactly
79 * matches calculated values. In reality, there are unaccounted factors,
80 * which are captured in these empirically measured (using a loopback test)
81 * timing correction values.
82 *
83 * Notes:
84 * USRP1 with timestamps is not supported by UHD.
85 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080086static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050087 { USRP1, 1, 0.0, "USRP1 not supported" },
88 { USRP1, 4, 0.0, "USRP1 not supported"},
89 { USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
90 { USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
91 { B100, 1, 1.2104e-4, "B100 1 SPS" },
92 { B100, 4, 7.9307e-5, "B100 4 SPS" },
Thomas Tsoue7882392014-02-13 14:46:23 -050093 { B200, 1, 9.9692e-5, "B200 1 SPS" },
94 { B200, 4, 6.9248e-5, "B200 4 SPS" },
95 { B210, 1, 9.9692e-5, "B210 1 SPS" },
96 { B210, 4, 6.9248e-5, "B210 4 SPS" },
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010097 { E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
98 { E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
Tom Tsou4ad9ea62014-12-03 18:47:20 -080099 { E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
100 { E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
101 { X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
102 { X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
Thomas Tsou2c1f85a2013-11-13 22:53:15 -0500103 { UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
104 { UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
Thomas Tsoue3e88142013-04-05 20:42:41 -0400105};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000106
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500107/*
108 * Offset handling for special cases. Currently used for UmTRX dual channel
109 * diversity receiver only.
110 */
111static struct uhd_dev_offset special_offsets[] = {
112 { UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
113 { UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
114};
115
116static double get_dev_offset(enum uhd_dev_type type,
117 int sps, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -0400118{
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800119 struct uhd_dev_offset *offset = NULL;
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500120
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500121 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -0400122 if (type == USRP1) {
123 LOG(ERR) << "Invalid device type";
124 return 0.0;
125 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000126
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500127 /* Special cases (e.g. diversity receiver) */
128 if (diversity) {
129 if (type != UMTRX) {
130 LOG(ALERT) << "Diversity on UmTRX only";
131 return 0.0;
132 }
133
134 switch (sps) {
135 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500136 offset = &special_offsets[0];
137 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500138 case 4:
139 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500140 offset = &special_offsets[1];
141 }
142 } else {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800143 /* Search for matching offset value */
144 for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
145 if ((type == uhd_offsets[i].type) &&
146 (sps == uhd_offsets[i].sps)) {
147 offset = &uhd_offsets[i];
148 break;
149 }
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500150 }
151 }
152
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800153 if (!offset) {
154 LOG(ERR) << "Invalid device configuration";
155 return 0.0;
156 }
157
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500158 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000159
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500160 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400161}
kurtis.heimerlce317332011-11-26 03:18:39 +0000162
Thomas Tsoucb69f082013-04-08 14:18:26 -0400163/*
164 * Select sample rate based on device type and requested samples-per-symbol.
165 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
166 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800167 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500168static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400169{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500170 if (diversity && (type == UMTRX)) {
171 return GSMRATE * 4;
172 } else if (diversity) {
173 LOG(ALERT) << "Diversity supported on UmTRX only";
174 return -9999.99;
175 }
176
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800177 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400178 return -9999.99;
179
180 switch (type) {
181 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800182 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400183 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400184 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400185 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500186 case B200:
187 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100188 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800189 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400190 case UMTRX:
191 return GSMRATE * sps;
192 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400193 break;
194 }
195
196 LOG(ALERT) << "Unknown device type " << type;
197 return -9999.99;
198}
199
kurtis.heimerl965e7572011-11-26 03:16:54 +0000200/** Timestamp conversion
201 @param timestamp a UHD or OpenBTS timestamp
202 @param rate sample rate
203 @return the converted timestamp
204*/
205uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
206{
207 double secs = (double) ticks / rate;
208 return uhd::time_spec_t(secs);
209}
210
211TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
212{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000213 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000214 return ts.get_tick_count(rate) + ticks;
215}
216
217/*
218 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
219 or UHD style timestamps. Time conversions are handled
220 internally or accessable through the static convert calls.
221*/
222class smpl_buf {
223public:
224 /** Sample buffer constructor
225 @param len number of 32-bit samples the buffer should hold
226 @param rate sample clockrate
227 @param timestamp
228 */
229 smpl_buf(size_t len, double rate);
230 ~smpl_buf();
231
232 /** Query number of samples available for reading
233 @param timestamp time of first sample
234 @return number of available samples or error
235 */
236 ssize_t avail_smpls(TIMESTAMP timestamp) const;
237 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
238
239 /** Read and write
240 @param buf pointer to buffer
241 @param len number of samples desired to read or write
242 @param timestamp time of first stample
243 @return number of actual samples read or written or error
244 */
245 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
246 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
247 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
248 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
249
250 /** Buffer status string
251 @return a formatted string describing internal buffer state
252 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800253 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000254
255 /** Formatted error string
256 @param code an error code
257 @return a formatted error string
258 */
259 static std::string str_code(ssize_t code);
260
261 enum err_code {
262 ERROR_TIMESTAMP = -1,
263 ERROR_READ = -2,
264 ERROR_WRITE = -3,
265 ERROR_OVERFLOW = -4
266 };
267
268private:
269 uint32_t *data;
270 size_t buf_len;
271
272 double clk_rt;
273
274 TIMESTAMP time_start;
275 TIMESTAMP time_end;
276
277 size_t data_start;
278 size_t data_end;
279};
280
281/*
282 uhd_device - UHD implementation of the Device interface. Timestamped samples
283 are sent to and received from the device. An intermediate buffer
284 on the receive side collects and aligns packets of samples.
285 Events and errors such as underruns are reported asynchronously
286 by the device and received in a separate thread.
287*/
288class uhd_device : public RadioDevice {
289public:
Thomas Tsou8e17df72014-03-06 14:16:11 -0500290 uhd_device(size_t sps, size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000291 ~uhd_device();
292
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400293 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000294 bool start();
295 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800296 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500297 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400298 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000299
Thomas Tsou204a9f12013-10-29 18:34:16 -0400300 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000301 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
302
Thomas Tsou204a9f12013-10-29 18:34:16 -0400303 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000304 TIMESTAMP timestamp, bool isControl);
305
306 bool updateAlignment(TIMESTAMP timestamp);
307
Thomas Tsou204a9f12013-10-29 18:34:16 -0400308 bool setTxFreq(double wFreq, size_t chan);
309 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000310
Thomas Tsou18d3b832014-02-13 14:55:23 -0500311 inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
312 inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000313
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300314 double fullScaleInputValue();
315 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000316
Thomas Tsou204a9f12013-10-29 18:34:16 -0400317 double setRxGain(double db, size_t chan);
318 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000319 double maxRxGain(void) { return rx_gain_max; }
320 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000321
Thomas Tsou204a9f12013-10-29 18:34:16 -0400322 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000323 double maxTxGain(void) { return tx_gain_max; }
324 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000325
Thomas Tsou204a9f12013-10-29 18:34:16 -0400326 double getTxFreq(size_t chan);
327 double getRxFreq(size_t chan);
328 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400330 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000331 inline double numberRead() { return rx_pkt_cnt; }
332 inline double numberWritten() { return 0; }
333
334 /** Receive and process asynchronous message
335 @return true if message received or false on timeout or error
336 */
337 bool recv_async_msg();
338
kurtis.heimerld4be0742011-11-26 03:17:46 +0000339 enum err_code {
340 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800341 ERROR_TIMEOUT = -2,
342 ERROR_UNRECOVERABLE = -3,
343 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000344 };
345
kurtis.heimerl965e7572011-11-26 03:16:54 +0000346private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000347 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400348 uhd::tx_streamer::sptr tx_stream;
349 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400350 enum TxWindowType tx_window;
351 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000352
Thomas Tsou204a9f12013-10-29 18:34:16 -0400353 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400354 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000355
Thomas Tsou204a9f12013-10-29 18:34:16 -0400356 double tx_gain_min, tx_gain_max;
357 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500358 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000359
Thomas Tsou204a9f12013-10-29 18:34:16 -0400360 std::vector<double> tx_gains, rx_gains;
361 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000362 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000363
364 bool started;
365 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000366
367 size_t rx_pkt_cnt;
368 size_t drop_cnt;
369 uhd::time_spec_t prev_ts;
370
Thomas Tsou18d3b832014-02-13 14:55:23 -0500371 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400372 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000373
kurtis.heimerl02d04052011-11-26 03:17:10 +0000374 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400375 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400376 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000377 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000378 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000379 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000380
kurtis.heimerl965e7572011-11-26 03:16:54 +0000381 std::string str_code(uhd::rx_metadata_t metadata);
382 std::string str_code(uhd::async_metadata_t metadata);
383
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500384 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
385 bool set_freq(double freq, size_t chan, bool tx);
386
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800387 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500388 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800389 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000390};
391
392void *async_event_loop(uhd_device *dev)
393{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500394 dev->setPriority(0.43);
395
kurtis.heimerl965e7572011-11-26 03:16:54 +0000396 while (1) {
397 dev->recv_async_msg();
398 pthread_testcancel();
399 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500400
401 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000402}
403
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000404/*
405 Catch and drop underrun 'U' and overrun 'O' messages from stdout
406 since we already report using the logging facility. Direct
407 everything else appropriately.
408 */
409void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
410{
411 switch (type) {
412 case uhd::msg::status:
413 LOG(INFO) << msg;
414 break;
415 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000416 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000417 break;
418 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000419 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000420 break;
421 case uhd::msg::fastpath:
422 break;
423 }
424}
425
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800426static void thread_enable_cancel(bool cancel)
427{
428 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
429 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
430}
431
Thomas Tsou8e17df72014-03-06 14:16:11 -0500432uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400433 : tx_gain_min(0.0), tx_gain_max(0.0),
434 rx_gain_min(0.0), rx_gain_max(0.0),
435 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000436 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500437 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000438{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400439 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400440 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500441 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500442 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000443}
444
445uhd_device::~uhd_device()
446{
447 stop();
448
Thomas Tsou204a9f12013-10-29 18:34:16 -0400449 for (size_t i = 0; i < rx_buffers.size(); i++)
450 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000451}
452
kurtis.heimerl24481de2011-11-26 03:17:18 +0000453void uhd_device::init_gains()
454{
455 uhd::gain_range_t range;
456
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400457 if (dev_type == UMTRX) {
458 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
459 if (gain_stages[0] == "VGA") {
460 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
461 }
462 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
463 range = usrp_dev->get_tx_gain_range();
464 tx_gain_min = range.start();
465 tx_gain_max = range.stop();
466 } else {
467 range = usrp_dev->get_tx_gain_range("VGA2");
468 tx_gain_min = UMTRX_VGA1_DEF + range.start();
469 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
470 }
471 } else {
472 range = usrp_dev->get_tx_gain_range();
473 tx_gain_min = range.start();
474 tx_gain_max = range.stop();
475 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000476
477 range = usrp_dev->get_rx_gain_range();
478 rx_gain_min = range.start();
479 rx_gain_max = range.stop();
480
Thomas Tsou204a9f12013-10-29 18:34:16 -0400481 for (size_t i = 0; i < tx_gains.size(); i++) {
482 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
483 tx_gains[i] = usrp_dev->get_tx_gain(i);
484 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000485
Thomas Tsou204a9f12013-10-29 18:34:16 -0400486 for (size_t i = 0; i < rx_gains.size(); i++) {
487 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
488 rx_gains[i] = usrp_dev->get_rx_gain(i);
489 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000490
491 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400492
kurtis.heimerl24481de2011-11-26 03:17:18 +0000493}
494
Thomas Tsou02d88d12013-04-05 15:36:30 -0400495int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000496{
Thomas Tsou092f7572013-04-04 17:04:39 -0400497 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000498
Thomas Tsou7e068062013-04-08 19:39:37 -0400499 try {
500 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400501 } catch (const std::exception &ex) {
502 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
503 LOG(ALERT) << ex.what();
504 return -1;
505 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000506
Thomas Tsou092f7572013-04-04 17:04:39 -0400507 actual = usrp_dev->get_master_clock_rate();
508 offset = fabs(clk_rate - actual);
509
510 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000511 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400512 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400513 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400514 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000515 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400516
517 return 0;
518}
519
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400520int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400521{
Thomas Tsou092f7572013-04-04 17:04:39 -0400522 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400523 double tx_offset, rx_offset;
524
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100525 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800526 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400527 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400528 return -1;
529 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100530 else if (dev_type == E1XX) {
531 if (set_master_clk(E1XX_CLK_RT) < 0)
532 return -1;
533 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000534
535 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400536 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400537 usrp_dev->set_tx_rate(tx_rate);
538 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400539 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400540 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400541 LOG(ALERT) << ex.what();
542 return -1;
543 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400544 this->tx_rate = usrp_dev->get_tx_rate();
545 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000546
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400547 tx_offset = fabs(this->tx_rate - tx_rate);
548 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400549 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000550 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400551 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
552 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400553 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000554 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000555
Thomas Tsou02d88d12013-04-05 15:36:30 -0400556 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000557}
558
Thomas Tsou204a9f12013-10-29 18:34:16 -0400559double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000560{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400561 if (chan >= tx_gains.size()) {
562 LOG(ALERT) << "Requested non-existent channel" << chan;
563 return 0.0f;
564 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000565
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400566 if (dev_type == UMTRX) {
567 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
568 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
569 usrp_dev->set_tx_gain(db, chan);
570 } else {
571 // New UHD versions support split configuration of
572 // Tx gain stages. We utilize this to set the gain
573 // configuration, optimal for the Tx signal quality.
574 // From our measurements, VGA1 must be 18dB plus-minus
575 // one and VGA2 is the best when 23dB or lower.
576 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
577 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
578 }
579 } else {
580 usrp_dev->set_tx_gain(db, chan);
581 }
582
Thomas Tsou204a9f12013-10-29 18:34:16 -0400583 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000584
Thomas Tsou204a9f12013-10-29 18:34:16 -0400585 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
586
587 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000588}
589
Thomas Tsou204a9f12013-10-29 18:34:16 -0400590double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000591{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400592 if (chan >= rx_gains.size()) {
593 LOG(ALERT) << "Requested non-existent channel " << chan;
594 return 0.0f;
595 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000596
Thomas Tsou204a9f12013-10-29 18:34:16 -0400597 usrp_dev->set_rx_gain(db, chan);
598 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000599
Thomas Tsou204a9f12013-10-29 18:34:16 -0400600 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
601
602 return rx_gains[chan];
603}
604
605double uhd_device::getRxGain(size_t chan)
606{
607 if (chan >= rx_gains.size()) {
608 LOG(ALERT) << "Requested non-existent channel " << chan;
609 return 0.0f;
610 }
611
612 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000613}
614
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000615/*
616 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400617 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000618 deal with the transport latency. Reject the USRP1 because UHD doesn't
619 support timestamped samples with it.
620 */
621bool uhd_device::parse_dev_type()
622{
623 std::string mboard_str, dev_str;
624 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800625 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
626 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000627
628 prop_tree = usrp_dev->get_device()->get_tree();
629 dev_str = prop_tree->access<std::string>("/name").get();
630 mboard_str = usrp_dev->get_mboard_name();
631
632 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400633 usrp2_str = dev_str.find("USRP2");
634 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400635 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400636 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100637 e100_str = mboard_str.find("E100");
638 e110_str = mboard_str.find("E110");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800639 e310_str = mboard_str.find("E310");
640 x300_str = mboard_str.find("X300");
641 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400642 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000643
644 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000645 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
646 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400647 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000648 return false;
649 }
650
Thomas Tsoucb69f082013-04-08 14:18:26 -0400651 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400652 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400653 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400654 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500655 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500656 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400657 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500658 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500659 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100660 } else if (e100_str != std::string::npos) {
661 tx_window = TX_WINDOW_FIXED;
662 dev_type = E1XX;
663 } else if (e110_str != std::string::npos) {
664 tx_window = TX_WINDOW_FIXED;
665 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400666 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500667 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400668 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800669 } else if (e310_str != std::string::npos) {
670 tx_window = TX_WINDOW_FIXED;
671 dev_type = E3XX;
672 } else if (x300_str != std::string::npos) {
673 tx_window = TX_WINDOW_FIXED;
674 dev_type = X3XX;
675 } else if (x310_str != std::string::npos) {
676 tx_window = TX_WINDOW_FIXED;
677 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400678 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500679 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400680 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400681 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400682 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400683 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000684 }
685
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500686 if (tx_window == TX_WINDOW_USRP1) {
687 LOG(INFO) << "Using USRP1 type transmit window for "
688 << dev_str << " " << mboard_str;
689 } else {
690 LOG(INFO) << "Using fixed transmit window for "
691 << dev_str << " " << mboard_str;
692 }
693
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000694 return true;
695}
696
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400697int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000698{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000699 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000700 uhd::device_addr_t addr(args);
701 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000702 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000703 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400704 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000705 }
706
707 // Use the first found device
708 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000709 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700710 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000711 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700712 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400713 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000714 }
715
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000716 // Check for a valid device type and set bus type
717 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400718 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000719
Thomas Tsou204a9f12013-10-29 18:34:16 -0400720 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500721 if ((dev_type == B210) && (chans == 2)) {
722 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400723 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400724 usrp_dev->set_tx_subdev_spec(subdev_spec);
725 usrp_dev->set_rx_subdev_spec(subdev_spec);
726 } else if (chans != 1) {
727 LOG(ALERT) << "Invalid channel combination for device";
728 return -1;
729 }
730
731 tx_freqs.resize(chans);
732 rx_freqs.resize(chans);
733 tx_gains.resize(chans);
734 rx_gains.resize(chans);
735 rx_buffers.resize(chans);
736
Thomas Tsou010fff72013-10-16 00:31:18 -0400737 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400738 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400739
kurtis.heimerl965e7572011-11-26 03:16:54 +0000740 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500741 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400742 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500743 if (diversity)
744 _rx_rate = select_rate(dev_type, 1, true);
745 else
746 _rx_rate = _tx_rate / sps;
747
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500748 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
749 return -1;
750 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400751 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752
Alexander Chemerise1714252015-04-28 23:09:02 -0400753 // Set RF frontend bandwidth
754 if (dev_type == UMTRX) {
755 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
756 for (size_t i = 0; i < chans; i++) {
757 usrp_dev->set_tx_bandwidth(500*1000*2, i);
758 if (!diversity)
759 usrp_dev->set_rx_bandwidth(500*1000*2, i);
760 }
761 }
762
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400763 /* Create TX and RX streamers */
764 uhd::stream_args_t stream_args("sc16");
765 for (size_t i = 0; i < chans; i++)
766 stream_args.channels.push_back(i);
767
768 tx_stream = usrp_dev->get_tx_stream(stream_args);
769 rx_stream = usrp_dev->get_rx_stream(stream_args);
770
771 /* Number of samples per over-the-wire packet */
772 tx_spp = tx_stream->get_max_num_samps();
773 rx_spp = rx_stream->get_max_num_samps();
774
kurtis.heimerl965e7572011-11-26 03:16:54 +0000775 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400776 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400777 for (size_t i = 0; i < rx_buffers.size(); i++)
778 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000779
780 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500781 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400782 if (offset == 0.0) {
783 LOG(ERR) << "Unsupported configuration, no correction applied";
784 ts_offset = 0;
785 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400786 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400787 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000788
kurtis.heimerl02d04052011-11-26 03:17:10 +0000789 // Initialize and shadow gain values
790 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000791
kurtis.heimerl965e7572011-11-26 03:16:54 +0000792 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000793 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000794
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500795 if (diversity)
796 return DIVERSITY;
797
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400798 switch (dev_type) {
799 case B100:
800 return RESAMP_64M;
801 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800802 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400803 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500804 case B200:
805 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100806 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800807 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500808 default:
809 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400810 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400811
812 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000813}
814
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000815bool uhd_device::flush_recv(size_t num_pkts)
816{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000817 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000818 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800819 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000820
Thomas Tsou18d3b832014-02-13 14:55:23 -0500821 std::vector<std::vector<short> >
822 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000823
Thomas Tsou18d3b832014-02-13 14:55:23 -0500824 std::vector<short *> pkt_ptrs;
825 for (size_t i = 0; i < pkt_bufs.size(); i++)
826 pkt_ptrs.push_back(&pkt_bufs[i].front());
827
828 ts_initial = 0;
829 while (!ts_initial || (num_pkts-- > 0)) {
830 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400831 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000832 if (!num_smpls) {
833 switch (md.error_code) {
834 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800835 LOG(ALERT) << "Device timed out";
836 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000837 default:
838 continue;
839 }
840 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500841
842 ts_initial = convert_time(md.time_spec, rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000843 }
844
Thomas Tsou18d3b832014-02-13 14:55:23 -0500845 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
846
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000847 return true;
848}
849
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800850bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000851{
Thomas Tsouc2839162014-03-27 13:52:58 -0400852 /* Allow 100 ms delay to align multi-channel streams */
853 double delay = 0.1;
854
kurtis.heimerl68292102011-11-26 03:17:28 +0000855 aligned = false;
856
Thomas Tsouc2839162014-03-27 13:52:58 -0400857 uhd::time_spec_t current = usrp_dev->get_time_now();
858
Thomas Tsou204a9f12013-10-29 18:34:16 -0400859 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400860 cmd.stream_now = false;
861 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400862
kurtis.heimerl68292102011-11-26 03:17:28 +0000863 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500864
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800865 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000866}
867
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868bool uhd_device::start()
869{
870 LOG(INFO) << "Starting USRP...";
871
872 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000873 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000874 return false;
875 }
876
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400877 // Register msg handler
878 uhd::msg::register_handler(&uhd_msg_handler);
879
kurtis.heimerl965e7572011-11-26 03:16:54 +0000880 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800881 async_event_thrd = new Thread();
882 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000883
884 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800885 if (!restart())
886 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000887
kurtis.heimerl965e7572011-11-26 03:16:54 +0000888 // Display usrp time
889 double time_now = usrp_dev->get_time_now().get_real_secs();
890 LOG(INFO) << "The current time is " << time_now << " seconds";
891
892 started = true;
893 return true;
894}
895
896bool uhd_device::stop()
897{
Thomas Tsou2c791102013-11-15 23:00:28 -0500898 if (!started)
899 return false;
900
901 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000902 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
903
904 usrp_dev->issue_stream_cmd(stream_cmd);
905
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800906 async_event_thrd->cancel();
907 async_event_thrd->join();
908 delete async_event_thrd;
909
kurtis.heimerl965e7572011-11-26 03:16:54 +0000910 started = false;
911 return true;
912}
913
Thomas Tsou7553aa92013-11-08 12:50:03 -0500914void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000915{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500916 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000917 return;
918}
919
kurtis.heimerld4be0742011-11-26 03:17:46 +0000920int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000921{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000922 uhd::time_spec_t ts;
923
kurtis.heimerld4be0742011-11-26 03:17:46 +0000924 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000925 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000926
927 switch (md.error_code) {
928 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000929 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800930 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000931 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
932 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
933 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
934 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
935 default:
936 return ERROR_UNHANDLED;
937 }
938 }
939
kurtis.heimerl965e7572011-11-26 03:16:54 +0000940 // Missing timestamp
941 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000942 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000943 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000944 }
945
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000946 ts = md.time_spec;
947
kurtis.heimerl965e7572011-11-26 03:16:54 +0000948 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000949 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000950 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000951 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
952 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000953 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000955 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000956 }
957
958 return 0;
959}
960
Thomas Tsou204a9f12013-10-29 18:34:16 -0400961int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
962 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000963{
964 ssize_t rc;
965 uhd::time_spec_t ts;
966 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967
Thomas Tsou204a9f12013-10-29 18:34:16 -0400968 if (bufs.size() != chans) {
969 LOG(ALERT) << "Invalid channel combination " << bufs.size();
970 return -1;
971 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000972
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000973 *overrun = false;
974 *underrun = false;
975
kurtis.heimerl965e7572011-11-26 03:16:54 +0000976 // Shift read time with respect to transmit clock
977 timestamp += ts_offset;
978
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400979 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000980 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000981
982 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400983 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000984 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400985 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800986 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000987 return 0;
988 }
989
Thomas Tsou204a9f12013-10-29 18:34:16 -0400990 // Create vector buffer
991 std::vector<std::vector<short> >
992 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
993
994 std::vector<short *> pkt_ptrs;
995 for (size_t i = 0; i < pkt_bufs.size(); i++)
996 pkt_ptrs.push_back(&pkt_bufs[i].front());
997
kurtis.heimerl965e7572011-11-26 03:16:54 +0000998 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400999 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001000 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001001 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1002 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001003 thread_enable_cancel(true);
1004
kurtis.heimerl965e7572011-11-26 03:16:54 +00001005 rx_pkt_cnt++;
1006
kurtis.heimerld4be0742011-11-26 03:17:46 +00001007 // Check for errors
1008 rc = check_rx_md_err(metadata, num_smpls);
1009 switch (rc) {
1010 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001011 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1012 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001013 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001014 case ERROR_TIMEOUT:
1015 // Assume stopping condition
1016 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001017 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001018 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001019 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001020 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001021 }
1022
kurtis.heimerl965e7572011-11-26 03:16:54 +00001023 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001024 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001025
Thomas Tsou204a9f12013-10-29 18:34:16 -04001026 for (size_t i = 0; i < rx_buffers.size(); i++) {
1027 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1028 num_smpls,
1029 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001030
Thomas Tsou204a9f12013-10-29 18:34:16 -04001031 // Continue on local overrun, exit on other errors
1032 if ((rc < 0)) {
1033 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001034 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001035 if (rc != smpl_buf::ERROR_OVERFLOW)
1036 return 0;
1037 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001038 }
1039 }
1040
1041 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001042 for (size_t i = 0; i < rx_buffers.size(); i++) {
1043 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1044 if ((rc < 0) || (rc != len)) {
1045 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001046 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001047 return 0;
1048 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001049 }
1050
1051 return len;
1052}
1053
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1055 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001056{
1057 uhd::tx_metadata_t metadata;
1058 metadata.has_time_spec = true;
1059 metadata.start_of_burst = false;
1060 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001061 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001062
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001063 *underrun = false;
1064
kurtis.heimerl965e7572011-11-26 03:16:54 +00001065 // No control packets
1066 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001067 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001068 return 0;
1069 }
1070
Thomas Tsou204a9f12013-10-29 18:34:16 -04001071 if (bufs.size() != chans) {
1072 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1073 return -1;
1074 }
1075
kurtis.heimerl965e7572011-11-26 03:16:54 +00001076 // Drop a fixed number of packets (magic value)
1077 if (!aligned) {
1078 drop_cnt++;
1079
1080 if (drop_cnt == 1) {
1081 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001082 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001083 metadata.end_of_burst = true;
1084 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001085 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001086 return len;
1087 } else {
1088 LOG(DEBUG) << "Aligning transmitter: start burst";
1089 metadata.start_of_burst = true;
1090 aligned = true;
1091 drop_cnt = 0;
1092 }
1093 }
1094
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001095 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001096 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001097 thread_enable_cancel(true);
1098
ttsoub371ed52012-01-09 18:11:34 +00001099 if (num_smpls != (unsigned) len) {
1100 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001101 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001102
1103 return num_smpls;
1104}
1105
1106bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1107{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001108 return true;
1109}
1110
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001111uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1112{
1113 double rf_spread, rf_freq;
1114 std::vector<double> freqs;
1115 uhd::tune_request_t treq(freq);
1116
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001117 if (dev_type == UMTRX) {
1118 if (offset > 0.0)
1119 return uhd::tune_request_t(freq, offset);
1120
1121 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1122 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1123 // only distort the signal (because cordic is not ideal).
1124 treq.target_freq = freq;
1125 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1126 treq.rf_freq = freq;
1127 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1128 treq.dsp_freq = 0.0;
1129 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001130 if (offset == 0.0)
1131 return treq;
1132
1133 return uhd::tune_request_t(freq, offset);
1134 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001135 LOG(ALERT) << chans << " channels unsupported";
1136 return treq;
1137 }
1138
1139 if (tx)
1140 freqs = tx_freqs;
1141 else
1142 freqs = rx_freqs;
1143
1144 /* Tune directly if other channel isn't tuned */
1145 if (freqs[!chan] < 10.0)
1146 return treq;
1147
1148 /* Find center frequency between channels */
1149 rf_spread = fabs(freqs[!chan] - freq);
1150 if (rf_spread > B2XX_CLK_RT) {
1151 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1152 return treq;
1153 }
1154
1155 rf_freq = (freqs[!chan] + freq) / 2.0f;
1156
1157 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1158 treq.target_freq = freq;
1159 treq.rf_freq = rf_freq;
1160
1161 return treq;
1162}
1163
1164bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1165{
1166 std::vector<double> freqs;
1167 uhd::tune_result_t tres;
1168 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1169
1170 if (tx) {
1171 tres = usrp_dev->set_tx_freq(treq, chan);
1172 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1173 } else {
1174 tres = usrp_dev->set_rx_freq(treq, chan);
1175 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1176 }
1177 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1178
Thomas Tsou8e17df72014-03-06 14:16:11 -05001179 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1180 return true;
1181
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001182 /* Manual RF policy means we intentionally tuned with a baseband
1183 * offset for dual-channel purposes. Now retune the other channel
1184 * with the opposite corresponding frequency offset
1185 */
1186 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1187 if (tx) {
1188 treq = select_freq(tx_freqs[!chan], !chan, true);
1189 tres = usrp_dev->set_tx_freq(treq, !chan);
1190 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1191 } else {
1192 treq = select_freq(rx_freqs[!chan], !chan, false);
1193 tres = usrp_dev->set_rx_freq(treq, !chan);
1194 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1195
1196 }
1197 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1198 }
1199
1200 return true;
1201}
1202
Thomas Tsou204a9f12013-10-29 18:34:16 -04001203bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001204{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001205 if (chan >= tx_freqs.size()) {
1206 LOG(ALERT) << "Requested non-existent channel " << chan;
1207 return false;
1208 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001209 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001210
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001211 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001212}
1213
Thomas Tsou204a9f12013-10-29 18:34:16 -04001214bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001215{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001216 if (chan >= rx_freqs.size()) {
1217 LOG(ALERT) << "Requested non-existent channel " << chan;
1218 return false;
1219 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001220 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001221
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001222 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001223}
1224
Thomas Tsou204a9f12013-10-29 18:34:16 -04001225double uhd_device::getTxFreq(size_t chan)
1226{
1227 if (chan >= tx_freqs.size()) {
1228 LOG(ALERT) << "Requested non-existent channel " << chan;
1229 return 0.0;
1230 }
1231
1232 return tx_freqs[chan];
1233}
1234
1235double uhd_device::getRxFreq(size_t chan)
1236{
1237 if (chan >= rx_freqs.size()) {
1238 LOG(ALERT) << "Requested non-existent channel " << chan;
1239 return 0.0;
1240 }
1241
1242 return rx_freqs[chan];
1243}
1244
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001245double uhd_device::fullScaleInputValue()
1246{
1247 if (dev_type == UMTRX)
1248 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1249 else
1250 return (double) SHRT_MAX * USRP_TX_AMPL;
1251}
1252
1253double uhd_device::fullScaleOutputValue()
1254{
1255 return (double) SHRT_MAX;
1256}
1257
kurtis.heimerl965e7572011-11-26 03:16:54 +00001258bool uhd_device::recv_async_msg()
1259{
ttsou60dc4c92012-08-08 23:30:23 +00001260 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001261
1262 thread_enable_cancel(false);
1263 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1264 thread_enable_cancel(true);
1265 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001266 return false;
1267
1268 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001269 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001270 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001271
1272 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1273 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1274 LOG(ERR) << str_code(md);
1275 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001276 }
1277
1278 return true;
1279}
1280
1281std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1282{
1283 std::ostringstream ost("UHD: ");
1284
1285 switch (metadata.error_code) {
1286 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1287 ost << "No error";
1288 break;
1289 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1290 ost << "No packet received, implementation timed-out";
1291 break;
1292 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1293 ost << "A stream command was issued in the past";
1294 break;
1295 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1296 ost << "Expected another stream command";
1297 break;
1298 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1299 ost << "An internal receive buffer has filled";
1300 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001301 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1302 ost << "Multi-channel alignment failed";
1303 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001304 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1305 ost << "The packet could not be parsed";
1306 break;
1307 default:
1308 ost << "Unknown error " << metadata.error_code;
1309 }
1310
1311 if (metadata.has_time_spec)
1312 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1313
1314 return ost.str();
1315}
1316
1317std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1318{
1319 std::ostringstream ost("UHD: ");
1320
1321 switch (metadata.event_code) {
1322 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1323 ost << "A packet was successfully transmitted";
1324 break;
1325 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1326 ost << "An internal send buffer has emptied";
1327 break;
1328 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1329 ost << "Packet loss between host and device";
1330 break;
1331 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1332 ost << "Packet time was too late or too early";
1333 break;
1334 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1335 ost << "Underflow occurred inside a packet";
1336 break;
1337 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1338 ost << "Packet loss within a burst";
1339 break;
1340 default:
1341 ost << "Unknown error " << metadata.event_code;
1342 }
1343
1344 if (metadata.has_time_spec)
1345 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1346
1347 return ost.str();
1348}
1349
1350smpl_buf::smpl_buf(size_t len, double rate)
1351 : buf_len(len), clk_rt(rate),
1352 time_start(0), time_end(0), data_start(0), data_end(0)
1353{
1354 data = new uint32_t[len];
1355}
1356
1357smpl_buf::~smpl_buf()
1358{
1359 delete[] data;
1360}
1361
1362ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1363{
1364 if (timestamp < time_start)
1365 return ERROR_TIMESTAMP;
1366 else if (timestamp >= time_end)
1367 return 0;
1368 else
1369 return time_end - timestamp;
1370}
1371
1372ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1373{
1374 return avail_smpls(convert_time(timespec, clk_rt));
1375}
1376
1377ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1378{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001379 int type_sz = 2 * sizeof(short);
1380
kurtis.heimerl965e7572011-11-26 03:16:54 +00001381 // Check for valid read
1382 if (timestamp < time_start)
1383 return ERROR_TIMESTAMP;
1384 if (timestamp >= time_end)
1385 return 0;
1386 if (len >= buf_len)
1387 return ERROR_READ;
1388
1389 // How many samples should be copied
1390 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001391 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001392 num_smpls = len;
1393
1394 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001395 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001396
1397 // Read it
1398 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001399 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001400 memcpy(buf, data + read_start, numBytes);
1401 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001402 size_t first_cp = (buf_len - read_start) * type_sz;
1403 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001404
1405 memcpy(buf, data + read_start, first_cp);
1406 memcpy((char*) buf + first_cp, data, second_cp);
1407 }
1408
1409 data_start = (read_start + len) % buf_len;
1410 time_start = timestamp + len;
1411
1412 if (time_start > time_end)
1413 return ERROR_READ;
1414 else
1415 return num_smpls;
1416}
1417
1418ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1419{
1420 return read(buf, len, convert_time(ts, clk_rt));
1421}
1422
1423ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1424{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001425 int type_sz = 2 * sizeof(short);
1426
kurtis.heimerl965e7572011-11-26 03:16:54 +00001427 // Check for valid write
1428 if ((len == 0) || (len >= buf_len))
1429 return ERROR_WRITE;
1430 if ((timestamp + len) <= time_end)
1431 return ERROR_TIMESTAMP;
1432
1433 // Starting index
1434 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1435
1436 // Write it
1437 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001438 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001439 memcpy(data + write_start, buf, numBytes);
1440 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001441 size_t first_cp = (buf_len - write_start) * type_sz;
1442 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001443
1444 memcpy(data + write_start, buf, first_cp);
1445 memcpy(data, (char*) buf + first_cp, second_cp);
1446 }
1447
1448 data_end = (write_start + len) % buf_len;
1449 time_end = timestamp + len;
1450
Thomas Tsou18d3b832014-02-13 14:55:23 -05001451 if (!data_start)
1452 data_start = write_start;
1453
kurtis.heimerl965e7572011-11-26 03:16:54 +00001454 if (((write_start + len) > buf_len) && (data_end > data_start))
1455 return ERROR_OVERFLOW;
1456 else if (time_end <= time_start)
1457 return ERROR_WRITE;
1458 else
1459 return len;
1460}
1461
1462ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1463{
1464 return write(buf, len, convert_time(ts, clk_rt));
1465}
1466
Tom Tsou1ae25562014-12-05 12:56:34 -08001467std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001468{
1469 std::ostringstream ost("Sample buffer: ");
1470
Tom Tsou1ae25562014-12-05 12:56:34 -08001471 ost << "timestamp = " << ts;
1472 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001473 ost << ", time_start = " << time_start;
1474 ost << ", time_end = " << time_end;
1475 ost << ", data_start = " << data_start;
1476 ost << ", data_end = " << data_end;
1477
1478 return ost.str();
1479}
1480
1481std::string smpl_buf::str_code(ssize_t code)
1482{
1483 switch (code) {
1484 case ERROR_TIMESTAMP:
1485 return "Sample buffer: Requested timestamp is not valid";
1486 case ERROR_READ:
1487 return "Sample buffer: Read error";
1488 case ERROR_WRITE:
1489 return "Sample buffer: Write error";
1490 case ERROR_OVERFLOW:
1491 return "Sample buffer: Overrun";
1492 default:
1493 return "Sample buffer: Unknown error";
1494 }
1495}
1496
Thomas Tsou8e17df72014-03-06 14:16:11 -05001497RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1498 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001499{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001500 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001501}