blob: 0c572226c1a9c867187bbd1f367d260144a5e876 [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00002 * Device support for Ettus Research UHD driver
3 * Written by Thomas Tsou <ttsou@vt.edu>
4 *
5 * Copyright 2010,2011 Free Software Foundation, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000021
22#include "radioDevice.h"
23#include "Threads.h"
24#include "Logger.h"
ttsoub371ed52012-01-09 18:11:34 +000025#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000026#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000027#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000028#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000029#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000030
kurtis.heimerlce317332011-11-26 03:18:39 +000031#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
Thomas Tsou635f1bf2014-02-13 14:34:10 -050035#define B2XX_CLK_RT 26e6
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010036#define E1XX_CLK_RT 52e6
Thomas Tsoufe269fe2013-10-14 23:56:51 -040037#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040038#define USRP2_BASE_RT 390625
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030039#define USRP_TX_AMPL 0.3
40#define UMTRX_TX_AMPL 0.7
Thomas Tsoue3e88142013-04-05 20:42:41 -040041#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040042
Tom Tsoueb54bdd2014-11-25 16:06:32 -080043/*
44 * UHD timeout value on streaming (re)start
45 *
46 * Allow some time for streaming to commence after the start command is issued,
47 * but consider a wait beyond one second to be a definite error condition.
48 */
49#define UHD_RESTART_TIMEOUT 1.0
50
Alexander Chemeris4d029d82014-04-19 17:25:00 +040051/*
52 * UmTRX specific settings
53 */
54#define UMTRX_VGA1_DEF -18
55
Thomas Tsou02d88d12013-04-05 15:36:30 -040056enum uhd_dev_type {
57 USRP1,
58 USRP2,
59 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050060 B200,
61 B210,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010062 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080063 E3XX,
64 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040065 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040066 NUM_USRP_TYPES,
67};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000068
Thomas Tsoue3e88142013-04-05 20:42:41 -040069struct uhd_dev_offset {
70 enum uhd_dev_type type;
Tom Tsou3f4a13f2016-05-03 19:02:24 -070071 size_t tx_sps;
72 size_t rx_sps;
Thomas Tsoue3e88142013-04-05 20:42:41 -040073 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050074 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040075};
76
kurtis.heimerl965e7572011-11-26 03:16:54 +000077/*
Tom Tsouc3129052015-08-21 18:28:52 -070078 * USRP version dependent device timings
79 */
80#ifdef USE_UHD_3_9
81#define B2XX_TIMING_1SPS 1.7153e-4
82#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070083#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsouc3129052015-08-21 18:28:52 -070084#else
85#define B2XX_TIMING_1SPS 9.9692e-5
86#define B2XX_TIMING_4SPS 6.9248e-5
Tom Tsoud2b07032016-04-26 19:28:59 -070087#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsouc3129052015-08-21 18:28:52 -070088#endif
89
90/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040091 * Tx / Rx sample offset values. In a perfect world, there is no group delay
92 * though analog components, and behaviour through digital filters exactly
93 * matches calculated values. In reality, there are unaccounted factors,
94 * which are captured in these empirically measured (using a loopback test)
95 * timing correction values.
96 *
97 * Notes:
98 * USRP1 with timestamps is not supported by UHD.
99 */
Alexander Chemeris871b8782016-03-20 20:08:57 +0300100static struct uhd_dev_offset uhd_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800101 { USRP1, 1, 1, 0.0, "USRP1 not supported" },
102 { USRP1, 4, 1, 0.0, "USRP1 not supported"},
103 { USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
104 { USRP2, 4, 1, 8.0230e-5, "N2XX 4 SPS" },
105 { B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
106 { B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
107 { B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
108 { B200, 4, 1, B2XX_TIMING_4SPS, "B200 4 SPS" },
109 { B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
110 { B210, 4, 1, B2XX_TIMING_4SPS, "B210 4 SPS" },
111 { E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
112 { E1XX, 4, 1, 6.5571e-5, "E1XX 4 SPS" },
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700113 { E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
114 { E3XX, 4, 1, 1.29231e-4, "E3XX 4 SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800115 { X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
116 { X3XX, 4, 1, 1.1264e-4, "X3XX 4 SPS"},
117 { UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
118 { UMTRX, 4, 1, 7.3846e-5, "UmTRX 4 SPS" },
Alexander Chemeris871b8782016-03-20 20:08:57 +0300119 { B200, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
Tom Tsou9bd649e2016-03-23 17:20:08 -0700120 { B210, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
Alexander Chemeris871b8782016-03-20 20:08:57 +0300121 { UMTRX, 4, 4, 5.1503e-5, "UmTRX EDGE mode (4 SPS TX/RX)" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800122};
Alexander Chemeris871b8782016-03-20 20:08:57 +0300123#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000124
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500125/*
126 * Offset handling for special cases. Currently used for UmTRX dual channel
127 * diversity receiver only.
128 */
129static struct uhd_dev_offset special_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800130 { UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
131 { UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500132};
133
Thomas Tsoucb69f082013-04-08 14:18:26 -0400134/*
135 * Select sample rate based on device type and requested samples-per-symbol.
136 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
137 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800138 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500139static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400140{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500141 if (diversity && (type == UMTRX)) {
142 return GSMRATE * 4;
143 } else if (diversity) {
144 LOG(ALERT) << "Diversity supported on UmTRX only";
145 return -9999.99;
146 }
147
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800148 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400149 return -9999.99;
150
151 switch (type) {
152 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800153 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400154 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400155 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400156 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500157 case B200:
158 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100159 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800160 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400161 case UMTRX:
162 return GSMRATE * sps;
163 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400164 break;
165 }
166
167 LOG(ALERT) << "Unknown device type " << type;
168 return -9999.99;
169}
170
kurtis.heimerl965e7572011-11-26 03:16:54 +0000171/*
172 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
173 or UHD style timestamps. Time conversions are handled
174 internally or accessable through the static convert calls.
175*/
176class smpl_buf {
177public:
178 /** Sample buffer constructor
179 @param len number of 32-bit samples the buffer should hold
180 @param rate sample clockrate
181 @param timestamp
182 */
183 smpl_buf(size_t len, double rate);
184 ~smpl_buf();
185
186 /** Query number of samples available for reading
187 @param timestamp time of first sample
188 @return number of available samples or error
189 */
190 ssize_t avail_smpls(TIMESTAMP timestamp) const;
191 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
192
193 /** Read and write
194 @param buf pointer to buffer
195 @param len number of samples desired to read or write
196 @param timestamp time of first stample
197 @return number of actual samples read or written or error
198 */
199 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
200 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
201 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
202 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
203
204 /** Buffer status string
205 @return a formatted string describing internal buffer state
206 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800207 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000208
209 /** Formatted error string
210 @param code an error code
211 @return a formatted error string
212 */
213 static std::string str_code(ssize_t code);
214
215 enum err_code {
216 ERROR_TIMESTAMP = -1,
217 ERROR_READ = -2,
218 ERROR_WRITE = -3,
219 ERROR_OVERFLOW = -4
220 };
221
222private:
223 uint32_t *data;
224 size_t buf_len;
225
226 double clk_rt;
227
228 TIMESTAMP time_start;
229 TIMESTAMP time_end;
230
231 size_t data_start;
232 size_t data_end;
233};
234
235/*
236 uhd_device - UHD implementation of the Device interface. Timestamped samples
237 are sent to and received from the device. An intermediate buffer
238 on the receive side collects and aligns packets of samples.
239 Events and errors such as underruns are reported asynchronously
240 by the device and received in a separate thread.
241*/
242class uhd_device : public RadioDevice {
243public:
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800244 uhd_device(size_t tx_sps, size_t rx_sps,
245 size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000246 ~uhd_device();
247
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400248 int open(const std::string &args, bool extref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000249 bool start();
250 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800251 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500252 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400253 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000254
Thomas Tsou204a9f12013-10-29 18:34:16 -0400255 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000256 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
257
Thomas Tsou204a9f12013-10-29 18:34:16 -0400258 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000259 TIMESTAMP timestamp, bool isControl);
260
261 bool updateAlignment(TIMESTAMP timestamp);
262
Thomas Tsou204a9f12013-10-29 18:34:16 -0400263 bool setTxFreq(double wFreq, size_t chan);
264 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000265
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800266 inline TIMESTAMP initialWriteTimestamp();
267 inline TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000268
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300269 double fullScaleInputValue();
270 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000271
Thomas Tsou204a9f12013-10-29 18:34:16 -0400272 double setRxGain(double db, size_t chan);
273 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000274 double maxRxGain(void) { return rx_gain_max; }
275 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000276
Thomas Tsou204a9f12013-10-29 18:34:16 -0400277 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000278 double maxTxGain(void) { return tx_gain_max; }
279 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280
Thomas Tsou204a9f12013-10-29 18:34:16 -0400281 double getTxFreq(size_t chan);
282 double getRxFreq(size_t chan);
283 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000284
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400285 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000286 inline double numberRead() { return rx_pkt_cnt; }
287 inline double numberWritten() { return 0; }
288
289 /** Receive and process asynchronous message
290 @return true if message received or false on timeout or error
291 */
292 bool recv_async_msg();
293
kurtis.heimerld4be0742011-11-26 03:17:46 +0000294 enum err_code {
295 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800296 ERROR_TIMEOUT = -2,
297 ERROR_UNRECOVERABLE = -3,
298 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000299 };
300
kurtis.heimerl965e7572011-11-26 03:16:54 +0000301private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000302 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400303 uhd::tx_streamer::sptr tx_stream;
304 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400305 enum TxWindowType tx_window;
306 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000307
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800308 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400309 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000310
Thomas Tsou204a9f12013-10-29 18:34:16 -0400311 double tx_gain_min, tx_gain_max;
312 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500313 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000314
Thomas Tsou204a9f12013-10-29 18:34:16 -0400315 std::vector<double> tx_gains, rx_gains;
316 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000317 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000318
319 bool started;
320 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000321
322 size_t rx_pkt_cnt;
323 size_t drop_cnt;
324 uhd::time_spec_t prev_ts;
325
Thomas Tsou18d3b832014-02-13 14:55:23 -0500326 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400327 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000328
kurtis.heimerl02d04052011-11-26 03:17:10 +0000329 void init_gains();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700330 double get_dev_offset(bool edge, bool diversity);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400331 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400332 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000333 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000334 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000335 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000336
kurtis.heimerl965e7572011-11-26 03:16:54 +0000337 std::string str_code(uhd::rx_metadata_t metadata);
338 std::string str_code(uhd::async_metadata_t metadata);
339
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500340 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
341 bool set_freq(double freq, size_t chan, bool tx);
342
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800343 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500344 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800345 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000346};
347
348void *async_event_loop(uhd_device *dev)
349{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500350 dev->setPriority(0.43);
351
kurtis.heimerl965e7572011-11-26 03:16:54 +0000352 while (1) {
353 dev->recv_async_msg();
354 pthread_testcancel();
355 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500356
357 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000358}
359
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000360/*
361 Catch and drop underrun 'U' and overrun 'O' messages from stdout
362 since we already report using the logging facility. Direct
363 everything else appropriately.
364 */
365void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
366{
367 switch (type) {
368 case uhd::msg::status:
369 LOG(INFO) << msg;
370 break;
371 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000372 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000373 break;
374 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000375 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000376 break;
377 case uhd::msg::fastpath:
378 break;
379 }
380}
381
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800382static void thread_enable_cancel(bool cancel)
383{
384 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
385 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
386}
387
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800388uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
389 size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400390 : tx_gain_min(0.0), tx_gain_max(0.0),
391 rx_gain_min(0.0), rx_gain_max(0.0),
392 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000393 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500394 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000395{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800396 this->tx_sps = tx_sps;
397 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400398 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500399 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500400 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000401}
402
403uhd_device::~uhd_device()
404{
405 stop();
406
Thomas Tsou204a9f12013-10-29 18:34:16 -0400407 for (size_t i = 0; i < rx_buffers.size(); i++)
408 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000409}
410
kurtis.heimerl24481de2011-11-26 03:17:18 +0000411void uhd_device::init_gains()
412{
413 uhd::gain_range_t range;
414
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400415 if (dev_type == UMTRX) {
416 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
417 if (gain_stages[0] == "VGA") {
418 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
419 }
420 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
421 range = usrp_dev->get_tx_gain_range();
422 tx_gain_min = range.start();
423 tx_gain_max = range.stop();
424 } else {
425 range = usrp_dev->get_tx_gain_range("VGA2");
426 tx_gain_min = UMTRX_VGA1_DEF + range.start();
427 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
428 }
429 } else {
430 range = usrp_dev->get_tx_gain_range();
431 tx_gain_min = range.start();
432 tx_gain_max = range.stop();
433 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400434 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000435
436 range = usrp_dev->get_rx_gain_range();
437 rx_gain_min = range.start();
438 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400439 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000440
Thomas Tsou204a9f12013-10-29 18:34:16 -0400441 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400442 double gain = (tx_gain_min + tx_gain_max) / 2;
443 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
444 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400445 tx_gains[i] = usrp_dev->get_tx_gain(i);
446 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000447
Thomas Tsou204a9f12013-10-29 18:34:16 -0400448 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400449 double gain = (rx_gain_min + rx_gain_max) / 2;
450 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
451 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400452 rx_gains[i] = usrp_dev->get_rx_gain(i);
453 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000454
455 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400456
kurtis.heimerl24481de2011-11-26 03:17:18 +0000457}
458
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700459double uhd_device::get_dev_offset(bool edge, bool diversity)
460{
461 struct uhd_dev_offset *offset = NULL;
462
463 /* Reject USRP1 */
464 if (dev_type == USRP1) {
465 LOG(ERR) << "Invalid device type";
466 return 0.0;
467 }
468
469 if (edge && diversity) {
470 LOG(ERR) << "Unsupported configuration";
471 return 0.0;
472 }
473
474 if (edge && (dev_type != B200) &&
475 (dev_type != B210) && (dev_type != UMTRX)) {
476 LOG(ALERT) << "EDGE is supported on B200/B210 and UmTRX only";
477 return 0.0;
478 }
479
480 /* Special cases (e.g. diversity receiver) */
481 if (diversity) {
482 if (dev_type != UMTRX) {
483 LOG(ALERT) << "Diversity on UmTRX only";
484 return 0.0;
485 }
486
487 switch (tx_sps) {
488 case 1:
489 offset = &special_offsets[0];
490 break;
491 case 4:
492 default:
493 offset = &special_offsets[1];
494 }
495 } else {
496 /* Search for matching offset value */
497 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
498 if ((dev_type == uhd_offsets[i].type) &&
499 (tx_sps == uhd_offsets[i].tx_sps) &&
500 (rx_sps == uhd_offsets[i].rx_sps)) {
501 offset = &uhd_offsets[i];
502 break;
503 }
504 }
505 }
506
507 if (!offset) {
508 LOG(ERR) << "Invalid device configuration";
509 return 0.0;
510 }
511
512 std::cout << "-- Setting " << offset->desc << std::endl;
513
514 return offset->offset;
515}
516
Thomas Tsou02d88d12013-04-05 15:36:30 -0400517int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000518{
Thomas Tsou092f7572013-04-04 17:04:39 -0400519 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000520
Thomas Tsou7e068062013-04-08 19:39:37 -0400521 try {
522 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400523 } catch (const std::exception &ex) {
524 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
525 LOG(ALERT) << ex.what();
526 return -1;
527 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000528
Thomas Tsou092f7572013-04-04 17:04:39 -0400529 actual = usrp_dev->get_master_clock_rate();
530 offset = fabs(clk_rate - actual);
531
532 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000533 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400534 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400535 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400536 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000537 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400538
539 return 0;
540}
541
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400542int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400543{
Thomas Tsou092f7572013-04-04 17:04:39 -0400544 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400545 double tx_offset, rx_offset;
546
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100547 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700548 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400549 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400550 return -1;
551 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100552 else if (dev_type == E1XX) {
553 if (set_master_clk(E1XX_CLK_RT) < 0)
554 return -1;
555 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000556
557 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400558 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400559 usrp_dev->set_tx_rate(tx_rate);
560 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400561 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400562 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400563 LOG(ALERT) << ex.what();
564 return -1;
565 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400566 this->tx_rate = usrp_dev->get_tx_rate();
567 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000568
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400569 tx_offset = fabs(this->tx_rate - tx_rate);
570 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400571 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000572 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400573 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
574 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400575 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000576 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000577
Thomas Tsou02d88d12013-04-05 15:36:30 -0400578 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000579}
580
Thomas Tsou204a9f12013-10-29 18:34:16 -0400581double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000582{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400583 if (chan >= tx_gains.size()) {
584 LOG(ALERT) << "Requested non-existent channel" << chan;
585 return 0.0f;
586 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000587
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400588 if (dev_type == UMTRX) {
589 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
590 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
591 usrp_dev->set_tx_gain(db, chan);
592 } else {
593 // New UHD versions support split configuration of
594 // Tx gain stages. We utilize this to set the gain
595 // configuration, optimal for the Tx signal quality.
596 // From our measurements, VGA1 must be 18dB plus-minus
597 // one and VGA2 is the best when 23dB or lower.
598 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
599 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
600 }
601 } else {
602 usrp_dev->set_tx_gain(db, chan);
603 }
604
Thomas Tsou204a9f12013-10-29 18:34:16 -0400605 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000606
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400607 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400608
609 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000610}
611
Thomas Tsou204a9f12013-10-29 18:34:16 -0400612double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000613{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400614 if (chan >= rx_gains.size()) {
615 LOG(ALERT) << "Requested non-existent channel " << chan;
616 return 0.0f;
617 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000618
Thomas Tsou204a9f12013-10-29 18:34:16 -0400619 usrp_dev->set_rx_gain(db, chan);
620 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000621
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400622 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400623
624 return rx_gains[chan];
625}
626
627double uhd_device::getRxGain(size_t chan)
628{
629 if (chan >= rx_gains.size()) {
630 LOG(ALERT) << "Requested non-existent channel " << chan;
631 return 0.0f;
632 }
633
634 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000635}
636
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000637/*
638 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400639 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000640 deal with the transport latency. Reject the USRP1 because UHD doesn't
641 support timestamped samples with it.
642 */
643bool uhd_device::parse_dev_type()
644{
645 std::string mboard_str, dev_str;
646 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800647 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
Tom Tsou283b22d2015-10-21 17:10:23 -0700648 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000649
650 prop_tree = usrp_dev->get_device()->get_tree();
651 dev_str = prop_tree->access<std::string>("/name").get();
652 mboard_str = usrp_dev->get_mboard_name();
653
654 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400655 usrp2_str = dev_str.find("USRP2");
656 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400657 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400658 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100659 e100_str = mboard_str.find("E100");
660 e110_str = mboard_str.find("E110");
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700661 e310_str = mboard_str.find("E3XX");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800662 x300_str = mboard_str.find("X300");
663 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400664 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000665
666 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000667 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
668 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400669 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000670 return false;
671 }
672
Thomas Tsoucb69f082013-04-08 14:18:26 -0400673 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400674 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400675 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400676 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500677 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500678 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400679 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500680 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500681 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100682 } else if (e100_str != std::string::npos) {
683 tx_window = TX_WINDOW_FIXED;
684 dev_type = E1XX;
685 } else if (e110_str != std::string::npos) {
686 tx_window = TX_WINDOW_FIXED;
687 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400688 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500689 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400690 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800691 } else if (e310_str != std::string::npos) {
692 tx_window = TX_WINDOW_FIXED;
693 dev_type = E3XX;
694 } else if (x300_str != std::string::npos) {
695 tx_window = TX_WINDOW_FIXED;
696 dev_type = X3XX;
697 } else if (x310_str != std::string::npos) {
698 tx_window = TX_WINDOW_FIXED;
699 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400700 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500701 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400702 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400703 } else {
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700704 LOG(ALERT) << "Unknown UHD device type "
705 << dev_str << " " << mboard_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400706 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000707 }
708
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500709 if (tx_window == TX_WINDOW_USRP1) {
710 LOG(INFO) << "Using USRP1 type transmit window for "
711 << dev_str << " " << mboard_str;
712 } else {
713 LOG(INFO) << "Using fixed transmit window for "
714 << dev_str << " " << mboard_str;
715 }
716
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000717 return true;
718}
719
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400720int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000721{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000722 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000723 uhd::device_addr_t addr(args);
724 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000725 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000726 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400727 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000728 }
729
730 // Use the first found device
731 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000732 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700733 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000734 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700735 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400736 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000737 }
738
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000739 // Check for a valid device type and set bus type
740 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400741 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000742
Thomas Tsou204a9f12013-10-29 18:34:16 -0400743 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500744 if ((dev_type == B210) && (chans == 2)) {
745 } else if ((dev_type == UMTRX) && (chans == 2)) {
Alexander Chemeris50747dc2015-06-07 01:07:45 -0400746 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400747 usrp_dev->set_tx_subdev_spec(subdev_spec);
748 usrp_dev->set_rx_subdev_spec(subdev_spec);
749 } else if (chans != 1) {
750 LOG(ALERT) << "Invalid channel combination for device";
751 return -1;
752 }
753
754 tx_freqs.resize(chans);
755 rx_freqs.resize(chans);
756 tx_gains.resize(chans);
757 rx_gains.resize(chans);
758 rx_buffers.resize(chans);
759
Thomas Tsou010fff72013-10-16 00:31:18 -0400760 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400761 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400762
kurtis.heimerl965e7572011-11-26 03:16:54 +0000763 // Set rates
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800764 double _tx_rate = select_rate(dev_type, tx_sps);
765 double _rx_rate = select_rate(dev_type, rx_sps, diversity);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500766
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500767 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
768 return -1;
769 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400770 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000771
Alexander Chemerise1714252015-04-28 23:09:02 -0400772 // Set RF frontend bandwidth
773 if (dev_type == UMTRX) {
774 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
775 for (size_t i = 0; i < chans; i++) {
776 usrp_dev->set_tx_bandwidth(500*1000*2, i);
777 if (!diversity)
778 usrp_dev->set_rx_bandwidth(500*1000*2, i);
779 }
780 }
781
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400782 /* Create TX and RX streamers */
783 uhd::stream_args_t stream_args("sc16");
784 for (size_t i = 0; i < chans; i++)
785 stream_args.channels.push_back(i);
786
787 tx_stream = usrp_dev->get_tx_stream(stream_args);
788 rx_stream = usrp_dev->get_rx_stream(stream_args);
789
790 /* Number of samples per over-the-wire packet */
791 tx_spp = tx_stream->get_max_num_samps();
792 rx_spp = rx_stream->get_max_num_samps();
793
kurtis.heimerl965e7572011-11-26 03:16:54 +0000794 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400795 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400796 for (size_t i = 0; i < rx_buffers.size(); i++)
797 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000798
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800799 // Set receive chain sample offset. Trigger the EDGE offset
800 // table by checking for 4 SPS on the receive path. No other
801 // configuration supports using 4 SPS.
802 bool edge = false;
803 if (rx_sps == 4)
804 edge = true;
805
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700806 double offset = get_dev_offset(edge, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400807 if (offset == 0.0) {
808 LOG(ERR) << "Unsupported configuration, no correction applied";
809 ts_offset = 0;
810 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400811 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400812 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000813
kurtis.heimerl02d04052011-11-26 03:17:10 +0000814 // Initialize and shadow gain values
815 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000816
kurtis.heimerl965e7572011-11-26 03:16:54 +0000817 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000818 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000819
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500820 if (diversity)
821 return DIVERSITY;
822
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400823 switch (dev_type) {
824 case B100:
825 return RESAMP_64M;
826 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800827 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400828 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500829 case B200:
830 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100831 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800832 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500833 default:
834 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400835 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400836
837 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000838}
839
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000840bool uhd_device::flush_recv(size_t num_pkts)
841{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000842 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000843 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800844 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000845
Thomas Tsou18d3b832014-02-13 14:55:23 -0500846 std::vector<std::vector<short> >
847 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000848
Thomas Tsou18d3b832014-02-13 14:55:23 -0500849 std::vector<short *> pkt_ptrs;
850 for (size_t i = 0; i < pkt_bufs.size(); i++)
851 pkt_ptrs.push_back(&pkt_bufs[i].front());
852
853 ts_initial = 0;
854 while (!ts_initial || (num_pkts-- > 0)) {
855 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400856 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000857 if (!num_smpls) {
858 switch (md.error_code) {
859 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800860 LOG(ALERT) << "Device timed out";
861 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000862 default:
863 continue;
864 }
865 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500866
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700867 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000868 }
869
Thomas Tsou18d3b832014-02-13 14:55:23 -0500870 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
871
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000872 return true;
873}
874
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800875bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000876{
Thomas Tsouc2839162014-03-27 13:52:58 -0400877 /* Allow 100 ms delay to align multi-channel streams */
878 double delay = 0.1;
879
kurtis.heimerl68292102011-11-26 03:17:28 +0000880 aligned = false;
881
Thomas Tsouc2839162014-03-27 13:52:58 -0400882 uhd::time_spec_t current = usrp_dev->get_time_now();
883
Thomas Tsou204a9f12013-10-29 18:34:16 -0400884 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400885 cmd.stream_now = false;
886 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400887
kurtis.heimerl68292102011-11-26 03:17:28 +0000888 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500889
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800890 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000891}
892
kurtis.heimerl965e7572011-11-26 03:16:54 +0000893bool uhd_device::start()
894{
895 LOG(INFO) << "Starting USRP...";
896
897 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000898 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000899 return false;
900 }
901
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400902 // Register msg handler
903 uhd::msg::register_handler(&uhd_msg_handler);
904
kurtis.heimerl965e7572011-11-26 03:16:54 +0000905 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800906 async_event_thrd = new Thread();
907 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000908
909 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800910 if (!restart())
911 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000912
kurtis.heimerl965e7572011-11-26 03:16:54 +0000913 // Display usrp time
914 double time_now = usrp_dev->get_time_now().get_real_secs();
915 LOG(INFO) << "The current time is " << time_now << " seconds";
916
917 started = true;
918 return true;
919}
920
921bool uhd_device::stop()
922{
Thomas Tsou2c791102013-11-15 23:00:28 -0500923 if (!started)
924 return false;
925
926 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
928
929 usrp_dev->issue_stream_cmd(stream_cmd);
930
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800931 async_event_thrd->cancel();
932 async_event_thrd->join();
933 delete async_event_thrd;
934
kurtis.heimerl965e7572011-11-26 03:16:54 +0000935 started = false;
936 return true;
937}
938
Thomas Tsou7553aa92013-11-08 12:50:03 -0500939void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000940{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500941 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000942 return;
943}
944
kurtis.heimerld4be0742011-11-26 03:17:46 +0000945int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000946{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000947 uhd::time_spec_t ts;
948
kurtis.heimerld4be0742011-11-26 03:17:46 +0000949 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000950 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000951
952 switch (md.error_code) {
953 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000954 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800955 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000956 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
957 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
958 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
959 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
960 default:
961 return ERROR_UNHANDLED;
962 }
963 }
964
kurtis.heimerl965e7572011-11-26 03:16:54 +0000965 // Missing timestamp
966 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000967 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000968 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969 }
970
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000971 ts = md.time_spec;
972
kurtis.heimerl965e7572011-11-26 03:16:54 +0000973 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000974 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000975 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000976 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
977 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000978 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000979 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000980 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000981 }
982
983 return 0;
984}
985
Thomas Tsou204a9f12013-10-29 18:34:16 -0400986int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
987 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988{
989 ssize_t rc;
990 uhd::time_spec_t ts;
991 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000992
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993 if (bufs.size() != chans) {
994 LOG(ALERT) << "Invalid channel combination " << bufs.size();
995 return -1;
996 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000998 *overrun = false;
999 *underrun = false;
1000
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001 // Shift read time with respect to transmit clock
1002 timestamp += ts_offset;
1003
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001004 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001005 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001006
1007 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001008 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001009 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001010 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001011 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001012 return 0;
1013 }
1014
Thomas Tsou204a9f12013-10-29 18:34:16 -04001015 // Create vector buffer
1016 std::vector<std::vector<short> >
1017 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1018
1019 std::vector<short *> pkt_ptrs;
1020 for (size_t i = 0; i < pkt_bufs.size(); i++)
1021 pkt_ptrs.push_back(&pkt_bufs[i].front());
1022
kurtis.heimerl965e7572011-11-26 03:16:54 +00001023 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001024 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001025 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001026 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1027 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001028 thread_enable_cancel(true);
1029
kurtis.heimerl965e7572011-11-26 03:16:54 +00001030 rx_pkt_cnt++;
1031
kurtis.heimerld4be0742011-11-26 03:17:46 +00001032 // Check for errors
1033 rc = check_rx_md_err(metadata, num_smpls);
1034 switch (rc) {
1035 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001036 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1037 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001038 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001039 case ERROR_TIMEOUT:
1040 // Assume stopping condition
1041 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001042 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001043 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001044 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001045 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001046 }
1047
kurtis.heimerl965e7572011-11-26 03:16:54 +00001048 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001049 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001050
Thomas Tsou204a9f12013-10-29 18:34:16 -04001051 for (size_t i = 0; i < rx_buffers.size(); i++) {
1052 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1053 num_smpls,
1054 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055
Thomas Tsou204a9f12013-10-29 18:34:16 -04001056 // Continue on local overrun, exit on other errors
1057 if ((rc < 0)) {
1058 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001059 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001060 if (rc != smpl_buf::ERROR_OVERFLOW)
1061 return 0;
1062 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001063 }
1064 }
1065
1066 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067 for (size_t i = 0; i < rx_buffers.size(); i++) {
1068 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1069 if ((rc < 0) || (rc != len)) {
1070 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001071 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001072 return 0;
1073 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001074 }
1075
1076 return len;
1077}
1078
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1080 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001081{
1082 uhd::tx_metadata_t metadata;
1083 metadata.has_time_spec = true;
1084 metadata.start_of_burst = false;
1085 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001086 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001087
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001088 *underrun = false;
1089
kurtis.heimerl965e7572011-11-26 03:16:54 +00001090 // No control packets
1091 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001092 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001093 return 0;
1094 }
1095
Thomas Tsou204a9f12013-10-29 18:34:16 -04001096 if (bufs.size() != chans) {
1097 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1098 return -1;
1099 }
1100
kurtis.heimerl965e7572011-11-26 03:16:54 +00001101 // Drop a fixed number of packets (magic value)
1102 if (!aligned) {
1103 drop_cnt++;
1104
1105 if (drop_cnt == 1) {
1106 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001107 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001108 metadata.end_of_burst = true;
1109 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001110 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001111 return len;
1112 } else {
1113 LOG(DEBUG) << "Aligning transmitter: start burst";
1114 metadata.start_of_burst = true;
1115 aligned = true;
1116 drop_cnt = 0;
1117 }
1118 }
1119
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001120 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001121 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001122 thread_enable_cancel(true);
1123
ttsoub371ed52012-01-09 18:11:34 +00001124 if (num_smpls != (unsigned) len) {
1125 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001126 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001127
1128 return num_smpls;
1129}
1130
1131bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1132{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001133 return true;
1134}
1135
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001136uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1137{
1138 double rf_spread, rf_freq;
1139 std::vector<double> freqs;
1140 uhd::tune_request_t treq(freq);
1141
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001142 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001143 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001144 return uhd::tune_request_t(freq, offset);
1145
1146 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1147 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1148 // only distort the signal (because cordic is not ideal).
1149 treq.target_freq = freq;
1150 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1151 treq.rf_freq = freq;
1152 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1153 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001154 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001155 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001156 if (offset == 0.0)
1157 return treq;
1158
1159 return uhd::tune_request_t(freq, offset);
1160 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001161 LOG(ALERT) << chans << " channels unsupported";
1162 return treq;
1163 }
1164
1165 if (tx)
1166 freqs = tx_freqs;
1167 else
1168 freqs = rx_freqs;
1169
1170 /* Tune directly if other channel isn't tuned */
1171 if (freqs[!chan] < 10.0)
1172 return treq;
1173
1174 /* Find center frequency between channels */
1175 rf_spread = fabs(freqs[!chan] - freq);
1176 if (rf_spread > B2XX_CLK_RT) {
1177 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1178 return treq;
1179 }
1180
1181 rf_freq = (freqs[!chan] + freq) / 2.0f;
1182
1183 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1184 treq.target_freq = freq;
1185 treq.rf_freq = rf_freq;
1186
1187 return treq;
1188}
1189
1190bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1191{
1192 std::vector<double> freqs;
1193 uhd::tune_result_t tres;
1194 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1195
1196 if (tx) {
1197 tres = usrp_dev->set_tx_freq(treq, chan);
1198 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1199 } else {
1200 tres = usrp_dev->set_rx_freq(treq, chan);
1201 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1202 }
1203 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1204
Thomas Tsou8e17df72014-03-06 14:16:11 -05001205 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1206 return true;
1207
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001208 /* Manual RF policy means we intentionally tuned with a baseband
1209 * offset for dual-channel purposes. Now retune the other channel
1210 * with the opposite corresponding frequency offset
1211 */
1212 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1213 if (tx) {
1214 treq = select_freq(tx_freqs[!chan], !chan, true);
1215 tres = usrp_dev->set_tx_freq(treq, !chan);
1216 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1217 } else {
1218 treq = select_freq(rx_freqs[!chan], !chan, false);
1219 tres = usrp_dev->set_rx_freq(treq, !chan);
1220 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1221
1222 }
1223 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1224 }
1225
1226 return true;
1227}
1228
Thomas Tsou204a9f12013-10-29 18:34:16 -04001229bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001230{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001231 if (chan >= tx_freqs.size()) {
1232 LOG(ALERT) << "Requested non-existent channel " << chan;
1233 return false;
1234 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001235 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001236
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001237 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001238}
1239
Thomas Tsou204a9f12013-10-29 18:34:16 -04001240bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001241{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001242 if (chan >= rx_freqs.size()) {
1243 LOG(ALERT) << "Requested non-existent channel " << chan;
1244 return false;
1245 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001246 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001247
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001248 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001249}
1250
Thomas Tsou204a9f12013-10-29 18:34:16 -04001251double uhd_device::getTxFreq(size_t chan)
1252{
1253 if (chan >= tx_freqs.size()) {
1254 LOG(ALERT) << "Requested non-existent channel " << chan;
1255 return 0.0;
1256 }
1257
1258 return tx_freqs[chan];
1259}
1260
1261double uhd_device::getRxFreq(size_t chan)
1262{
1263 if (chan >= rx_freqs.size()) {
1264 LOG(ALERT) << "Requested non-existent channel " << chan;
1265 return 0.0;
1266 }
1267
1268 return rx_freqs[chan];
1269}
1270
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001271/*
1272 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1273 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1274 * combination.
1275 */
1276TIMESTAMP uhd_device::initialWriteTimestamp()
1277{
1278 if (rx_sps == tx_sps)
1279 return ts_initial;
1280 else
1281 return ts_initial * tx_sps;
1282}
1283
1284TIMESTAMP uhd_device::initialReadTimestamp()
1285{
1286 return ts_initial;
1287}
1288
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001289double uhd_device::fullScaleInputValue()
1290{
1291 if (dev_type == UMTRX)
1292 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1293 else
1294 return (double) SHRT_MAX * USRP_TX_AMPL;
1295}
1296
1297double uhd_device::fullScaleOutputValue()
1298{
1299 return (double) SHRT_MAX;
1300}
1301
kurtis.heimerl965e7572011-11-26 03:16:54 +00001302bool uhd_device::recv_async_msg()
1303{
ttsou60dc4c92012-08-08 23:30:23 +00001304 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001305
1306 thread_enable_cancel(false);
1307 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1308 thread_enable_cancel(true);
1309 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001310 return false;
1311
1312 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001313 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001314 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001315
1316 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1317 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1318 LOG(ERR) << str_code(md);
1319 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001320 }
1321
1322 return true;
1323}
1324
1325std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1326{
1327 std::ostringstream ost("UHD: ");
1328
1329 switch (metadata.error_code) {
1330 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1331 ost << "No error";
1332 break;
1333 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1334 ost << "No packet received, implementation timed-out";
1335 break;
1336 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1337 ost << "A stream command was issued in the past";
1338 break;
1339 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1340 ost << "Expected another stream command";
1341 break;
1342 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1343 ost << "An internal receive buffer has filled";
1344 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001345 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1346 ost << "Multi-channel alignment failed";
1347 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001348 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1349 ost << "The packet could not be parsed";
1350 break;
1351 default:
1352 ost << "Unknown error " << metadata.error_code;
1353 }
1354
1355 if (metadata.has_time_spec)
1356 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1357
1358 return ost.str();
1359}
1360
1361std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1362{
1363 std::ostringstream ost("UHD: ");
1364
1365 switch (metadata.event_code) {
1366 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1367 ost << "A packet was successfully transmitted";
1368 break;
1369 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1370 ost << "An internal send buffer has emptied";
1371 break;
1372 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1373 ost << "Packet loss between host and device";
1374 break;
1375 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1376 ost << "Packet time was too late or too early";
1377 break;
1378 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1379 ost << "Underflow occurred inside a packet";
1380 break;
1381 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1382 ost << "Packet loss within a burst";
1383 break;
1384 default:
1385 ost << "Unknown error " << metadata.event_code;
1386 }
1387
1388 if (metadata.has_time_spec)
1389 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1390
1391 return ost.str();
1392}
1393
1394smpl_buf::smpl_buf(size_t len, double rate)
1395 : buf_len(len), clk_rt(rate),
1396 time_start(0), time_end(0), data_start(0), data_end(0)
1397{
1398 data = new uint32_t[len];
1399}
1400
1401smpl_buf::~smpl_buf()
1402{
1403 delete[] data;
1404}
1405
1406ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1407{
1408 if (timestamp < time_start)
1409 return ERROR_TIMESTAMP;
1410 else if (timestamp >= time_end)
1411 return 0;
1412 else
1413 return time_end - timestamp;
1414}
1415
1416ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1417{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001418 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001419}
1420
1421ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1422{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001423 int type_sz = 2 * sizeof(short);
1424
kurtis.heimerl965e7572011-11-26 03:16:54 +00001425 // Check for valid read
1426 if (timestamp < time_start)
1427 return ERROR_TIMESTAMP;
1428 if (timestamp >= time_end)
1429 return 0;
1430 if (len >= buf_len)
1431 return ERROR_READ;
1432
1433 // How many samples should be copied
1434 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001435 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001436 num_smpls = len;
1437
1438 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001439 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001440
1441 // Read it
1442 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001443 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001444 memcpy(buf, data + read_start, numBytes);
1445 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001446 size_t first_cp = (buf_len - read_start) * type_sz;
1447 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001448
1449 memcpy(buf, data + read_start, first_cp);
1450 memcpy((char*) buf + first_cp, data, second_cp);
1451 }
1452
1453 data_start = (read_start + len) % buf_len;
1454 time_start = timestamp + len;
1455
1456 if (time_start > time_end)
1457 return ERROR_READ;
1458 else
1459 return num_smpls;
1460}
1461
1462ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1463{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001464 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001465}
1466
1467ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1468{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001469 int type_sz = 2 * sizeof(short);
1470
kurtis.heimerl965e7572011-11-26 03:16:54 +00001471 // Check for valid write
1472 if ((len == 0) || (len >= buf_len))
1473 return ERROR_WRITE;
1474 if ((timestamp + len) <= time_end)
1475 return ERROR_TIMESTAMP;
1476
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001477 if (timestamp < time_end) {
1478 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001479 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1480 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001481 // Do not return error here, because it's a rounding error and is not fatal
1482 }
1483 if (timestamp > time_end && time_end != 0) {
1484 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001485 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1486 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001487 // Do not return error here, because it's a rounding error and is not fatal
1488 }
1489
kurtis.heimerl965e7572011-11-26 03:16:54 +00001490 // Starting index
1491 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1492
1493 // Write it
1494 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001495 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001496 memcpy(data + write_start, buf, numBytes);
1497 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001498 size_t first_cp = (buf_len - write_start) * type_sz;
1499 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001500
1501 memcpy(data + write_start, buf, first_cp);
1502 memcpy(data, (char*) buf + first_cp, second_cp);
1503 }
1504
1505 data_end = (write_start + len) % buf_len;
1506 time_end = timestamp + len;
1507
Thomas Tsou18d3b832014-02-13 14:55:23 -05001508 if (!data_start)
1509 data_start = write_start;
1510
kurtis.heimerl965e7572011-11-26 03:16:54 +00001511 if (((write_start + len) > buf_len) && (data_end > data_start))
1512 return ERROR_OVERFLOW;
1513 else if (time_end <= time_start)
1514 return ERROR_WRITE;
1515 else
1516 return len;
1517}
1518
1519ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1520{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001521 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001522}
1523
Tom Tsou1ae25562014-12-05 12:56:34 -08001524std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001525{
1526 std::ostringstream ost("Sample buffer: ");
1527
Tom Tsou1ae25562014-12-05 12:56:34 -08001528 ost << "timestamp = " << ts;
1529 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001530 ost << ", time_start = " << time_start;
1531 ost << ", time_end = " << time_end;
1532 ost << ", data_start = " << data_start;
1533 ost << ", data_end = " << data_end;
1534
1535 return ost.str();
1536}
1537
1538std::string smpl_buf::str_code(ssize_t code)
1539{
1540 switch (code) {
1541 case ERROR_TIMESTAMP:
1542 return "Sample buffer: Requested timestamp is not valid";
1543 case ERROR_READ:
1544 return "Sample buffer: Read error";
1545 case ERROR_WRITE:
1546 return "Sample buffer: Write error";
1547 case ERROR_OVERFLOW:
1548 return "Sample buffer: Overrun";
1549 default:
1550 return "Sample buffer: Unknown error";
1551 }
1552}
1553
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001554RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
1555 size_t chans, bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001556{
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001557 return new uhd_device(tx_sps, rx_sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001558}