blob: f4b7cf449152dd432da85982fea2736c240e88a1 [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 Tsoufe269fe2013-10-14 23:56:51 -040035#define B2XX_CLK_RT 52e6
36#define B2XX_BASE_RT GSMRATE
37#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040038#define USRP2_BASE_RT 390625
Thomas Tsoue3e88142013-04-05 20:42:41 -040039#define TX_AMPL 0.3
40#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040041
42enum uhd_dev_type {
43 USRP1,
44 USRP2,
45 B100,
Thomas Tsou69d14c92013-10-11 14:27:35 -040046 B2XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040047 UMTRX,
Thomas Tsou02d88d12013-04-05 15:36:30 -040048 NUM_USRP_TYPES,
49};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000050
Thomas Tsoue3e88142013-04-05 20:42:41 -040051struct uhd_dev_offset {
52 enum uhd_dev_type type;
53 int sps;
54 double offset;
55};
56
kurtis.heimerl965e7572011-11-26 03:16:54 +000057/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040058 * Tx / Rx sample offset values. In a perfect world, there is no group delay
59 * though analog components, and behaviour through digital filters exactly
60 * matches calculated values. In reality, there are unaccounted factors,
61 * which are captured in these empirically measured (using a loopback test)
62 * timing correction values.
63 *
64 * Notes:
65 * USRP1 with timestamps is not supported by UHD.
66 */
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080067static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
Thomas Tsoue3e88142013-04-05 20:42:41 -040068 { USRP1, 1, 0.0 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040069 { USRP1, 4, 0.0 },
Thomas Tsoufe269fe2013-10-14 23:56:51 -040070 { USRP2, 1, 1.2184e-4 },
71 { USRP2, 4, 8.0230e-5 },
72 { B100, 1, 1.2104e-4 },
73 { B100, 4, 7.9307e-5 },
Thomas Tsou69d14c92013-10-11 14:27:35 -040074 { B2XX, 1, 9.9692e-5 },
75 { B2XX, 4, 6.9248e-5 },
Thomas Tsouc1f7c422013-10-11 13:49:55 -040076 { UMTRX, 1, 9.9692e-5 },
77 { UMTRX, 4, 7.3846e-5 },
Thomas Tsoue3e88142013-04-05 20:42:41 -040078};
kurtis.heimerl965e7572011-11-26 03:16:54 +000079
Thomas Tsoue3e88142013-04-05 20:42:41 -040080static double get_dev_offset(enum uhd_dev_type type, int sps)
81{
82 if (type == USRP1) {
83 LOG(ERR) << "Invalid device type";
84 return 0.0;
85 }
kurtis.heimerl965e7572011-11-26 03:16:54 +000086
Thomas Tsoue3e88142013-04-05 20:42:41 -040087 switch (sps) {
88 case 1:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080089 return uhd_offsets[2 * type + 0].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040090 case 4:
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080091 return uhd_offsets[2 * type + 1].offset;
Thomas Tsoue3e88142013-04-05 20:42:41 -040092 }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000093
Thomas Tsoue3e88142013-04-05 20:42:41 -040094 LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
Thomas Tsoua57bc8a2013-09-05 08:16:47 +080095 return 0.0;
Thomas Tsoue3e88142013-04-05 20:42:41 -040096}
kurtis.heimerlce317332011-11-26 03:18:39 +000097
Thomas Tsoucb69f082013-04-08 14:18:26 -040098/*
99 * Select sample rate based on device type and requested samples-per-symbol.
100 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
101 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800102 */
Thomas Tsoucb69f082013-04-08 14:18:26 -0400103static double select_rate(uhd_dev_type type, int sps)
104{
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800105 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400106 return -9999.99;
107
108 switch (type) {
109 case USRP2:
110 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400111 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400112 return B100_BASE_RT * sps;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400113 case B2XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400114 case UMTRX:
115 return GSMRATE * sps;
116 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400117 break;
118 }
119
120 LOG(ALERT) << "Unknown device type " << type;
121 return -9999.99;
122}
123
kurtis.heimerl965e7572011-11-26 03:16:54 +0000124/** Timestamp conversion
125 @param timestamp a UHD or OpenBTS timestamp
126 @param rate sample rate
127 @return the converted timestamp
128*/
129uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
130{
131 double secs = (double) ticks / rate;
132 return uhd::time_spec_t(secs);
133}
134
135TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
136{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000137 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000138 return ts.get_tick_count(rate) + ticks;
139}
140
141/*
142 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
143 or UHD style timestamps. Time conversions are handled
144 internally or accessable through the static convert calls.
145*/
146class smpl_buf {
147public:
148 /** Sample buffer constructor
149 @param len number of 32-bit samples the buffer should hold
150 @param rate sample clockrate
151 @param timestamp
152 */
153 smpl_buf(size_t len, double rate);
154 ~smpl_buf();
155
156 /** Query number of samples available for reading
157 @param timestamp time of first sample
158 @return number of available samples or error
159 */
160 ssize_t avail_smpls(TIMESTAMP timestamp) const;
161 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
162
163 /** Read and write
164 @param buf pointer to buffer
165 @param len number of samples desired to read or write
166 @param timestamp time of first stample
167 @return number of actual samples read or written or error
168 */
169 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
170 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
171 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
172 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
173
174 /** Buffer status string
175 @return a formatted string describing internal buffer state
176 */
177 std::string str_status() const;
178
179 /** Formatted error string
180 @param code an error code
181 @return a formatted error string
182 */
183 static std::string str_code(ssize_t code);
184
185 enum err_code {
186 ERROR_TIMESTAMP = -1,
187 ERROR_READ = -2,
188 ERROR_WRITE = -3,
189 ERROR_OVERFLOW = -4
190 };
191
192private:
193 uint32_t *data;
194 size_t buf_len;
195
196 double clk_rt;
197
198 TIMESTAMP time_start;
199 TIMESTAMP time_end;
200
201 size_t data_start;
202 size_t data_end;
203};
204
205/*
206 uhd_device - UHD implementation of the Device interface. Timestamped samples
207 are sent to and received from the device. An intermediate buffer
208 on the receive side collects and aligns packets of samples.
209 Events and errors such as underruns are reported asynchronously
210 by the device and received in a separate thread.
211*/
212class uhd_device : public RadioDevice {
213public:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400214 uhd_device(int sps, bool skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000215 ~uhd_device();
216
Thomas Tsoucb69f082013-04-08 14:18:26 -0400217 int open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000218 bool start();
219 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000220 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000221 void setPriority();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400222 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000223
224 int readSamples(short *buf, int len, bool *overrun,
225 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
226
227 int writeSamples(short *buf, int len, bool *underrun,
228 TIMESTAMP timestamp, bool isControl);
229
230 bool updateAlignment(TIMESTAMP timestamp);
231
232 bool setTxFreq(double wFreq);
233 bool setRxFreq(double wFreq);
234
235 inline TIMESTAMP initialWriteTimestamp() { return 0; }
236 inline TIMESTAMP initialReadTimestamp() { return 0; }
237
Thomas Tsoue3e88142013-04-05 20:42:41 -0400238 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000239 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000240
kurtis.heimerl02d04052011-11-26 03:17:10 +0000241 double setRxGain(double db);
242 double getRxGain(void) { return rx_gain; }
243 double maxRxGain(void) { return rx_gain_max; }
244 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000245
kurtis.heimerl02d04052011-11-26 03:17:10 +0000246 double setTxGain(double db);
247 double maxTxGain(void) { return tx_gain_max; }
248 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000249
kurtis.heimerl02d04052011-11-26 03:17:10 +0000250 double getTxFreq() { return tx_freq; }
251 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000252
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400253 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000254 inline double numberRead() { return rx_pkt_cnt; }
255 inline double numberWritten() { return 0; }
256
257 /** Receive and process asynchronous message
258 @return true if message received or false on timeout or error
259 */
260 bool recv_async_msg();
261
kurtis.heimerld4be0742011-11-26 03:17:46 +0000262 enum err_code {
263 ERROR_TIMING = -1,
264 ERROR_UNRECOVERABLE = -2,
265 ERROR_UNHANDLED = -3,
266 };
267
kurtis.heimerl965e7572011-11-26 03:16:54 +0000268private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000269 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400270 uhd::tx_streamer::sptr tx_stream;
271 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400272 enum TxWindowType tx_window;
273 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000274
Thomas Tsoue3e88142013-04-05 20:42:41 -0400275 int sps;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400276 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277
kurtis.heimerl02d04052011-11-26 03:17:10 +0000278 double tx_gain, tx_gain_min, tx_gain_max;
279 double rx_gain, rx_gain_min, rx_gain_max;
280
281 double tx_freq, rx_freq;
282 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000283
284 bool started;
285 bool aligned;
286 bool skip_rx;
287
288 size_t rx_pkt_cnt;
289 size_t drop_cnt;
290 uhd::time_spec_t prev_ts;
291
292 TIMESTAMP ts_offset;
293 smpl_buf *rx_smpl_buf;
294
kurtis.heimerl02d04052011-11-26 03:17:10 +0000295 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000296 void set_ref_clk(bool ext_clk);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400297 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400298 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000299 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000300 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000301 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000302
kurtis.heimerl965e7572011-11-26 03:16:54 +0000303 std::string str_code(uhd::rx_metadata_t metadata);
304 std::string str_code(uhd::async_metadata_t metadata);
305
306 Thread async_event_thrd;
307};
308
309void *async_event_loop(uhd_device *dev)
310{
311 while (1) {
312 dev->recv_async_msg();
313 pthread_testcancel();
314 }
315}
316
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000317/*
318 Catch and drop underrun 'U' and overrun 'O' messages from stdout
319 since we already report using the logging facility. Direct
320 everything else appropriately.
321 */
322void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
323{
324 switch (type) {
325 case uhd::msg::status:
326 LOG(INFO) << msg;
327 break;
328 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000329 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000330 break;
331 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000332 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000333 break;
334 case uhd::msg::fastpath:
335 break;
336 }
337}
338
Thomas Tsoucb69f082013-04-08 14:18:26 -0400339uhd_device::uhd_device(int sps, bool skip_rx)
340 : tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
kurtis.heimerl02d04052011-11-26 03:17:10 +0000341 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
342 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000343 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000344 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
345{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400346 this->sps = sps;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347 this->skip_rx = skip_rx;
348}
349
350uhd_device::~uhd_device()
351{
352 stop();
353
354 if (rx_smpl_buf)
355 delete rx_smpl_buf;
356}
357
kurtis.heimerl24481de2011-11-26 03:17:18 +0000358void uhd_device::init_gains()
359{
360 uhd::gain_range_t range;
361
362 range = usrp_dev->get_tx_gain_range();
363 tx_gain_min = range.start();
364 tx_gain_max = range.stop();
365
366 range = usrp_dev->get_rx_gain_range();
367 rx_gain_min = range.start();
368 rx_gain_max = range.stop();
369
370 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
371 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
372
373 tx_gain = usrp_dev->get_tx_gain();
374 rx_gain = usrp_dev->get_rx_gain();
375
376 return;
377}
378
379void uhd_device::set_ref_clk(bool ext_clk)
380{
kurtis.heimerl24481de2011-11-26 03:17:18 +0000381 if (ext_clk)
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400382 usrp_dev->set_clock_source("external");
kurtis.heimerl24481de2011-11-26 03:17:18 +0000383
384 return;
385}
386
Thomas Tsou02d88d12013-04-05 15:36:30 -0400387int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000388{
Thomas Tsou092f7572013-04-04 17:04:39 -0400389 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000390
Thomas Tsou7e068062013-04-08 19:39:37 -0400391 try {
392 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400393 } catch (const std::exception &ex) {
394 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
395 LOG(ALERT) << ex.what();
396 return -1;
397 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000398
Thomas Tsou092f7572013-04-04 17:04:39 -0400399 actual = usrp_dev->get_master_clock_rate();
400 offset = fabs(clk_rate - actual);
401
402 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000403 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400404 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400405 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400406 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000407 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400408
409 return 0;
410}
411
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400412int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400413{
Thomas Tsou092f7572013-04-04 17:04:39 -0400414 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400415 double tx_offset, rx_offset;
416
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400417 // B2XX is the only device where we set FPGA clocking
418 if (dev_type == B2XX) {
419 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400420 return -1;
421 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000422
423 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400424 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400425 usrp_dev->set_tx_rate(tx_rate);
426 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400427 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400428 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400429 LOG(ALERT) << ex.what();
430 return -1;
431 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400432 this->tx_rate = usrp_dev->get_tx_rate();
433 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000434
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400435 tx_offset = fabs(this->tx_rate - tx_rate);
436 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400437 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000438 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400439 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
440 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400441 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000443
Thomas Tsou02d88d12013-04-05 15:36:30 -0400444 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000445}
446
kurtis.heimerl02d04052011-11-26 03:17:10 +0000447double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000448{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000449 usrp_dev->set_tx_gain(db);
450 tx_gain = usrp_dev->get_tx_gain();
451
452 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
453
454 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000455}
456
kurtis.heimerl02d04052011-11-26 03:17:10 +0000457double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000458{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000459 usrp_dev->set_rx_gain(db);
460 rx_gain = usrp_dev->get_rx_gain();
461
462 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
463
464 return rx_gain;
465}
466
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000467/*
468 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400469 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000470 deal with the transport latency. Reject the USRP1 because UHD doesn't
471 support timestamped samples with it.
472 */
473bool uhd_device::parse_dev_type()
474{
475 std::string mboard_str, dev_str;
476 uhd::property_tree::sptr prop_tree;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400477 size_t usrp1_str, usrp2_str, b100_str, b200_str, b210_str, umtrx_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000478
479 prop_tree = usrp_dev->get_device()->get_tree();
480 dev_str = prop_tree->access<std::string>("/name").get();
481 mboard_str = usrp_dev->get_mboard_name();
482
483 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400484 usrp2_str = dev_str.find("USRP2");
485 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400486 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400487 b210_str = mboard_str.find("B210");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400488 umtrx_str = dev_str.find("UmTRX");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000489
490 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000491 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
492 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400493 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000494 return false;
495 }
496
Thomas Tsoucb69f082013-04-08 14:18:26 -0400497 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400498 tx_window = TX_WINDOW_USRP1;
499 LOG(INFO) << "Using USRP1 type transmit window for "
500 << dev_str << " " << mboard_str;
501 dev_type = B100;
502 return true;
Thomas Tsou092f7572013-04-04 17:04:39 -0400503 } else if (b200_str != std::string::npos) {
Thomas Tsou69d14c92013-10-11 14:27:35 -0400504 dev_type = B2XX;
505 } else if (b210_str != std::string::npos) {
506 dev_type = B2XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400507 } else if (usrp2_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400508 dev_type = USRP2;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400509 } else if (umtrx_str != std::string::npos) {
510 dev_type = UMTRX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400511 } else {
Thomas Tsou092f7572013-04-04 17:04:39 -0400512 LOG(ALERT) << "Unknown UHD device type " << dev_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400513 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000514 }
515
Thomas Tsou02d88d12013-04-05 15:36:30 -0400516 tx_window = TX_WINDOW_FIXED;
517 LOG(INFO) << "Using fixed transmit window for "
518 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000519 return true;
520}
521
Thomas Tsoucb69f082013-04-08 14:18:26 -0400522int uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000523{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000524 // Register msg handler
525 uhd::msg::register_handler(&uhd_msg_handler);
526
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000527 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000528 uhd::device_addr_t addr(args);
529 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000530 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000531 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400532 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000533 }
534
535 // Use the first found device
536 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000537 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000538 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000539 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000540 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400541 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000542 }
543
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000544 // Check for a valid device type and set bus type
545 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400546 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000547
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000548#ifdef EXTREF
549 set_ref_clk(true);
550#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400551 // Create TX and RX streamers
552 uhd::stream_args_t stream_args("sc16");
553 tx_stream = usrp_dev->get_tx_stream(stream_args);
554 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000555
kurtis.heimerl965e7572011-11-26 03:16:54 +0000556 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400557 tx_spp = tx_stream->get_max_num_samps();
558 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000559
560 // Set rates
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400561 double _tx_rate = select_rate(dev_type, sps);
562 double _rx_rate = _tx_rate / sps;
563 if ((_tx_rate > 0.0) && (set_rates(_tx_rate, _rx_rate) < 0))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400564 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000565
566 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400567 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400568 rx_smpl_buf = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000569
570 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400571 double offset = get_dev_offset(dev_type, sps);
572 if (offset == 0.0) {
573 LOG(ERR) << "Unsupported configuration, no correction applied";
574 ts_offset = 0;
575 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400576 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400577 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000578
kurtis.heimerl02d04052011-11-26 03:17:10 +0000579 // Initialize and shadow gain values
580 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000581
kurtis.heimerl965e7572011-11-26 03:16:54 +0000582 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000583 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000584
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400585 switch (dev_type) {
586 case B100:
587 return RESAMP_64M;
588 case USRP2:
589 return RESAMP_100M;
590 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400591
592 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000593}
594
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000595bool uhd_device::flush_recv(size_t num_pkts)
596{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000597 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000598 size_t num_smpls;
599 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000600 float timeout;
601
602 // Use .01 sec instead of the default .1 sec
603 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000604
605 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400606 num_smpls = rx_stream->recv(buff, rx_spp, md,
607 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000608 if (!num_smpls) {
609 switch (md.error_code) {
610 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
611 return true;
612 default:
613 continue;
614 }
615 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000616 }
617
618 return true;
619}
620
kurtis.heimerl68292102011-11-26 03:17:28 +0000621void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000622{
kurtis.heimerl68292102011-11-26 03:17:28 +0000623 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
624 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000625
kurtis.heimerl68292102011-11-26 03:17:28 +0000626 flush_recv(50);
627
628 usrp_dev->set_time_now(ts);
629 aligned = false;
630
631 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
632 cmd.stream_now = true;
633 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000634}
635
kurtis.heimerl965e7572011-11-26 03:16:54 +0000636bool uhd_device::start()
637{
638 LOG(INFO) << "Starting USRP...";
639
640 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000641 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000642 return false;
643 }
644
645 setPriority();
646
647 // Start asynchronous event (underrun check) loop
648 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
649
650 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000651 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000652
kurtis.heimerl965e7572011-11-26 03:16:54 +0000653 // Display usrp time
654 double time_now = usrp_dev->get_time_now().get_real_secs();
655 LOG(INFO) << "The current time is " << time_now << " seconds";
656
657 started = true;
658 return true;
659}
660
661bool uhd_device::stop()
662{
663 uhd::stream_cmd_t stream_cmd =
664 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
665
666 usrp_dev->issue_stream_cmd(stream_cmd);
667
668 started = false;
669 return true;
670}
671
672void uhd_device::setPriority()
673{
674 uhd::set_thread_priority_safe();
675 return;
676}
677
kurtis.heimerld4be0742011-11-26 03:17:46 +0000678int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000679{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000680 uhd::time_spec_t ts;
681
kurtis.heimerld4be0742011-11-26 03:17:46 +0000682 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000683 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000684
685 switch (md.error_code) {
686 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000687 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000688 return ERROR_UNRECOVERABLE;
689 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
690 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
691 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
692 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
693 default:
694 return ERROR_UNHANDLED;
695 }
696 }
697
kurtis.heimerl965e7572011-11-26 03:16:54 +0000698 // Missing timestamp
699 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000700 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000701 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000702 }
703
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000704 ts = md.time_spec;
705
kurtis.heimerl965e7572011-11-26 03:16:54 +0000706 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000707 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000708 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000709 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
710 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000711 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000712 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000713 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000714 }
715
716 return 0;
717}
718
719int uhd_device::readSamples(short *buf, int len, bool *overrun,
720 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
721{
722 ssize_t rc;
723 uhd::time_spec_t ts;
724 uhd::rx_metadata_t metadata;
725 uint32_t pkt_buf[rx_spp];
726
727 if (skip_rx)
728 return 0;
729
730 // Shift read time with respect to transmit clock
731 timestamp += ts_offset;
732
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400733 ts = convert_time(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000734 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000735
736 // Check that timestamp is valid
737 rc = rx_smpl_buf->avail_smpls(timestamp);
738 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000739 LOG(ERR) << rx_smpl_buf->str_code(rc);
740 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000741 return 0;
742 }
743
744 // Receive samples from the usrp until we have enough
745 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400746 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000747 (void*)pkt_buf,
748 rx_spp,
749 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400750 0.1,
751 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752 rx_pkt_cnt++;
753
kurtis.heimerld4be0742011-11-26 03:17:46 +0000754 // Check for errors
755 rc = check_rx_md_err(metadata, num_smpls);
756 switch (rc) {
757 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000758 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
759 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000760 exit(-1);
761 case ERROR_TIMING:
762 restart(prev_ts);
763 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000764 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000765 }
766
kurtis.heimerl965e7572011-11-26 03:16:54 +0000767
768 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000769 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000770
771 rc = rx_smpl_buf->write(pkt_buf,
772 num_smpls,
773 metadata.time_spec);
774
775 // Continue on local overrun, exit on other errors
776 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000777 LOG(ERR) << rx_smpl_buf->str_code(rc);
778 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000779 if (rc != smpl_buf::ERROR_OVERFLOW)
780 return 0;
781 }
782 }
783
784 // We have enough samples
785 rc = rx_smpl_buf->read(buf, len, timestamp);
786 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000787 LOG(ERR) << rx_smpl_buf->str_code(rc);
788 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000789 return 0;
790 }
791
792 return len;
793}
794
795int uhd_device::writeSamples(short *buf, int len, bool *underrun,
796 unsigned long long timestamp,bool isControl)
797{
798 uhd::tx_metadata_t metadata;
799 metadata.has_time_spec = true;
800 metadata.start_of_burst = false;
801 metadata.end_of_burst = false;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400802 metadata.time_spec = convert_time(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000803
804 // No control packets
805 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000806 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000807 return 0;
808 }
809
810 // Drop a fixed number of packets (magic value)
811 if (!aligned) {
812 drop_cnt++;
813
814 if (drop_cnt == 1) {
815 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000816 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000817 metadata.end_of_burst = true;
818 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000819 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000820 return len;
821 } else {
822 LOG(DEBUG) << "Aligning transmitter: start burst";
823 metadata.start_of_burst = true;
824 aligned = true;
825 drop_cnt = 0;
826 }
827 }
828
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400829 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000830
ttsoub371ed52012-01-09 18:11:34 +0000831 if (num_smpls != (unsigned) len) {
832 LOG(ALERT) << "UHD: Device send timed out";
833 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
834 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
835 exit(-1);
836 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837
838 return num_smpls;
839}
840
841bool uhd_device::updateAlignment(TIMESTAMP timestamp)
842{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000843 return true;
844}
845
846bool uhd_device::setTxFreq(double wFreq)
847{
848 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000849 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000850 tx_freq = usrp_dev->get_tx_freq();
851
kurtis.heimerl965e7572011-11-26 03:16:54 +0000852 return true;
853}
854
855bool uhd_device::setRxFreq(double wFreq)
856{
857 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000858 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000859 rx_freq = usrp_dev->get_rx_freq();
860
kurtis.heimerl965e7572011-11-26 03:16:54 +0000861 return true;
862}
863
864bool uhd_device::recv_async_msg()
865{
ttsou60dc4c92012-08-08 23:30:23 +0000866 uhd::async_metadata_t md;
867 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868 return false;
869
870 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000871 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000872 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000873
874 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
875 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
876 LOG(ERR) << str_code(md);
877 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000878 }
879
880 return true;
881}
882
883std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
884{
885 std::ostringstream ost("UHD: ");
886
887 switch (metadata.error_code) {
888 case uhd::rx_metadata_t::ERROR_CODE_NONE:
889 ost << "No error";
890 break;
891 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
892 ost << "No packet received, implementation timed-out";
893 break;
894 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
895 ost << "A stream command was issued in the past";
896 break;
897 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
898 ost << "Expected another stream command";
899 break;
900 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
901 ost << "An internal receive buffer has filled";
902 break;
903 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
904 ost << "The packet could not be parsed";
905 break;
906 default:
907 ost << "Unknown error " << metadata.error_code;
908 }
909
910 if (metadata.has_time_spec)
911 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
912
913 return ost.str();
914}
915
916std::string uhd_device::str_code(uhd::async_metadata_t metadata)
917{
918 std::ostringstream ost("UHD: ");
919
920 switch (metadata.event_code) {
921 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
922 ost << "A packet was successfully transmitted";
923 break;
924 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
925 ost << "An internal send buffer has emptied";
926 break;
927 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
928 ost << "Packet loss between host and device";
929 break;
930 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
931 ost << "Packet time was too late or too early";
932 break;
933 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
934 ost << "Underflow occurred inside a packet";
935 break;
936 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
937 ost << "Packet loss within a burst";
938 break;
939 default:
940 ost << "Unknown error " << metadata.event_code;
941 }
942
943 if (metadata.has_time_spec)
944 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
945
946 return ost.str();
947}
948
949smpl_buf::smpl_buf(size_t len, double rate)
950 : buf_len(len), clk_rt(rate),
951 time_start(0), time_end(0), data_start(0), data_end(0)
952{
953 data = new uint32_t[len];
954}
955
956smpl_buf::~smpl_buf()
957{
958 delete[] data;
959}
960
961ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
962{
963 if (timestamp < time_start)
964 return ERROR_TIMESTAMP;
965 else if (timestamp >= time_end)
966 return 0;
967 else
968 return time_end - timestamp;
969}
970
971ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
972{
973 return avail_smpls(convert_time(timespec, clk_rt));
974}
975
976ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
977{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000978 int type_sz = 2 * sizeof(short);
979
kurtis.heimerl965e7572011-11-26 03:16:54 +0000980 // Check for valid read
981 if (timestamp < time_start)
982 return ERROR_TIMESTAMP;
983 if (timestamp >= time_end)
984 return 0;
985 if (len >= buf_len)
986 return ERROR_READ;
987
988 // How many samples should be copied
989 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000990 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000991 num_smpls = len;
992
993 // Starting index
994 size_t read_start = data_start + (timestamp - time_start);
995
996 // Read it
997 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000998 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000999 memcpy(buf, data + read_start, numBytes);
1000 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001001 size_t first_cp = (buf_len - read_start) * type_sz;
1002 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001003
1004 memcpy(buf, data + read_start, first_cp);
1005 memcpy((char*) buf + first_cp, data, second_cp);
1006 }
1007
1008 data_start = (read_start + len) % buf_len;
1009 time_start = timestamp + len;
1010
1011 if (time_start > time_end)
1012 return ERROR_READ;
1013 else
1014 return num_smpls;
1015}
1016
1017ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1018{
1019 return read(buf, len, convert_time(ts, clk_rt));
1020}
1021
1022ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1023{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001024 int type_sz = 2 * sizeof(short);
1025
kurtis.heimerl965e7572011-11-26 03:16:54 +00001026 // Check for valid write
1027 if ((len == 0) || (len >= buf_len))
1028 return ERROR_WRITE;
1029 if ((timestamp + len) <= time_end)
1030 return ERROR_TIMESTAMP;
1031
1032 // Starting index
1033 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1034
1035 // Write it
1036 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001037 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001038 memcpy(data + write_start, buf, numBytes);
1039 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001040 size_t first_cp = (buf_len - write_start) * type_sz;
1041 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001042
1043 memcpy(data + write_start, buf, first_cp);
1044 memcpy(data, (char*) buf + first_cp, second_cp);
1045 }
1046
1047 data_end = (write_start + len) % buf_len;
1048 time_end = timestamp + len;
1049
1050 if (((write_start + len) > buf_len) && (data_end > data_start))
1051 return ERROR_OVERFLOW;
1052 else if (time_end <= time_start)
1053 return ERROR_WRITE;
1054 else
1055 return len;
1056}
1057
1058ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1059{
1060 return write(buf, len, convert_time(ts, clk_rt));
1061}
1062
1063std::string smpl_buf::str_status() const
1064{
1065 std::ostringstream ost("Sample buffer: ");
1066
1067 ost << "length = " << buf_len;
1068 ost << ", time_start = " << time_start;
1069 ost << ", time_end = " << time_end;
1070 ost << ", data_start = " << data_start;
1071 ost << ", data_end = " << data_end;
1072
1073 return ost.str();
1074}
1075
1076std::string smpl_buf::str_code(ssize_t code)
1077{
1078 switch (code) {
1079 case ERROR_TIMESTAMP:
1080 return "Sample buffer: Requested timestamp is not valid";
1081 case ERROR_READ:
1082 return "Sample buffer: Read error";
1083 case ERROR_WRITE:
1084 return "Sample buffer: Write error";
1085 case ERROR_OVERFLOW:
1086 return "Sample buffer: Overrun";
1087 default:
1088 return "Sample buffer: Unknown error";
1089 }
1090}
1091
Thomas Tsoucb69f082013-04-08 14:18:26 -04001092RadioDevice *RadioDevice::make(int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001093{
Thomas Tsoucb69f082013-04-08 14:18:26 -04001094 return new uhd_device(sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001095}