blob: 272ce60dbc7b6f4f9889f2b3b1edf8c82bc4bb1f [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{
Alexander Chemerisc052aa12015-06-10 21:47:33 -0400213 return (TIMESTAMP)(ts.get_real_secs() * rate + 0.5);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000214}
215
216/*
217 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
218 or UHD style timestamps. Time conversions are handled
219 internally or accessable through the static convert calls.
220*/
221class smpl_buf {
222public:
223 /** Sample buffer constructor
224 @param len number of 32-bit samples the buffer should hold
225 @param rate sample clockrate
226 @param timestamp
227 */
228 smpl_buf(size_t len, double rate);
229 ~smpl_buf();
230
231 /** Query number of samples available for reading
232 @param timestamp time of first sample
233 @return number of available samples or error
234 */
235 ssize_t avail_smpls(TIMESTAMP timestamp) const;
236 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
237
238 /** Read and write
239 @param buf pointer to buffer
240 @param len number of samples desired to read or write
241 @param timestamp time of first stample
242 @return number of actual samples read or written or error
243 */
244 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
245 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
246 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
247 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
248
249 /** Buffer status string
250 @return a formatted string describing internal buffer state
251 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800252 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000253
254 /** Formatted error string
255 @param code an error code
256 @return a formatted error string
257 */
258 static std::string str_code(ssize_t code);
259
260 enum err_code {
261 ERROR_TIMESTAMP = -1,
262 ERROR_READ = -2,
263 ERROR_WRITE = -3,
264 ERROR_OVERFLOW = -4
265 };
266
267private:
268 uint32_t *data;
269 size_t buf_len;
270
271 double clk_rt;
272
273 TIMESTAMP time_start;
274 TIMESTAMP time_end;
275
276 size_t data_start;
277 size_t data_end;
278};
279
280/*
281 uhd_device - UHD implementation of the Device interface. Timestamped samples
282 are sent to and received from the device. An intermediate buffer
283 on the receive side collects and aligns packets of samples.
284 Events and errors such as underruns are reported asynchronously
285 by the device and received in a separate thread.
286*/
287class uhd_device : public RadioDevice {
288public:
Thomas Tsou8e17df72014-03-06 14:16:11 -0500289 uhd_device(size_t sps, size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000290 ~uhd_device();
291
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400292 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000293 bool start();
294 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800295 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500296 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400297 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000298
Thomas Tsou204a9f12013-10-29 18:34:16 -0400299 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000300 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
301
Thomas Tsou204a9f12013-10-29 18:34:16 -0400302 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000303 TIMESTAMP timestamp, bool isControl);
304
305 bool updateAlignment(TIMESTAMP timestamp);
306
Thomas Tsou204a9f12013-10-29 18:34:16 -0400307 bool setTxFreq(double wFreq, size_t chan);
308 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000309
Thomas Tsou18d3b832014-02-13 14:55:23 -0500310 inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
311 inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000312
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300313 double fullScaleInputValue();
314 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000315
Thomas Tsou204a9f12013-10-29 18:34:16 -0400316 double setRxGain(double db, size_t chan);
317 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000318 double maxRxGain(void) { return rx_gain_max; }
319 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320
Thomas Tsou204a9f12013-10-29 18:34:16 -0400321 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000322 double maxTxGain(void) { return tx_gain_max; }
323 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324
Thomas Tsou204a9f12013-10-29 18:34:16 -0400325 double getTxFreq(size_t chan);
326 double getRxFreq(size_t chan);
327 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000328
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400329 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000330 inline double numberRead() { return rx_pkt_cnt; }
331 inline double numberWritten() { return 0; }
332
333 /** Receive and process asynchronous message
334 @return true if message received or false on timeout or error
335 */
336 bool recv_async_msg();
337
kurtis.heimerld4be0742011-11-26 03:17:46 +0000338 enum err_code {
339 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800340 ERROR_TIMEOUT = -2,
341 ERROR_UNRECOVERABLE = -3,
342 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000343 };
344
kurtis.heimerl965e7572011-11-26 03:16:54 +0000345private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000346 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400347 uhd::tx_streamer::sptr tx_stream;
348 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400349 enum TxWindowType tx_window;
350 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000351
Thomas Tsou204a9f12013-10-29 18:34:16 -0400352 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400353 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000354
Thomas Tsou204a9f12013-10-29 18:34:16 -0400355 double tx_gain_min, tx_gain_max;
356 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500357 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000358
Thomas Tsou204a9f12013-10-29 18:34:16 -0400359 std::vector<double> tx_gains, rx_gains;
360 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000361 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000362
363 bool started;
364 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000365
366 size_t rx_pkt_cnt;
367 size_t drop_cnt;
368 uhd::time_spec_t prev_ts;
369
Thomas Tsou18d3b832014-02-13 14:55:23 -0500370 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400371 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000372
kurtis.heimerl02d04052011-11-26 03:17:10 +0000373 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400374 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400375 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000376 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000377 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000378 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000379
kurtis.heimerl965e7572011-11-26 03:16:54 +0000380 std::string str_code(uhd::rx_metadata_t metadata);
381 std::string str_code(uhd::async_metadata_t metadata);
382
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500383 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
384 bool set_freq(double freq, size_t chan, bool tx);
385
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800386 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500387 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800388 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000389};
390
391void *async_event_loop(uhd_device *dev)
392{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500393 dev->setPriority(0.43);
394
kurtis.heimerl965e7572011-11-26 03:16:54 +0000395 while (1) {
396 dev->recv_async_msg();
397 pthread_testcancel();
398 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500399
400 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000401}
402
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000403/*
404 Catch and drop underrun 'U' and overrun 'O' messages from stdout
405 since we already report using the logging facility. Direct
406 everything else appropriately.
407 */
408void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
409{
410 switch (type) {
411 case uhd::msg::status:
412 LOG(INFO) << msg;
413 break;
414 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000415 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000416 break;
417 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000418 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000419 break;
420 case uhd::msg::fastpath:
421 break;
422 }
423}
424
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800425static void thread_enable_cancel(bool cancel)
426{
427 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
428 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
429}
430
Thomas Tsou8e17df72014-03-06 14:16:11 -0500431uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 : tx_gain_min(0.0), tx_gain_max(0.0),
433 rx_gain_min(0.0), rx_gain_max(0.0),
434 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000435 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500436 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000437{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400438 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400439 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500440 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500441 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442}
443
444uhd_device::~uhd_device()
445{
446 stop();
447
Thomas Tsou204a9f12013-10-29 18:34:16 -0400448 for (size_t i = 0; i < rx_buffers.size(); i++)
449 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000450}
451
kurtis.heimerl24481de2011-11-26 03:17:18 +0000452void uhd_device::init_gains()
453{
454 uhd::gain_range_t range;
455
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400456 if (dev_type == UMTRX) {
457 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
458 if (gain_stages[0] == "VGA") {
459 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
460 }
461 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
462 range = usrp_dev->get_tx_gain_range();
463 tx_gain_min = range.start();
464 tx_gain_max = range.stop();
465 } else {
466 range = usrp_dev->get_tx_gain_range("VGA2");
467 tx_gain_min = UMTRX_VGA1_DEF + range.start();
468 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
469 }
470 } else {
471 range = usrp_dev->get_tx_gain_range();
472 tx_gain_min = range.start();
473 tx_gain_max = range.stop();
474 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000475
476 range = usrp_dev->get_rx_gain_range();
477 rx_gain_min = range.start();
478 rx_gain_max = range.stop();
479
Thomas Tsou204a9f12013-10-29 18:34:16 -0400480 for (size_t i = 0; i < tx_gains.size(); i++) {
481 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
482 tx_gains[i] = usrp_dev->get_tx_gain(i);
483 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000484
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 for (size_t i = 0; i < rx_gains.size(); i++) {
486 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
487 rx_gains[i] = usrp_dev->get_rx_gain(i);
488 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000489
490 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400491
kurtis.heimerl24481de2011-11-26 03:17:18 +0000492}
493
Thomas Tsou02d88d12013-04-05 15:36:30 -0400494int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000495{
Thomas Tsou092f7572013-04-04 17:04:39 -0400496 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000497
Thomas Tsou7e068062013-04-08 19:39:37 -0400498 try {
499 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400500 } catch (const std::exception &ex) {
501 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
502 LOG(ALERT) << ex.what();
503 return -1;
504 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000505
Thomas Tsou092f7572013-04-04 17:04:39 -0400506 actual = usrp_dev->get_master_clock_rate();
507 offset = fabs(clk_rate - actual);
508
509 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000510 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400511 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400512 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400513 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000514 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400515
516 return 0;
517}
518
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400519int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400520{
Thomas Tsou092f7572013-04-04 17:04:39 -0400521 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400522 double tx_offset, rx_offset;
523
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100524 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800525 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400526 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400527 return -1;
528 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100529 else if (dev_type == E1XX) {
530 if (set_master_clk(E1XX_CLK_RT) < 0)
531 return -1;
532 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000533
534 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400535 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400536 usrp_dev->set_tx_rate(tx_rate);
537 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400538 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400539 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400540 LOG(ALERT) << ex.what();
541 return -1;
542 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400543 this->tx_rate = usrp_dev->get_tx_rate();
544 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000545
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400546 tx_offset = fabs(this->tx_rate - tx_rate);
547 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400548 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000549 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400550 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
551 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400552 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000553 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000554
Thomas Tsou02d88d12013-04-05 15:36:30 -0400555 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000556}
557
Thomas Tsou204a9f12013-10-29 18:34:16 -0400558double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000559{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400560 if (chan >= tx_gains.size()) {
561 LOG(ALERT) << "Requested non-existent channel" << chan;
562 return 0.0f;
563 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000564
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400565 if (dev_type == UMTRX) {
566 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
567 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
568 usrp_dev->set_tx_gain(db, chan);
569 } else {
570 // New UHD versions support split configuration of
571 // Tx gain stages. We utilize this to set the gain
572 // configuration, optimal for the Tx signal quality.
573 // From our measurements, VGA1 must be 18dB plus-minus
574 // one and VGA2 is the best when 23dB or lower.
575 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
576 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
577 }
578 } else {
579 usrp_dev->set_tx_gain(db, chan);
580 }
581
Thomas Tsou204a9f12013-10-29 18:34:16 -0400582 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000583
Thomas Tsou204a9f12013-10-29 18:34:16 -0400584 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
585
586 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000587}
588
Thomas Tsou204a9f12013-10-29 18:34:16 -0400589double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000590{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400591 if (chan >= rx_gains.size()) {
592 LOG(ALERT) << "Requested non-existent channel " << chan;
593 return 0.0f;
594 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000595
Thomas Tsou204a9f12013-10-29 18:34:16 -0400596 usrp_dev->set_rx_gain(db, chan);
597 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000598
Thomas Tsou204a9f12013-10-29 18:34:16 -0400599 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
600
601 return rx_gains[chan];
602}
603
604double uhd_device::getRxGain(size_t chan)
605{
606 if (chan >= rx_gains.size()) {
607 LOG(ALERT) << "Requested non-existent channel " << chan;
608 return 0.0f;
609 }
610
611 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000612}
613
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000614/*
615 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400616 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000617 deal with the transport latency. Reject the USRP1 because UHD doesn't
618 support timestamped samples with it.
619 */
620bool uhd_device::parse_dev_type()
621{
622 std::string mboard_str, dev_str;
623 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800624 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
625 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000626
627 prop_tree = usrp_dev->get_device()->get_tree();
628 dev_str = prop_tree->access<std::string>("/name").get();
629 mboard_str = usrp_dev->get_mboard_name();
630
631 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400632 usrp2_str = dev_str.find("USRP2");
633 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400634 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400635 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100636 e100_str = mboard_str.find("E100");
637 e110_str = mboard_str.find("E110");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800638 e310_str = mboard_str.find("E310");
639 x300_str = mboard_str.find("X300");
640 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400641 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000642
643 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000644 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
645 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400646 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000647 return false;
648 }
649
Thomas Tsoucb69f082013-04-08 14:18:26 -0400650 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400651 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400652 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400653 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500654 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500655 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400656 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500657 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500658 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100659 } else if (e100_str != std::string::npos) {
660 tx_window = TX_WINDOW_FIXED;
661 dev_type = E1XX;
662 } else if (e110_str != std::string::npos) {
663 tx_window = TX_WINDOW_FIXED;
664 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400665 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500666 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400667 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800668 } else if (e310_str != std::string::npos) {
669 tx_window = TX_WINDOW_FIXED;
670 dev_type = E3XX;
671 } else if (x300_str != std::string::npos) {
672 tx_window = TX_WINDOW_FIXED;
673 dev_type = X3XX;
674 } else if (x310_str != std::string::npos) {
675 tx_window = TX_WINDOW_FIXED;
676 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400677 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500678 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400679 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400680 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400681 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400682 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000683 }
684
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500685 if (tx_window == TX_WINDOW_USRP1) {
686 LOG(INFO) << "Using USRP1 type transmit window for "
687 << dev_str << " " << mboard_str;
688 } else {
689 LOG(INFO) << "Using fixed transmit window for "
690 << dev_str << " " << mboard_str;
691 }
692
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000693 return true;
694}
695
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400696int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000697{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000698 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000699 uhd::device_addr_t addr(args);
700 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000701 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000702 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400703 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000704 }
705
706 // Use the first found device
707 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000708 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700709 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000710 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700711 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400712 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000713 }
714
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000715 // Check for a valid device type and set bus type
716 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400717 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000718
Thomas Tsou204a9f12013-10-29 18:34:16 -0400719 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500720 if ((dev_type == B210) && (chans == 2)) {
721 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400722 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400723 usrp_dev->set_tx_subdev_spec(subdev_spec);
724 usrp_dev->set_rx_subdev_spec(subdev_spec);
725 } else if (chans != 1) {
726 LOG(ALERT) << "Invalid channel combination for device";
727 return -1;
728 }
729
730 tx_freqs.resize(chans);
731 rx_freqs.resize(chans);
732 tx_gains.resize(chans);
733 rx_gains.resize(chans);
734 rx_buffers.resize(chans);
735
Thomas Tsou010fff72013-10-16 00:31:18 -0400736 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400737 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400738
kurtis.heimerl965e7572011-11-26 03:16:54 +0000739 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500740 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400741 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500742 if (diversity)
743 _rx_rate = select_rate(dev_type, 1, true);
744 else
745 _rx_rate = _tx_rate / sps;
746
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500747 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
748 return -1;
749 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400750 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000751
Alexander Chemerise1714252015-04-28 23:09:02 -0400752 // Set RF frontend bandwidth
753 if (dev_type == UMTRX) {
754 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
755 for (size_t i = 0; i < chans; i++) {
756 usrp_dev->set_tx_bandwidth(500*1000*2, i);
757 if (!diversity)
758 usrp_dev->set_rx_bandwidth(500*1000*2, i);
759 }
760 }
761
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400762 /* Create TX and RX streamers */
763 uhd::stream_args_t stream_args("sc16");
764 for (size_t i = 0; i < chans; i++)
765 stream_args.channels.push_back(i);
766
767 tx_stream = usrp_dev->get_tx_stream(stream_args);
768 rx_stream = usrp_dev->get_rx_stream(stream_args);
769
770 /* Number of samples per over-the-wire packet */
771 tx_spp = tx_stream->get_max_num_samps();
772 rx_spp = rx_stream->get_max_num_samps();
773
kurtis.heimerl965e7572011-11-26 03:16:54 +0000774 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400775 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400776 for (size_t i = 0; i < rx_buffers.size(); i++)
777 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778
779 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500780 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400781 if (offset == 0.0) {
782 LOG(ERR) << "Unsupported configuration, no correction applied";
783 ts_offset = 0;
784 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400785 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400786 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000787
kurtis.heimerl02d04052011-11-26 03:17:10 +0000788 // Initialize and shadow gain values
789 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000790
kurtis.heimerl965e7572011-11-26 03:16:54 +0000791 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000792 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500794 if (diversity)
795 return DIVERSITY;
796
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400797 switch (dev_type) {
798 case B100:
799 return RESAMP_64M;
800 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800801 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400802 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500803 case B200:
804 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100805 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800806 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500807 default:
808 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400809 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400810
811 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000812}
813
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000814bool uhd_device::flush_recv(size_t num_pkts)
815{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000816 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000817 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800818 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000819
Thomas Tsou18d3b832014-02-13 14:55:23 -0500820 std::vector<std::vector<short> >
821 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000822
Thomas Tsou18d3b832014-02-13 14:55:23 -0500823 std::vector<short *> pkt_ptrs;
824 for (size_t i = 0; i < pkt_bufs.size(); i++)
825 pkt_ptrs.push_back(&pkt_bufs[i].front());
826
827 ts_initial = 0;
828 while (!ts_initial || (num_pkts-- > 0)) {
829 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400830 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000831 if (!num_smpls) {
832 switch (md.error_code) {
833 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800834 LOG(ALERT) << "Device timed out";
835 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000836 default:
837 continue;
838 }
839 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500840
841 ts_initial = convert_time(md.time_spec, rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000842 }
843
Thomas Tsou18d3b832014-02-13 14:55:23 -0500844 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
845
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000846 return true;
847}
848
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800849bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000850{
Thomas Tsouc2839162014-03-27 13:52:58 -0400851 /* Allow 100 ms delay to align multi-channel streams */
852 double delay = 0.1;
853
kurtis.heimerl68292102011-11-26 03:17:28 +0000854 aligned = false;
855
Thomas Tsouc2839162014-03-27 13:52:58 -0400856 uhd::time_spec_t current = usrp_dev->get_time_now();
857
Thomas Tsou204a9f12013-10-29 18:34:16 -0400858 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400859 cmd.stream_now = false;
860 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400861
kurtis.heimerl68292102011-11-26 03:17:28 +0000862 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500863
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800864 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000865}
866
kurtis.heimerl965e7572011-11-26 03:16:54 +0000867bool uhd_device::start()
868{
869 LOG(INFO) << "Starting USRP...";
870
871 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000872 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000873 return false;
874 }
875
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400876 // Register msg handler
877 uhd::msg::register_handler(&uhd_msg_handler);
878
kurtis.heimerl965e7572011-11-26 03:16:54 +0000879 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800880 async_event_thrd = new Thread();
881 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000882
883 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800884 if (!restart())
885 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000886
kurtis.heimerl965e7572011-11-26 03:16:54 +0000887 // Display usrp time
888 double time_now = usrp_dev->get_time_now().get_real_secs();
889 LOG(INFO) << "The current time is " << time_now << " seconds";
890
891 started = true;
892 return true;
893}
894
895bool uhd_device::stop()
896{
Thomas Tsou2c791102013-11-15 23:00:28 -0500897 if (!started)
898 return false;
899
900 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000901 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
902
903 usrp_dev->issue_stream_cmd(stream_cmd);
904
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800905 async_event_thrd->cancel();
906 async_event_thrd->join();
907 delete async_event_thrd;
908
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909 started = false;
910 return true;
911}
912
Thomas Tsou7553aa92013-11-08 12:50:03 -0500913void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000914{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500915 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000916 return;
917}
918
kurtis.heimerld4be0742011-11-26 03:17:46 +0000919int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000921 uhd::time_spec_t ts;
922
kurtis.heimerld4be0742011-11-26 03:17:46 +0000923 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000924 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000925
926 switch (md.error_code) {
927 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000928 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800929 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000930 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
931 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
932 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
933 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
934 default:
935 return ERROR_UNHANDLED;
936 }
937 }
938
kurtis.heimerl965e7572011-11-26 03:16:54 +0000939 // Missing timestamp
940 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000941 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000942 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000943 }
944
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000945 ts = md.time_spec;
946
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000948 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000949 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000950 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
951 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000952 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000953 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000954 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000955 }
956
957 return 0;
958}
959
Thomas Tsou204a9f12013-10-29 18:34:16 -0400960int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
961 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000962{
963 ssize_t rc;
964 uhd::time_spec_t ts;
965 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000966
Thomas Tsou204a9f12013-10-29 18:34:16 -0400967 if (bufs.size() != chans) {
968 LOG(ALERT) << "Invalid channel combination " << bufs.size();
969 return -1;
970 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000971
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000972 *overrun = false;
973 *underrun = false;
974
kurtis.heimerl965e7572011-11-26 03:16:54 +0000975 // Shift read time with respect to transmit clock
976 timestamp += ts_offset;
977
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400978 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000979 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000980
981 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400982 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000983 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400984 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800985 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000986 return 0;
987 }
988
Thomas Tsou204a9f12013-10-29 18:34:16 -0400989 // Create vector buffer
990 std::vector<std::vector<short> >
991 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
992
993 std::vector<short *> pkt_ptrs;
994 for (size_t i = 0; i < pkt_bufs.size(); i++)
995 pkt_ptrs.push_back(&pkt_bufs[i].front());
996
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400998 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800999 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001000 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1001 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001002 thread_enable_cancel(true);
1003
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 rx_pkt_cnt++;
1005
kurtis.heimerld4be0742011-11-26 03:17:46 +00001006 // Check for errors
1007 rc = check_rx_md_err(metadata, num_smpls);
1008 switch (rc) {
1009 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001010 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1011 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001012 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001013 case ERROR_TIMEOUT:
1014 // Assume stopping condition
1015 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001016 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001017 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001018 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001019 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001020 }
1021
kurtis.heimerl965e7572011-11-26 03:16:54 +00001022 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001023 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001024
Thomas Tsou204a9f12013-10-29 18:34:16 -04001025 for (size_t i = 0; i < rx_buffers.size(); i++) {
1026 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1027 num_smpls,
1028 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029
Thomas Tsou204a9f12013-10-29 18:34:16 -04001030 // Continue on local overrun, exit on other errors
1031 if ((rc < 0)) {
1032 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001033 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001034 if (rc != smpl_buf::ERROR_OVERFLOW)
1035 return 0;
1036 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001037 }
1038 }
1039
1040 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001041 for (size_t i = 0; i < rx_buffers.size(); i++) {
1042 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1043 if ((rc < 0) || (rc != len)) {
1044 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001045 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001046 return 0;
1047 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001048 }
1049
1050 return len;
1051}
1052
Thomas Tsou204a9f12013-10-29 18:34:16 -04001053int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1054 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055{
1056 uhd::tx_metadata_t metadata;
1057 metadata.has_time_spec = true;
1058 metadata.start_of_burst = false;
1059 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001060 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001061
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001062 *underrun = false;
1063
kurtis.heimerl965e7572011-11-26 03:16:54 +00001064 // No control packets
1065 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001066 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001067 return 0;
1068 }
1069
Thomas Tsou204a9f12013-10-29 18:34:16 -04001070 if (bufs.size() != chans) {
1071 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1072 return -1;
1073 }
1074
kurtis.heimerl965e7572011-11-26 03:16:54 +00001075 // Drop a fixed number of packets (magic value)
1076 if (!aligned) {
1077 drop_cnt++;
1078
1079 if (drop_cnt == 1) {
1080 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001081 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001082 metadata.end_of_burst = true;
1083 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001084 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001085 return len;
1086 } else {
1087 LOG(DEBUG) << "Aligning transmitter: start burst";
1088 metadata.start_of_burst = true;
1089 aligned = true;
1090 drop_cnt = 0;
1091 }
1092 }
1093
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001094 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001095 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001096 thread_enable_cancel(true);
1097
ttsoub371ed52012-01-09 18:11:34 +00001098 if (num_smpls != (unsigned) len) {
1099 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001100 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001101
1102 return num_smpls;
1103}
1104
1105bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1106{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001107 return true;
1108}
1109
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001110uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1111{
1112 double rf_spread, rf_freq;
1113 std::vector<double> freqs;
1114 uhd::tune_request_t treq(freq);
1115
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001116 if (dev_type == UMTRX) {
1117 if (offset > 0.0)
1118 return uhd::tune_request_t(freq, offset);
1119
1120 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1121 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1122 // only distort the signal (because cordic is not ideal).
1123 treq.target_freq = freq;
1124 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1125 treq.rf_freq = freq;
1126 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1127 treq.dsp_freq = 0.0;
1128 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001129 if (offset == 0.0)
1130 return treq;
1131
1132 return uhd::tune_request_t(freq, offset);
1133 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001134 LOG(ALERT) << chans << " channels unsupported";
1135 return treq;
1136 }
1137
1138 if (tx)
1139 freqs = tx_freqs;
1140 else
1141 freqs = rx_freqs;
1142
1143 /* Tune directly if other channel isn't tuned */
1144 if (freqs[!chan] < 10.0)
1145 return treq;
1146
1147 /* Find center frequency between channels */
1148 rf_spread = fabs(freqs[!chan] - freq);
1149 if (rf_spread > B2XX_CLK_RT) {
1150 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1151 return treq;
1152 }
1153
1154 rf_freq = (freqs[!chan] + freq) / 2.0f;
1155
1156 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1157 treq.target_freq = freq;
1158 treq.rf_freq = rf_freq;
1159
1160 return treq;
1161}
1162
1163bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1164{
1165 std::vector<double> freqs;
1166 uhd::tune_result_t tres;
1167 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1168
1169 if (tx) {
1170 tres = usrp_dev->set_tx_freq(treq, chan);
1171 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1172 } else {
1173 tres = usrp_dev->set_rx_freq(treq, chan);
1174 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1175 }
1176 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1177
Thomas Tsou8e17df72014-03-06 14:16:11 -05001178 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1179 return true;
1180
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001181 /* Manual RF policy means we intentionally tuned with a baseband
1182 * offset for dual-channel purposes. Now retune the other channel
1183 * with the opposite corresponding frequency offset
1184 */
1185 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1186 if (tx) {
1187 treq = select_freq(tx_freqs[!chan], !chan, true);
1188 tres = usrp_dev->set_tx_freq(treq, !chan);
1189 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1190 } else {
1191 treq = select_freq(rx_freqs[!chan], !chan, false);
1192 tres = usrp_dev->set_rx_freq(treq, !chan);
1193 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1194
1195 }
1196 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1197 }
1198
1199 return true;
1200}
1201
Thomas Tsou204a9f12013-10-29 18:34:16 -04001202bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001203{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001204 if (chan >= tx_freqs.size()) {
1205 LOG(ALERT) << "Requested non-existent channel " << chan;
1206 return false;
1207 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001208 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001209
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001210 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001211}
1212
Thomas Tsou204a9f12013-10-29 18:34:16 -04001213bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001214{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001215 if (chan >= rx_freqs.size()) {
1216 LOG(ALERT) << "Requested non-existent channel " << chan;
1217 return false;
1218 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001219 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001220
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001221 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001222}
1223
Thomas Tsou204a9f12013-10-29 18:34:16 -04001224double uhd_device::getTxFreq(size_t chan)
1225{
1226 if (chan >= tx_freqs.size()) {
1227 LOG(ALERT) << "Requested non-existent channel " << chan;
1228 return 0.0;
1229 }
1230
1231 return tx_freqs[chan];
1232}
1233
1234double uhd_device::getRxFreq(size_t chan)
1235{
1236 if (chan >= rx_freqs.size()) {
1237 LOG(ALERT) << "Requested non-existent channel " << chan;
1238 return 0.0;
1239 }
1240
1241 return rx_freqs[chan];
1242}
1243
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001244double uhd_device::fullScaleInputValue()
1245{
1246 if (dev_type == UMTRX)
1247 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1248 else
1249 return (double) SHRT_MAX * USRP_TX_AMPL;
1250}
1251
1252double uhd_device::fullScaleOutputValue()
1253{
1254 return (double) SHRT_MAX;
1255}
1256
kurtis.heimerl965e7572011-11-26 03:16:54 +00001257bool uhd_device::recv_async_msg()
1258{
ttsou60dc4c92012-08-08 23:30:23 +00001259 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001260
1261 thread_enable_cancel(false);
1262 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1263 thread_enable_cancel(true);
1264 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001265 return false;
1266
1267 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001268 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001269 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001270
1271 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1272 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1273 LOG(ERR) << str_code(md);
1274 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001275 }
1276
1277 return true;
1278}
1279
1280std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1281{
1282 std::ostringstream ost("UHD: ");
1283
1284 switch (metadata.error_code) {
1285 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1286 ost << "No error";
1287 break;
1288 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1289 ost << "No packet received, implementation timed-out";
1290 break;
1291 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1292 ost << "A stream command was issued in the past";
1293 break;
1294 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1295 ost << "Expected another stream command";
1296 break;
1297 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1298 ost << "An internal receive buffer has filled";
1299 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001300 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1301 ost << "Multi-channel alignment failed";
1302 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001303 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1304 ost << "The packet could not be parsed";
1305 break;
1306 default:
1307 ost << "Unknown error " << metadata.error_code;
1308 }
1309
1310 if (metadata.has_time_spec)
1311 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1312
1313 return ost.str();
1314}
1315
1316std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1317{
1318 std::ostringstream ost("UHD: ");
1319
1320 switch (metadata.event_code) {
1321 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1322 ost << "A packet was successfully transmitted";
1323 break;
1324 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1325 ost << "An internal send buffer has emptied";
1326 break;
1327 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1328 ost << "Packet loss between host and device";
1329 break;
1330 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1331 ost << "Packet time was too late or too early";
1332 break;
1333 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1334 ost << "Underflow occurred inside a packet";
1335 break;
1336 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1337 ost << "Packet loss within a burst";
1338 break;
1339 default:
1340 ost << "Unknown error " << metadata.event_code;
1341 }
1342
1343 if (metadata.has_time_spec)
1344 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1345
1346 return ost.str();
1347}
1348
1349smpl_buf::smpl_buf(size_t len, double rate)
1350 : buf_len(len), clk_rt(rate),
1351 time_start(0), time_end(0), data_start(0), data_end(0)
1352{
1353 data = new uint32_t[len];
1354}
1355
1356smpl_buf::~smpl_buf()
1357{
1358 delete[] data;
1359}
1360
1361ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1362{
1363 if (timestamp < time_start)
1364 return ERROR_TIMESTAMP;
1365 else if (timestamp >= time_end)
1366 return 0;
1367 else
1368 return time_end - timestamp;
1369}
1370
1371ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1372{
1373 return avail_smpls(convert_time(timespec, clk_rt));
1374}
1375
1376ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1377{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001378 int type_sz = 2 * sizeof(short);
1379
kurtis.heimerl965e7572011-11-26 03:16:54 +00001380 // Check for valid read
1381 if (timestamp < time_start)
1382 return ERROR_TIMESTAMP;
1383 if (timestamp >= time_end)
1384 return 0;
1385 if (len >= buf_len)
1386 return ERROR_READ;
1387
1388 // How many samples should be copied
1389 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001390 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001391 num_smpls = len;
1392
1393 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001394 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001395
1396 // Read it
1397 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001398 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001399 memcpy(buf, data + read_start, numBytes);
1400 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001401 size_t first_cp = (buf_len - read_start) * type_sz;
1402 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001403
1404 memcpy(buf, data + read_start, first_cp);
1405 memcpy((char*) buf + first_cp, data, second_cp);
1406 }
1407
1408 data_start = (read_start + len) % buf_len;
1409 time_start = timestamp + len;
1410
1411 if (time_start > time_end)
1412 return ERROR_READ;
1413 else
1414 return num_smpls;
1415}
1416
1417ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1418{
1419 return read(buf, len, convert_time(ts, clk_rt));
1420}
1421
1422ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1423{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001424 int type_sz = 2 * sizeof(short);
1425
kurtis.heimerl965e7572011-11-26 03:16:54 +00001426 // Check for valid write
1427 if ((len == 0) || (len >= buf_len))
1428 return ERROR_WRITE;
1429 if ((timestamp + len) <= time_end)
1430 return ERROR_TIMESTAMP;
1431
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001432 if (timestamp < time_end) {
1433 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
1434 uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
1435 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
1436 // Do not return error here, because it's a rounding error and is not fatal
1437 }
1438 if (timestamp > time_end && time_end != 0) {
1439 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
1440 uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
1441 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
1442 // Do not return error here, because it's a rounding error and is not fatal
1443 }
1444
kurtis.heimerl965e7572011-11-26 03:16:54 +00001445 // Starting index
1446 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1447
1448 // Write it
1449 if ((write_start + len) < 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(data + write_start, buf, numBytes);
1452 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001453 size_t first_cp = (buf_len - write_start) * type_sz;
1454 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001455
1456 memcpy(data + write_start, buf, first_cp);
1457 memcpy(data, (char*) buf + first_cp, second_cp);
1458 }
1459
1460 data_end = (write_start + len) % buf_len;
1461 time_end = timestamp + len;
1462
Thomas Tsou18d3b832014-02-13 14:55:23 -05001463 if (!data_start)
1464 data_start = write_start;
1465
kurtis.heimerl965e7572011-11-26 03:16:54 +00001466 if (((write_start + len) > buf_len) && (data_end > data_start))
1467 return ERROR_OVERFLOW;
1468 else if (time_end <= time_start)
1469 return ERROR_WRITE;
1470 else
1471 return len;
1472}
1473
1474ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1475{
1476 return write(buf, len, convert_time(ts, clk_rt));
1477}
1478
Tom Tsou1ae25562014-12-05 12:56:34 -08001479std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001480{
1481 std::ostringstream ost("Sample buffer: ");
1482
Tom Tsou1ae25562014-12-05 12:56:34 -08001483 ost << "timestamp = " << ts;
1484 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001485 ost << ", time_start = " << time_start;
1486 ost << ", time_end = " << time_end;
1487 ost << ", data_start = " << data_start;
1488 ost << ", data_end = " << data_end;
1489
1490 return ost.str();
1491}
1492
1493std::string smpl_buf::str_code(ssize_t code)
1494{
1495 switch (code) {
1496 case ERROR_TIMESTAMP:
1497 return "Sample buffer: Requested timestamp is not valid";
1498 case ERROR_READ:
1499 return "Sample buffer: Read error";
1500 case ERROR_WRITE:
1501 return "Sample buffer: Write error";
1502 case ERROR_OVERFLOW:
1503 return "Sample buffer: Overrun";
1504 default:
1505 return "Sample buffer: Unknown error";
1506 }
1507}
1508
Thomas Tsou8e17df72014-03-06 14:16:11 -05001509RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1510 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001511{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001512 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001513}