blob: c9b71e1dc790d4b883ef88dec10dcf53aa267d5f [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
Thomas Tsou02d88d12013-04-05 15:36:30 -040051enum uhd_dev_type {
52 USRP1,
53 USRP2,
54 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050055 B200,
56 B210,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010057 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080058 E3XX,
59 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040060 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040061 NUM_USRP_TYPES,
62};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000063
Thomas Tsoue3e88142013-04-05 20:42:41 -040064struct uhd_dev_offset {
65 enum uhd_dev_type type;
66 int sps;
67 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050068 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040069};
70
kurtis.heimerl965e7572011-11-26 03:16:54 +000071/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040072 * Tx / Rx sample offset values. In a perfect world, there is no group delay
73 * though analog components, and behaviour through digital filters exactly
74 * matches calculated values. In reality, there are unaccounted factors,
75 * which are captured in these empirically measured (using a loopback test)
76 * timing correction values.
77 *
78 * Notes:
79 * USRP1 with timestamps is not supported by UHD.
80 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080081static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050082 { USRP1, 1, 0.0, "USRP1 not supported" },
83 { USRP1, 4, 0.0, "USRP1 not supported"},
84 { USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
85 { USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
86 { B100, 1, 1.2104e-4, "B100 1 SPS" },
87 { B100, 4, 7.9307e-5, "B100 4 SPS" },
Thomas Tsoue7882392014-02-13 14:46:23 -050088 { B200, 1, 9.9692e-5, "B200 1 SPS" },
89 { B200, 4, 6.9248e-5, "B200 4 SPS" },
90 { B210, 1, 9.9692e-5, "B210 1 SPS" },
91 { B210, 4, 6.9248e-5, "B210 4 SPS" },
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010092 { E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
93 { E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
Tom Tsou4ad9ea62014-12-03 18:47:20 -080094 { E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
95 { E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
96 { X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
97 { X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050098 { UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
99 { UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
Thomas Tsoue3e88142013-04-05 20:42:41 -0400100};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000101
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500102/*
103 * Offset handling for special cases. Currently used for UmTRX dual channel
104 * diversity receiver only.
105 */
106static struct uhd_dev_offset special_offsets[] = {
107 { UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
108 { UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
109};
110
111static double get_dev_offset(enum uhd_dev_type type,
112 int sps, bool diversity = false)
Thomas Tsoue3e88142013-04-05 20:42:41 -0400113{
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800114 struct uhd_dev_offset *offset = NULL;
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500115
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500116 /* Reject USRP1 */
Thomas Tsoue3e88142013-04-05 20:42:41 -0400117 if (type == USRP1) {
118 LOG(ERR) << "Invalid device type";
119 return 0.0;
120 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000121
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500122 /* Special cases (e.g. diversity receiver) */
123 if (diversity) {
124 if (type != UMTRX) {
125 LOG(ALERT) << "Diversity on UmTRX only";
126 return 0.0;
127 }
128
129 switch (sps) {
130 case 1:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500131 offset = &special_offsets[0];
132 break;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500133 case 4:
134 default:
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500135 offset = &special_offsets[1];
136 }
137 } else {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800138 /* Search for matching offset value */
139 for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
140 if ((type == uhd_offsets[i].type) &&
141 (sps == uhd_offsets[i].sps)) {
142 offset = &uhd_offsets[i];
143 break;
144 }
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500145 }
146 }
147
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800148 if (!offset) {
149 LOG(ERR) << "Invalid device configuration";
150 return 0.0;
151 }
152
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500153 std::cout << "-- Setting " << offset->desc << std::endl;
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000154
Thomas Tsou2e622ff2013-11-15 18:35:04 -0500155 return offset->offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -0400156}
kurtis.heimerlce317332011-11-26 03:18:39 +0000157
Thomas Tsoucb69f082013-04-08 14:18:26 -0400158/*
159 * Select sample rate based on device type and requested samples-per-symbol.
160 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
161 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800162 */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500163static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400164{
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500165 if (diversity && (type == UMTRX)) {
166 return GSMRATE * 4;
167 } else if (diversity) {
168 LOG(ALERT) << "Diversity supported on UmTRX only";
169 return -9999.99;
170 }
171
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800172 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400173 return -9999.99;
174
175 switch (type) {
176 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800177 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400178 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400179 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400180 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500181 case B200:
182 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100183 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800184 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400185 case UMTRX:
186 return GSMRATE * sps;
187 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400188 break;
189 }
190
191 LOG(ALERT) << "Unknown device type " << type;
192 return -9999.99;
193}
194
kurtis.heimerl965e7572011-11-26 03:16:54 +0000195/** Timestamp conversion
196 @param timestamp a UHD or OpenBTS timestamp
197 @param rate sample rate
198 @return the converted timestamp
199*/
200uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
201{
202 double secs = (double) ticks / rate;
203 return uhd::time_spec_t(secs);
204}
205
206TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
207{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000208 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000209 return ts.get_tick_count(rate) + ticks;
210}
211
212/*
213 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
214 or UHD style timestamps. Time conversions are handled
215 internally or accessable through the static convert calls.
216*/
217class smpl_buf {
218public:
219 /** Sample buffer constructor
220 @param len number of 32-bit samples the buffer should hold
221 @param rate sample clockrate
222 @param timestamp
223 */
224 smpl_buf(size_t len, double rate);
225 ~smpl_buf();
226
227 /** Query number of samples available for reading
228 @param timestamp time of first sample
229 @return number of available samples or error
230 */
231 ssize_t avail_smpls(TIMESTAMP timestamp) const;
232 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
233
234 /** Read and write
235 @param buf pointer to buffer
236 @param len number of samples desired to read or write
237 @param timestamp time of first stample
238 @return number of actual samples read or written or error
239 */
240 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
241 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
242 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
243 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
244
245 /** Buffer status string
246 @return a formatted string describing internal buffer state
247 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800248 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000249
250 /** Formatted error string
251 @param code an error code
252 @return a formatted error string
253 */
254 static std::string str_code(ssize_t code);
255
256 enum err_code {
257 ERROR_TIMESTAMP = -1,
258 ERROR_READ = -2,
259 ERROR_WRITE = -3,
260 ERROR_OVERFLOW = -4
261 };
262
263private:
264 uint32_t *data;
265 size_t buf_len;
266
267 double clk_rt;
268
269 TIMESTAMP time_start;
270 TIMESTAMP time_end;
271
272 size_t data_start;
273 size_t data_end;
274};
275
276/*
277 uhd_device - UHD implementation of the Device interface. Timestamped samples
278 are sent to and received from the device. An intermediate buffer
279 on the receive side collects and aligns packets of samples.
280 Events and errors such as underruns are reported asynchronously
281 by the device and received in a separate thread.
282*/
283class uhd_device : public RadioDevice {
284public:
Thomas Tsou8e17df72014-03-06 14:16:11 -0500285 uhd_device(size_t sps, size_t chans, bool diversity, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000286 ~uhd_device();
287
Thomas Tsou010fff72013-10-16 00:31:18 -0400288 int open(const std::string &args, bool extref);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000289 bool start();
290 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800291 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500292 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400293 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000294
Thomas Tsou204a9f12013-10-29 18:34:16 -0400295 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000296 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
297
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000299 TIMESTAMP timestamp, bool isControl);
300
301 bool updateAlignment(TIMESTAMP timestamp);
302
Thomas Tsou204a9f12013-10-29 18:34:16 -0400303 bool setTxFreq(double wFreq, size_t chan);
304 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000305
Thomas Tsou18d3b832014-02-13 14:55:23 -0500306 inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
307 inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000308
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300309 double fullScaleInputValue();
310 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311
Thomas Tsou204a9f12013-10-29 18:34:16 -0400312 double setRxGain(double db, size_t chan);
313 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000314 double maxRxGain(void) { return rx_gain_max; }
315 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000316
Thomas Tsou204a9f12013-10-29 18:34:16 -0400317 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000318 double maxTxGain(void) { return tx_gain_max; }
319 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320
Thomas Tsou204a9f12013-10-29 18:34:16 -0400321 double getTxFreq(size_t chan);
322 double getRxFreq(size_t chan);
323 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400325 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326 inline double numberRead() { return rx_pkt_cnt; }
327 inline double numberWritten() { return 0; }
328
329 /** Receive and process asynchronous message
330 @return true if message received or false on timeout or error
331 */
332 bool recv_async_msg();
333
kurtis.heimerld4be0742011-11-26 03:17:46 +0000334 enum err_code {
335 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800336 ERROR_TIMEOUT = -2,
337 ERROR_UNRECOVERABLE = -3,
338 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000339 };
340
kurtis.heimerl965e7572011-11-26 03:16:54 +0000341private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000342 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400343 uhd::tx_streamer::sptr tx_stream;
344 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400345 enum TxWindowType tx_window;
346 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347
Thomas Tsou204a9f12013-10-29 18:34:16 -0400348 size_t sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400349 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000350
Thomas Tsou204a9f12013-10-29 18:34:16 -0400351 double tx_gain_min, tx_gain_max;
352 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500353 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000354
Thomas Tsou204a9f12013-10-29 18:34:16 -0400355 std::vector<double> tx_gains, rx_gains;
356 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000357 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000358
359 bool started;
360 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000361
362 size_t rx_pkt_cnt;
363 size_t drop_cnt;
364 uhd::time_spec_t prev_ts;
365
Thomas Tsou18d3b832014-02-13 14:55:23 -0500366 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400367 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368
kurtis.heimerl02d04052011-11-26 03:17:10 +0000369 void init_gains();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400370 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400371 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000372 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000373 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000374 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000375
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376 std::string str_code(uhd::rx_metadata_t metadata);
377 std::string str_code(uhd::async_metadata_t metadata);
378
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500379 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
380 bool set_freq(double freq, size_t chan, bool tx);
381
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800382 Thread *async_event_thrd;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500383 bool diversity;
Tom Tsou93b7f372014-12-15 20:23:33 -0800384 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000385};
386
387void *async_event_loop(uhd_device *dev)
388{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500389 dev->setPriority(0.43);
390
kurtis.heimerl965e7572011-11-26 03:16:54 +0000391 while (1) {
392 dev->recv_async_msg();
393 pthread_testcancel();
394 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500395
396 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000397}
398
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000399/*
400 Catch and drop underrun 'U' and overrun 'O' messages from stdout
401 since we already report using the logging facility. Direct
402 everything else appropriately.
403 */
404void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
405{
406 switch (type) {
407 case uhd::msg::status:
408 LOG(INFO) << msg;
409 break;
410 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000411 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000412 break;
413 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000414 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000415 break;
416 case uhd::msg::fastpath:
417 break;
418 }
419}
420
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800421static void thread_enable_cancel(bool cancel)
422{
423 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
424 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
425}
426
Thomas Tsou8e17df72014-03-06 14:16:11 -0500427uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428 : tx_gain_min(0.0), tx_gain_max(0.0),
429 rx_gain_min(0.0), rx_gain_max(0.0),
430 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000431 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500432 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000433{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400434 this->sps = sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400435 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500436 this->offset = offset;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500437 this->diversity = diversity;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000438}
439
440uhd_device::~uhd_device()
441{
442 stop();
443
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444 for (size_t i = 0; i < rx_buffers.size(); i++)
445 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000446}
447
kurtis.heimerl24481de2011-11-26 03:17:18 +0000448void uhd_device::init_gains()
449{
450 uhd::gain_range_t range;
451
452 range = usrp_dev->get_tx_gain_range();
453 tx_gain_min = range.start();
454 tx_gain_max = range.stop();
455
456 range = usrp_dev->get_rx_gain_range();
457 rx_gain_min = range.start();
458 rx_gain_max = range.stop();
459
Thomas Tsou204a9f12013-10-29 18:34:16 -0400460 for (size_t i = 0; i < tx_gains.size(); i++) {
461 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
462 tx_gains[i] = usrp_dev->get_tx_gain(i);
463 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000464
Thomas Tsou204a9f12013-10-29 18:34:16 -0400465 for (size_t i = 0; i < rx_gains.size(); i++) {
466 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
467 rx_gains[i] = usrp_dev->get_rx_gain(i);
468 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000469
470 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400471
kurtis.heimerl24481de2011-11-26 03:17:18 +0000472}
473
Thomas Tsou02d88d12013-04-05 15:36:30 -0400474int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000475{
Thomas Tsou092f7572013-04-04 17:04:39 -0400476 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000477
Thomas Tsou7e068062013-04-08 19:39:37 -0400478 try {
479 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400480 } catch (const std::exception &ex) {
481 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
482 LOG(ALERT) << ex.what();
483 return -1;
484 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000485
Thomas Tsou092f7572013-04-04 17:04:39 -0400486 actual = usrp_dev->get_master_clock_rate();
487 offset = fabs(clk_rate - actual);
488
489 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000490 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400491 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400492 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400493 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000494 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400495
496 return 0;
497}
498
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400499int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400500{
Thomas Tsou092f7572013-04-04 17:04:39 -0400501 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400502 double tx_offset, rx_offset;
503
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100504 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800505 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400506 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400507 return -1;
508 }
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100509 else if (dev_type == E1XX) {
510 if (set_master_clk(E1XX_CLK_RT) < 0)
511 return -1;
512 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000513
514 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400515 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400516 usrp_dev->set_tx_rate(tx_rate);
517 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400518 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400519 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400520 LOG(ALERT) << ex.what();
521 return -1;
522 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400523 this->tx_rate = usrp_dev->get_tx_rate();
524 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000525
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400526 tx_offset = fabs(this->tx_rate - tx_rate);
527 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400528 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000529 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400530 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
531 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400532 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000533 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000534
Thomas Tsou02d88d12013-04-05 15:36:30 -0400535 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000536}
537
Thomas Tsou204a9f12013-10-29 18:34:16 -0400538double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000539{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400540 if (chan >= tx_gains.size()) {
541 LOG(ALERT) << "Requested non-existent channel" << chan;
542 return 0.0f;
543 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000544
Thomas Tsou204a9f12013-10-29 18:34:16 -0400545 usrp_dev->set_tx_gain(db, chan);
546 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000547
Thomas Tsou204a9f12013-10-29 18:34:16 -0400548 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
549
550 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000551}
552
Thomas Tsou204a9f12013-10-29 18:34:16 -0400553double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000554{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400555 if (chan >= rx_gains.size()) {
556 LOG(ALERT) << "Requested non-existent channel " << chan;
557 return 0.0f;
558 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000559
Thomas Tsou204a9f12013-10-29 18:34:16 -0400560 usrp_dev->set_rx_gain(db, chan);
561 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000562
Thomas Tsou204a9f12013-10-29 18:34:16 -0400563 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
564
565 return rx_gains[chan];
566}
567
568double uhd_device::getRxGain(size_t chan)
569{
570 if (chan >= rx_gains.size()) {
571 LOG(ALERT) << "Requested non-existent channel " << chan;
572 return 0.0f;
573 }
574
575 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000576}
577
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000578/*
579 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400580 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000581 deal with the transport latency. Reject the USRP1 because UHD doesn't
582 support timestamped samples with it.
583 */
584bool uhd_device::parse_dev_type()
585{
586 std::string mboard_str, dev_str;
587 uhd::property_tree::sptr prop_tree;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800588 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
589 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000590
591 prop_tree = usrp_dev->get_device()->get_tree();
592 dev_str = prop_tree->access<std::string>("/name").get();
593 mboard_str = usrp_dev->get_mboard_name();
594
595 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400596 usrp2_str = dev_str.find("USRP2");
597 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400598 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400599 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100600 e100_str = mboard_str.find("E100");
601 e110_str = mboard_str.find("E110");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800602 e310_str = mboard_str.find("E310");
603 x300_str = mboard_str.find("X300");
604 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400605 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000606
607 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000608 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
609 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400610 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000611 return false;
612 }
613
Thomas Tsoucb69f082013-04-08 14:18:26 -0400614 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400615 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400616 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400617 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500618 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500619 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400620 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500621 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500622 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100623 } else if (e100_str != std::string::npos) {
624 tx_window = TX_WINDOW_FIXED;
625 dev_type = E1XX;
626 } else if (e110_str != std::string::npos) {
627 tx_window = TX_WINDOW_FIXED;
628 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400629 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500630 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400631 dev_type = USRP2;
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800632 } else if (e310_str != std::string::npos) {
633 tx_window = TX_WINDOW_FIXED;
634 dev_type = E3XX;
635 } else if (x300_str != std::string::npos) {
636 tx_window = TX_WINDOW_FIXED;
637 dev_type = X3XX;
638 } else if (x310_str != std::string::npos) {
639 tx_window = TX_WINDOW_FIXED;
640 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400641 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500642 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400643 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400644 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400645 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400646 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000647 }
648
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500649 if (tx_window == TX_WINDOW_USRP1) {
650 LOG(INFO) << "Using USRP1 type transmit window for "
651 << dev_str << " " << mboard_str;
652 } else {
653 LOG(INFO) << "Using fixed transmit window for "
654 << dev_str << " " << mboard_str;
655 }
656
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000657 return true;
658}
659
Thomas Tsou010fff72013-10-16 00:31:18 -0400660int uhd_device::open(const std::string &args, bool extref)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000661{
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000662 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000663 uhd::device_addr_t addr(args);
664 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000665 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000666 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400667 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000668 }
669
670 // Use the first found device
671 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000672 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000673 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000674 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000675 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400676 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000677 }
678
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000679 // Check for a valid device type and set bus type
680 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400681 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000682
Thomas Tsou204a9f12013-10-29 18:34:16 -0400683 // Verify and set channels
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500684 if ((dev_type == B210) && (chans == 2)) {
685 } else if ((dev_type == UMTRX) && (chans == 2)) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400686 uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
687 usrp_dev->set_tx_subdev_spec(subdev_spec);
688 usrp_dev->set_rx_subdev_spec(subdev_spec);
689 } else if (chans != 1) {
690 LOG(ALERT) << "Invalid channel combination for device";
691 return -1;
692 }
693
694 tx_freqs.resize(chans);
695 rx_freqs.resize(chans);
696 tx_gains.resize(chans);
697 rx_gains.resize(chans);
698 rx_buffers.resize(chans);
699
Thomas Tsou010fff72013-10-16 00:31:18 -0400700 if (extref)
Thomas Tsou0169b312013-10-29 19:25:15 -0400701 usrp_dev->set_clock_source("external");
Thomas Tsou010fff72013-10-16 00:31:18 -0400702
kurtis.heimerl965e7572011-11-26 03:16:54 +0000703 // Set rates
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500704 double _rx_rate;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400705 double _tx_rate = select_rate(dev_type, sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500706 if (diversity)
707 _rx_rate = select_rate(dev_type, 1, true);
708 else
709 _rx_rate = _tx_rate / sps;
710
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500711 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
712 return -1;
713 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400714 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000715
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400716 /* Create TX and RX streamers */
717 uhd::stream_args_t stream_args("sc16");
718 for (size_t i = 0; i < chans; i++)
719 stream_args.channels.push_back(i);
720
721 tx_stream = usrp_dev->get_tx_stream(stream_args);
722 rx_stream = usrp_dev->get_rx_stream(stream_args);
723
724 /* Number of samples per over-the-wire packet */
725 tx_spp = tx_stream->get_max_num_samps();
726 rx_spp = rx_stream->get_max_num_samps();
727
kurtis.heimerl965e7572011-11-26 03:16:54 +0000728 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400729 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400730 for (size_t i = 0; i < rx_buffers.size(); i++)
731 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000732
733 // Set receive chain sample offset
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500734 double offset = get_dev_offset(dev_type, sps, diversity);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400735 if (offset == 0.0) {
736 LOG(ERR) << "Unsupported configuration, no correction applied";
737 ts_offset = 0;
738 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400739 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400740 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000741
kurtis.heimerl02d04052011-11-26 03:17:10 +0000742 // Initialize and shadow gain values
743 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000744
kurtis.heimerl965e7572011-11-26 03:16:54 +0000745 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000746 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000747
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500748 if (diversity)
749 return DIVERSITY;
750
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400751 switch (dev_type) {
752 case B100:
753 return RESAMP_64M;
754 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800755 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400756 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500757 case B200:
758 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100759 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800760 case E3XX:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500761 default:
762 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400763 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400764
765 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000766}
767
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000768bool uhd_device::flush_recv(size_t num_pkts)
769{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000770 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000771 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800772 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000773
Thomas Tsou18d3b832014-02-13 14:55:23 -0500774 std::vector<std::vector<short> >
775 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000776
Thomas Tsou18d3b832014-02-13 14:55:23 -0500777 std::vector<short *> pkt_ptrs;
778 for (size_t i = 0; i < pkt_bufs.size(); i++)
779 pkt_ptrs.push_back(&pkt_bufs[i].front());
780
781 ts_initial = 0;
782 while (!ts_initial || (num_pkts-- > 0)) {
783 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400784 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000785 if (!num_smpls) {
786 switch (md.error_code) {
787 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800788 LOG(ALERT) << "Device timed out";
789 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000790 default:
791 continue;
792 }
793 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500794
795 ts_initial = convert_time(md.time_spec, rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000796 }
797
Thomas Tsou18d3b832014-02-13 14:55:23 -0500798 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
799
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000800 return true;
801}
802
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800803bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000804{
Thomas Tsouc2839162014-03-27 13:52:58 -0400805 /* Allow 100 ms delay to align multi-channel streams */
806 double delay = 0.1;
807
kurtis.heimerl68292102011-11-26 03:17:28 +0000808 aligned = false;
809
Thomas Tsouc2839162014-03-27 13:52:58 -0400810 uhd::time_spec_t current = usrp_dev->get_time_now();
811
Thomas Tsou204a9f12013-10-29 18:34:16 -0400812 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400813 cmd.stream_now = false;
814 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400815
kurtis.heimerl68292102011-11-26 03:17:28 +0000816 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500817
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800818 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000819}
820
kurtis.heimerl965e7572011-11-26 03:16:54 +0000821bool uhd_device::start()
822{
823 LOG(INFO) << "Starting USRP...";
824
825 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000826 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000827 return false;
828 }
829
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400830 // Register msg handler
831 uhd::msg::register_handler(&uhd_msg_handler);
832
kurtis.heimerl965e7572011-11-26 03:16:54 +0000833 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800834 async_event_thrd = new Thread();
835 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000836
837 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800838 if (!restart())
839 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000840
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841 // Display usrp time
842 double time_now = usrp_dev->get_time_now().get_real_secs();
843 LOG(INFO) << "The current time is " << time_now << " seconds";
844
845 started = true;
846 return true;
847}
848
849bool uhd_device::stop()
850{
Thomas Tsou2c791102013-11-15 23:00:28 -0500851 if (!started)
852 return false;
853
854 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000855 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
856
857 usrp_dev->issue_stream_cmd(stream_cmd);
858
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800859 async_event_thrd->cancel();
860 async_event_thrd->join();
861 delete async_event_thrd;
862
kurtis.heimerl965e7572011-11-26 03:16:54 +0000863 started = false;
864 return true;
865}
866
Thomas Tsou7553aa92013-11-08 12:50:03 -0500867void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500869 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000870 return;
871}
872
kurtis.heimerld4be0742011-11-26 03:17:46 +0000873int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000874{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000875 uhd::time_spec_t ts;
876
kurtis.heimerld4be0742011-11-26 03:17:46 +0000877 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000878 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000879
880 switch (md.error_code) {
881 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000882 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800883 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000884 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
885 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
886 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
887 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
888 default:
889 return ERROR_UNHANDLED;
890 }
891 }
892
kurtis.heimerl965e7572011-11-26 03:16:54 +0000893 // Missing timestamp
894 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000895 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000896 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000897 }
898
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000899 ts = md.time_spec;
900
kurtis.heimerl965e7572011-11-26 03:16:54 +0000901 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000902 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000903 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000904 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
905 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000906 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000907 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000908 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909 }
910
911 return 0;
912}
913
Thomas Tsou204a9f12013-10-29 18:34:16 -0400914int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
915 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000916{
917 ssize_t rc;
918 uhd::time_spec_t ts;
919 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920
Thomas Tsou204a9f12013-10-29 18:34:16 -0400921 if (bufs.size() != chans) {
922 LOG(ALERT) << "Invalid channel combination " << bufs.size();
923 return -1;
924 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000925
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000926 *overrun = false;
927 *underrun = false;
928
kurtis.heimerl965e7572011-11-26 03:16:54 +0000929 // Shift read time with respect to transmit clock
930 timestamp += ts_offset;
931
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400932 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000933 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000934
935 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400936 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000937 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800939 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000940 return 0;
941 }
942
Thomas Tsou204a9f12013-10-29 18:34:16 -0400943 // Create vector buffer
944 std::vector<std::vector<short> >
945 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
946
947 std::vector<short *> pkt_ptrs;
948 for (size_t i = 0; i < pkt_bufs.size(); i++)
949 pkt_ptrs.push_back(&pkt_bufs[i].front());
950
kurtis.heimerl965e7572011-11-26 03:16:54 +0000951 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800953 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400954 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
955 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800956 thread_enable_cancel(true);
957
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958 rx_pkt_cnt++;
959
kurtis.heimerld4be0742011-11-26 03:17:46 +0000960 // Check for errors
961 rc = check_rx_md_err(metadata, num_smpls);
962 switch (rc) {
963 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000964 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
965 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000966 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800967 case ERROR_TIMEOUT:
968 // Assume stopping condition
969 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000970 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400971 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000972 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000973 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000974 }
975
kurtis.heimerl965e7572011-11-26 03:16:54 +0000976 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000977 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000978
Thomas Tsou204a9f12013-10-29 18:34:16 -0400979 for (size_t i = 0; i < rx_buffers.size(); i++) {
980 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
981 num_smpls,
982 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000983
Thomas Tsou204a9f12013-10-29 18:34:16 -0400984 // Continue on local overrun, exit on other errors
985 if ((rc < 0)) {
986 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800987 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400988 if (rc != smpl_buf::ERROR_OVERFLOW)
989 return 0;
990 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000991 }
992 }
993
994 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400995 for (size_t i = 0; i < rx_buffers.size(); i++) {
996 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
997 if ((rc < 0) || (rc != len)) {
998 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800999 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001000 return 0;
1001 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001002 }
1003
1004 return len;
1005}
1006
Thomas Tsou204a9f12013-10-29 18:34:16 -04001007int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1008 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001009{
1010 uhd::tx_metadata_t metadata;
1011 metadata.has_time_spec = true;
1012 metadata.start_of_burst = false;
1013 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -04001014 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001015
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001016 *underrun = false;
1017
kurtis.heimerl965e7572011-11-26 03:16:54 +00001018 // No control packets
1019 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001020 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001021 return 0;
1022 }
1023
Thomas Tsou204a9f12013-10-29 18:34:16 -04001024 if (bufs.size() != chans) {
1025 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1026 return -1;
1027 }
1028
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029 // Drop a fixed number of packets (magic value)
1030 if (!aligned) {
1031 drop_cnt++;
1032
1033 if (drop_cnt == 1) {
1034 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001035 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001036 metadata.end_of_burst = true;
1037 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001038 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001039 return len;
1040 } else {
1041 LOG(DEBUG) << "Aligning transmitter: start burst";
1042 metadata.start_of_burst = true;
1043 aligned = true;
1044 drop_cnt = 0;
1045 }
1046 }
1047
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001048 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001049 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001050 thread_enable_cancel(true);
1051
ttsoub371ed52012-01-09 18:11:34 +00001052 if (num_smpls != (unsigned) len) {
1053 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001054 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055
1056 return num_smpls;
1057}
1058
1059bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1060{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001061 return true;
1062}
1063
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001064uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1065{
1066 double rf_spread, rf_freq;
1067 std::vector<double> freqs;
1068 uhd::tune_request_t treq(freq);
1069
Thomas Tsou8e17df72014-03-06 14:16:11 -05001070 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
1071 if (offset == 0.0)
1072 return treq;
1073
1074 return uhd::tune_request_t(freq, offset);
1075 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001076 LOG(ALERT) << chans << " channels unsupported";
1077 return treq;
1078 }
1079
1080 if (tx)
1081 freqs = tx_freqs;
1082 else
1083 freqs = rx_freqs;
1084
1085 /* Tune directly if other channel isn't tuned */
1086 if (freqs[!chan] < 10.0)
1087 return treq;
1088
1089 /* Find center frequency between channels */
1090 rf_spread = fabs(freqs[!chan] - freq);
1091 if (rf_spread > B2XX_CLK_RT) {
1092 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1093 return treq;
1094 }
1095
1096 rf_freq = (freqs[!chan] + freq) / 2.0f;
1097
1098 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1099 treq.target_freq = freq;
1100 treq.rf_freq = rf_freq;
1101
1102 return treq;
1103}
1104
1105bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1106{
1107 std::vector<double> freqs;
1108 uhd::tune_result_t tres;
1109 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1110
1111 if (tx) {
1112 tres = usrp_dev->set_tx_freq(treq, chan);
1113 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1114 } else {
1115 tres = usrp_dev->set_rx_freq(treq, chan);
1116 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1117 }
1118 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1119
Thomas Tsou8e17df72014-03-06 14:16:11 -05001120 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1121 return true;
1122
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001123 /* Manual RF policy means we intentionally tuned with a baseband
1124 * offset for dual-channel purposes. Now retune the other channel
1125 * with the opposite corresponding frequency offset
1126 */
1127 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1128 if (tx) {
1129 treq = select_freq(tx_freqs[!chan], !chan, true);
1130 tres = usrp_dev->set_tx_freq(treq, !chan);
1131 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1132 } else {
1133 treq = select_freq(rx_freqs[!chan], !chan, false);
1134 tres = usrp_dev->set_rx_freq(treq, !chan);
1135 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1136
1137 }
1138 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1139 }
1140
1141 return true;
1142}
1143
Thomas Tsou204a9f12013-10-29 18:34:16 -04001144bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001145{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001146 if (chan >= tx_freqs.size()) {
1147 LOG(ALERT) << "Requested non-existent channel " << chan;
1148 return false;
1149 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001150 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001151
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001152 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001153}
1154
Thomas Tsou204a9f12013-10-29 18:34:16 -04001155bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001156{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001157 if (chan >= rx_freqs.size()) {
1158 LOG(ALERT) << "Requested non-existent channel " << chan;
1159 return false;
1160 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001161 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001162
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001163 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001164}
1165
Thomas Tsou204a9f12013-10-29 18:34:16 -04001166double uhd_device::getTxFreq(size_t chan)
1167{
1168 if (chan >= tx_freqs.size()) {
1169 LOG(ALERT) << "Requested non-existent channel " << chan;
1170 return 0.0;
1171 }
1172
1173 return tx_freqs[chan];
1174}
1175
1176double uhd_device::getRxFreq(size_t chan)
1177{
1178 if (chan >= rx_freqs.size()) {
1179 LOG(ALERT) << "Requested non-existent channel " << chan;
1180 return 0.0;
1181 }
1182
1183 return rx_freqs[chan];
1184}
1185
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001186double uhd_device::fullScaleInputValue()
1187{
1188 if (dev_type == UMTRX)
1189 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1190 else
1191 return (double) SHRT_MAX * USRP_TX_AMPL;
1192}
1193
1194double uhd_device::fullScaleOutputValue()
1195{
1196 return (double) SHRT_MAX;
1197}
1198
kurtis.heimerl965e7572011-11-26 03:16:54 +00001199bool uhd_device::recv_async_msg()
1200{
ttsou60dc4c92012-08-08 23:30:23 +00001201 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001202
1203 thread_enable_cancel(false);
1204 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1205 thread_enable_cancel(true);
1206 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001207 return false;
1208
1209 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001210 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001211 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001212
1213 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1214 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1215 LOG(ERR) << str_code(md);
1216 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001217 }
1218
1219 return true;
1220}
1221
1222std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1223{
1224 std::ostringstream ost("UHD: ");
1225
1226 switch (metadata.error_code) {
1227 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1228 ost << "No error";
1229 break;
1230 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1231 ost << "No packet received, implementation timed-out";
1232 break;
1233 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1234 ost << "A stream command was issued in the past";
1235 break;
1236 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1237 ost << "Expected another stream command";
1238 break;
1239 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1240 ost << "An internal receive buffer has filled";
1241 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001242 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1243 ost << "Multi-channel alignment failed";
1244 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001245 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1246 ost << "The packet could not be parsed";
1247 break;
1248 default:
1249 ost << "Unknown error " << metadata.error_code;
1250 }
1251
1252 if (metadata.has_time_spec)
1253 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1254
1255 return ost.str();
1256}
1257
1258std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1259{
1260 std::ostringstream ost("UHD: ");
1261
1262 switch (metadata.event_code) {
1263 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1264 ost << "A packet was successfully transmitted";
1265 break;
1266 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1267 ost << "An internal send buffer has emptied";
1268 break;
1269 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1270 ost << "Packet loss between host and device";
1271 break;
1272 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1273 ost << "Packet time was too late or too early";
1274 break;
1275 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1276 ost << "Underflow occurred inside a packet";
1277 break;
1278 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1279 ost << "Packet loss within a burst";
1280 break;
1281 default:
1282 ost << "Unknown error " << metadata.event_code;
1283 }
1284
1285 if (metadata.has_time_spec)
1286 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1287
1288 return ost.str();
1289}
1290
1291smpl_buf::smpl_buf(size_t len, double rate)
1292 : buf_len(len), clk_rt(rate),
1293 time_start(0), time_end(0), data_start(0), data_end(0)
1294{
1295 data = new uint32_t[len];
1296}
1297
1298smpl_buf::~smpl_buf()
1299{
1300 delete[] data;
1301}
1302
1303ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1304{
1305 if (timestamp < time_start)
1306 return ERROR_TIMESTAMP;
1307 else if (timestamp >= time_end)
1308 return 0;
1309 else
1310 return time_end - timestamp;
1311}
1312
1313ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1314{
1315 return avail_smpls(convert_time(timespec, clk_rt));
1316}
1317
1318ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1319{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001320 int type_sz = 2 * sizeof(short);
1321
kurtis.heimerl965e7572011-11-26 03:16:54 +00001322 // Check for valid read
1323 if (timestamp < time_start)
1324 return ERROR_TIMESTAMP;
1325 if (timestamp >= time_end)
1326 return 0;
1327 if (len >= buf_len)
1328 return ERROR_READ;
1329
1330 // How many samples should be copied
1331 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001332 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001333 num_smpls = len;
1334
1335 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001336 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001337
1338 // Read it
1339 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001340 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001341 memcpy(buf, data + read_start, numBytes);
1342 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001343 size_t first_cp = (buf_len - read_start) * type_sz;
1344 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001345
1346 memcpy(buf, data + read_start, first_cp);
1347 memcpy((char*) buf + first_cp, data, second_cp);
1348 }
1349
1350 data_start = (read_start + len) % buf_len;
1351 time_start = timestamp + len;
1352
1353 if (time_start > time_end)
1354 return ERROR_READ;
1355 else
1356 return num_smpls;
1357}
1358
1359ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1360{
1361 return read(buf, len, convert_time(ts, clk_rt));
1362}
1363
1364ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1365{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001366 int type_sz = 2 * sizeof(short);
1367
kurtis.heimerl965e7572011-11-26 03:16:54 +00001368 // Check for valid write
1369 if ((len == 0) || (len >= buf_len))
1370 return ERROR_WRITE;
1371 if ((timestamp + len) <= time_end)
1372 return ERROR_TIMESTAMP;
1373
1374 // Starting index
1375 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1376
1377 // Write it
1378 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001379 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001380 memcpy(data + write_start, buf, numBytes);
1381 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001382 size_t first_cp = (buf_len - write_start) * type_sz;
1383 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001384
1385 memcpy(data + write_start, buf, first_cp);
1386 memcpy(data, (char*) buf + first_cp, second_cp);
1387 }
1388
1389 data_end = (write_start + len) % buf_len;
1390 time_end = timestamp + len;
1391
Thomas Tsou18d3b832014-02-13 14:55:23 -05001392 if (!data_start)
1393 data_start = write_start;
1394
kurtis.heimerl965e7572011-11-26 03:16:54 +00001395 if (((write_start + len) > buf_len) && (data_end > data_start))
1396 return ERROR_OVERFLOW;
1397 else if (time_end <= time_start)
1398 return ERROR_WRITE;
1399 else
1400 return len;
1401}
1402
1403ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1404{
1405 return write(buf, len, convert_time(ts, clk_rt));
1406}
1407
Tom Tsou1ae25562014-12-05 12:56:34 -08001408std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001409{
1410 std::ostringstream ost("Sample buffer: ");
1411
Tom Tsou1ae25562014-12-05 12:56:34 -08001412 ost << "timestamp = " << ts;
1413 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001414 ost << ", time_start = " << time_start;
1415 ost << ", time_end = " << time_end;
1416 ost << ", data_start = " << data_start;
1417 ost << ", data_end = " << data_end;
1418
1419 return ost.str();
1420}
1421
1422std::string smpl_buf::str_code(ssize_t code)
1423{
1424 switch (code) {
1425 case ERROR_TIMESTAMP:
1426 return "Sample buffer: Requested timestamp is not valid";
1427 case ERROR_READ:
1428 return "Sample buffer: Read error";
1429 case ERROR_WRITE:
1430 return "Sample buffer: Write error";
1431 case ERROR_OVERFLOW:
1432 return "Sample buffer: Overrun";
1433 default:
1434 return "Sample buffer: Unknown error";
1435 }
1436}
1437
Thomas Tsou8e17df72014-03-06 14:16:11 -05001438RadioDevice *RadioDevice::make(size_t sps, size_t chans,
1439 bool diversity, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001440{
Thomas Tsou8e17df72014-03-06 14:16:11 -05001441 return new uhd_device(sps, chans, diversity, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001442}