blob: a627a64151ce65b04cc7ee6624e5be9d1ad46fe2 [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 Tsoucb69f082013-04-08 14:18:26 -040035#define B100_CLK_RT 52e6
36#define B100_BASE_RT GSMRATE
37#define USRP2_BASE_RT 400e3
Thomas Tsoue3e88142013-04-05 20:42:41 -040038#define TX_AMPL 0.3
39#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040040
41enum uhd_dev_type {
42 USRP1,
43 USRP2,
44 B100,
45 NUM_USRP_TYPES,
46};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000047
Thomas Tsoue3e88142013-04-05 20:42:41 -040048struct uhd_dev_offset {
49 enum uhd_dev_type type;
50 int sps;
51 double offset;
52};
53
kurtis.heimerl965e7572011-11-26 03:16:54 +000054/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040055 * Tx / Rx sample offset values. In a perfect world, there is no group delay
56 * though analog components, and behaviour through digital filters exactly
57 * matches calculated values. In reality, there are unaccounted factors,
58 * which are captured in these empirically measured (using a loopback test)
59 * timing correction values.
60 *
61 * Notes:
62 * USRP1 with timestamps is not supported by UHD.
63 */
64static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 3] = {
65 { USRP1, 1, 0.0 },
66 { USRP1, 2, 0.0 },
67 { USRP1, 4, 0.0 },
68 { USRP2, 1, 5.4394e-5 },
69 { USRP2, 2, 0.0 },
70 { USRP2, 4, 0.0 },
71 { B100, 1, 9.4778e-5 },
72 { B100, 2, 5.1100e-5 },
73 { B100, 4, 2.9418e-5 },
74};
kurtis.heimerl965e7572011-11-26 03:16:54 +000075
Thomas Tsoue3e88142013-04-05 20:42:41 -040076static double get_dev_offset(enum uhd_dev_type type, int sps)
77{
78 if (type == USRP1) {
79 LOG(ERR) << "Invalid device type";
80 return 0.0;
81 }
kurtis.heimerl965e7572011-11-26 03:16:54 +000082
Thomas Tsoue3e88142013-04-05 20:42:41 -040083 switch (sps) {
84 case 1:
85 return uhd_offsets[3 * type + 0].offset;
86 case 2:
87 return uhd_offsets[3 * type + 1].offset;
88 case 4:
89 return uhd_offsets[3 * type + 2].offset;
90 }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000091
Thomas Tsoue3e88142013-04-05 20:42:41 -040092 LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
93 return 0.0;
94}
kurtis.heimerlce317332011-11-26 03:18:39 +000095
Thomas Tsoucb69f082013-04-08 14:18:26 -040096/*
97 * Select sample rate based on device type and requested samples-per-symbol.
98 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
99 * usable channel spacing of 400 kHz.
100 */
101static double select_rate(uhd_dev_type type, int sps)
102{
103 if ((sps != 4) && (sps != 2) && (sps != 1))
104 return -9999.99;
105
106 switch (type) {
107 case USRP2:
108 return USRP2_BASE_RT * sps;
109 break;
110 case B100:
111 return B100_BASE_RT * sps;
112 break;
113 }
114
115 LOG(ALERT) << "Unknown device type " << type;
116 return -9999.99;
117}
118
kurtis.heimerl965e7572011-11-26 03:16:54 +0000119/** Timestamp conversion
120 @param timestamp a UHD or OpenBTS timestamp
121 @param rate sample rate
122 @return the converted timestamp
123*/
124uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
125{
126 double secs = (double) ticks / rate;
127 return uhd::time_spec_t(secs);
128}
129
130TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
131{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000132 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000133 return ts.get_tick_count(rate) + ticks;
134}
135
136/*
137 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
138 or UHD style timestamps. Time conversions are handled
139 internally or accessable through the static convert calls.
140*/
141class smpl_buf {
142public:
143 /** Sample buffer constructor
144 @param len number of 32-bit samples the buffer should hold
145 @param rate sample clockrate
146 @param timestamp
147 */
148 smpl_buf(size_t len, double rate);
149 ~smpl_buf();
150
151 /** Query number of samples available for reading
152 @param timestamp time of first sample
153 @return number of available samples or error
154 */
155 ssize_t avail_smpls(TIMESTAMP timestamp) const;
156 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
157
158 /** Read and write
159 @param buf pointer to buffer
160 @param len number of samples desired to read or write
161 @param timestamp time of first stample
162 @return number of actual samples read or written or error
163 */
164 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
165 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
166 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
167 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
168
169 /** Buffer status string
170 @return a formatted string describing internal buffer state
171 */
172 std::string str_status() const;
173
174 /** Formatted error string
175 @param code an error code
176 @return a formatted error string
177 */
178 static std::string str_code(ssize_t code);
179
180 enum err_code {
181 ERROR_TIMESTAMP = -1,
182 ERROR_READ = -2,
183 ERROR_WRITE = -3,
184 ERROR_OVERFLOW = -4
185 };
186
187private:
188 uint32_t *data;
189 size_t buf_len;
190
191 double clk_rt;
192
193 TIMESTAMP time_start;
194 TIMESTAMP time_end;
195
196 size_t data_start;
197 size_t data_end;
198};
199
200/*
201 uhd_device - UHD implementation of the Device interface. Timestamped samples
202 are sent to and received from the device. An intermediate buffer
203 on the receive side collects and aligns packets of samples.
204 Events and errors such as underruns are reported asynchronously
205 by the device and received in a separate thread.
206*/
207class uhd_device : public RadioDevice {
208public:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400209 uhd_device(int sps, bool skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000210 ~uhd_device();
211
Thomas Tsoucb69f082013-04-08 14:18:26 -0400212 int open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000213 bool start();
214 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000215 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000216 void setPriority();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400217 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000218
219 int readSamples(short *buf, int len, bool *overrun,
220 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
221
222 int writeSamples(short *buf, int len, bool *underrun,
223 TIMESTAMP timestamp, bool isControl);
224
225 bool updateAlignment(TIMESTAMP timestamp);
226
227 bool setTxFreq(double wFreq);
228 bool setRxFreq(double wFreq);
229
230 inline TIMESTAMP initialWriteTimestamp() { return 0; }
231 inline TIMESTAMP initialReadTimestamp() { return 0; }
232
Thomas Tsoue3e88142013-04-05 20:42:41 -0400233 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000234 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000235
kurtis.heimerl02d04052011-11-26 03:17:10 +0000236 double setRxGain(double db);
237 double getRxGain(void) { return rx_gain; }
238 double maxRxGain(void) { return rx_gain_max; }
239 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000240
kurtis.heimerl02d04052011-11-26 03:17:10 +0000241 double setTxGain(double db);
242 double maxTxGain(void) { return tx_gain_max; }
243 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000244
kurtis.heimerl02d04052011-11-26 03:17:10 +0000245 double getTxFreq() { return tx_freq; }
246 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000247
248 inline double getSampleRate() { return actual_smpl_rt; }
249 inline double numberRead() { return rx_pkt_cnt; }
250 inline double numberWritten() { return 0; }
251
252 /** Receive and process asynchronous message
253 @return true if message received or false on timeout or error
254 */
255 bool recv_async_msg();
256
kurtis.heimerld4be0742011-11-26 03:17:46 +0000257 enum err_code {
258 ERROR_TIMING = -1,
259 ERROR_UNRECOVERABLE = -2,
260 ERROR_UNHANDLED = -3,
261 };
262
kurtis.heimerl965e7572011-11-26 03:16:54 +0000263private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000264 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400265 uhd::tx_streamer::sptr tx_stream;
266 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400267 enum TxWindowType tx_window;
268 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000269
Thomas Tsoue3e88142013-04-05 20:42:41 -0400270 int sps;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000271 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000272
kurtis.heimerl02d04052011-11-26 03:17:10 +0000273 double tx_gain, tx_gain_min, tx_gain_max;
274 double rx_gain, rx_gain_min, rx_gain_max;
275
276 double tx_freq, rx_freq;
277 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000278
279 bool started;
280 bool aligned;
281 bool skip_rx;
282
283 size_t rx_pkt_cnt;
284 size_t drop_cnt;
285 uhd::time_spec_t prev_ts;
286
287 TIMESTAMP ts_offset;
288 smpl_buf *rx_smpl_buf;
289
kurtis.heimerl02d04052011-11-26 03:17:10 +0000290 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000291 void set_ref_clk(bool ext_clk);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400292 int set_master_clk(double rate);
293 int set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000294 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000295 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000296 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000297
kurtis.heimerl965e7572011-11-26 03:16:54 +0000298 std::string str_code(uhd::rx_metadata_t metadata);
299 std::string str_code(uhd::async_metadata_t metadata);
300
301 Thread async_event_thrd;
302};
303
304void *async_event_loop(uhd_device *dev)
305{
306 while (1) {
307 dev->recv_async_msg();
308 pthread_testcancel();
309 }
310}
311
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000312/*
313 Catch and drop underrun 'U' and overrun 'O' messages from stdout
314 since we already report using the logging facility. Direct
315 everything else appropriately.
316 */
317void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
318{
319 switch (type) {
320 case uhd::msg::status:
321 LOG(INFO) << msg;
322 break;
323 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000324 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000325 break;
326 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000327 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000328 break;
329 case uhd::msg::fastpath:
330 break;
331 }
332}
333
Thomas Tsoucb69f082013-04-08 14:18:26 -0400334uhd_device::uhd_device(int sps, bool skip_rx)
335 : tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
kurtis.heimerl02d04052011-11-26 03:17:10 +0000336 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
337 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000338 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000339 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
340{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400341 this->sps = sps;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342 this->skip_rx = skip_rx;
343}
344
345uhd_device::~uhd_device()
346{
347 stop();
348
349 if (rx_smpl_buf)
350 delete rx_smpl_buf;
351}
352
kurtis.heimerl24481de2011-11-26 03:17:18 +0000353void uhd_device::init_gains()
354{
355 uhd::gain_range_t range;
356
357 range = usrp_dev->get_tx_gain_range();
358 tx_gain_min = range.start();
359 tx_gain_max = range.stop();
360
361 range = usrp_dev->get_rx_gain_range();
362 rx_gain_min = range.start();
363 rx_gain_max = range.stop();
364
365 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
366 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
367
368 tx_gain = usrp_dev->get_tx_gain();
369 rx_gain = usrp_dev->get_rx_gain();
370
371 return;
372}
373
374void uhd_device::set_ref_clk(bool ext_clk)
375{
kurtis.heimerl24481de2011-11-26 03:17:18 +0000376 if (ext_clk)
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400377 usrp_dev->set_clock_source("external");
kurtis.heimerl24481de2011-11-26 03:17:18 +0000378
379 return;
380}
381
Thomas Tsou02d88d12013-04-05 15:36:30 -0400382int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000383{
Thomas Tsou02d88d12013-04-05 15:36:30 -0400384 double actual_clk_rt;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000385
Thomas Tsou7e068062013-04-08 19:39:37 -0400386 try {
387 usrp_dev->set_master_clock_rate(clk_rate);
388 actual_clk_rt = usrp_dev->get_master_clock_rate();
389 } catch (const std::exception &ex) {
390 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
391 LOG(ALERT) << ex.what();
392 return -1;
393 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000394
Thomas Tsou02d88d12013-04-05 15:36:30 -0400395 if (actual_clk_rt != clk_rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000396 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400397 LOG(ALERT) << "Requested clock rate " << clk_rate;
kurtis.heimerle3320322011-11-28 06:26:08 +0000398 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400399 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000400 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400401
402 return 0;
403}
404
405int uhd_device::set_rates(double rate)
406{
Thomas Tsoucb69f082013-04-08 14:18:26 -0400407 double offset_limit = 10.0;
408 double tx_offset, rx_offset;
409
Thomas Tsou02d88d12013-04-05 15:36:30 -0400410 // B100 is the only device where we set FPGA clocking
411 if (dev_type == B100) {
412 if (set_master_clk(B100_CLK_RT) < 0)
413 return -1;
414 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000415
416 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400417 try {
418 usrp_dev->set_tx_rate(rate);
419 usrp_dev->set_rx_rate(rate);
420 } catch (const std::exception &ex) {
421 LOG(ALERT) << "UHD rate setting failed: " << rate;
422 LOG(ALERT) << ex.what();
423 return -1;
424 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400425 actual_smpl_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000426
Thomas Tsoucb69f082013-04-08 14:18:26 -0400427 tx_offset = actual_smpl_rt - rate;
428 rx_offset = usrp_dev->get_rx_rate() - rate;
429 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000430 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400431 LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/"
432 << usrp_dev->get_rx_rate() << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400433 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000434 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000435
Thomas Tsou02d88d12013-04-05 15:36:30 -0400436 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000437}
438
kurtis.heimerl02d04052011-11-26 03:17:10 +0000439double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000440{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000441 usrp_dev->set_tx_gain(db);
442 tx_gain = usrp_dev->get_tx_gain();
443
444 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
445
446 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000447}
448
kurtis.heimerl02d04052011-11-26 03:17:10 +0000449double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000450{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000451 usrp_dev->set_rx_gain(db);
452 rx_gain = usrp_dev->get_rx_gain();
453
454 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
455
456 return rx_gain;
457}
458
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000459/*
460 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400461 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000462 deal with the transport latency. Reject the USRP1 because UHD doesn't
463 support timestamped samples with it.
464 */
465bool uhd_device::parse_dev_type()
466{
467 std::string mboard_str, dev_str;
468 uhd::property_tree::sptr prop_tree;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400469 size_t usrp1_str, usrp2_str, b100_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000470
471 prop_tree = usrp_dev->get_device()->get_tree();
472 dev_str = prop_tree->access<std::string>("/name").get();
473 mboard_str = usrp_dev->get_mboard_name();
474
475 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400476 usrp2_str = dev_str.find("USRP2");
477 b100_str = mboard_str.find("B100");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000478
479 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000480 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
481 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400482 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000483 return false;
484 }
485
Thomas Tsoucb69f082013-04-08 14:18:26 -0400486 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400487 tx_window = TX_WINDOW_USRP1;
488 LOG(INFO) << "Using USRP1 type transmit window for "
489 << dev_str << " " << mboard_str;
490 dev_type = B100;
491 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400492 } else if (usrp2_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400493 dev_type = USRP2;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400494 } else {
495 LOG(ALERT) << "Unknown UHD device type";
496 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000497 }
498
Thomas Tsou02d88d12013-04-05 15:36:30 -0400499 tx_window = TX_WINDOW_FIXED;
500 LOG(INFO) << "Using fixed transmit window for "
501 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000502 return true;
503}
504
Thomas Tsoucb69f082013-04-08 14:18:26 -0400505int uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000506{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000507 // Register msg handler
508 uhd::msg::register_handler(&uhd_msg_handler);
509
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000510 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000511 uhd::device_addr_t addr(args);
512 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000513 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000514 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400515 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000516 }
517
518 // Use the first found device
519 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000520 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000521 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000522 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000523 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400524 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000525 }
526
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000527 // Check for a valid device type and set bus type
528 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400529 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000530
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000531#ifdef EXTREF
532 set_ref_clk(true);
533#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400534 // Create TX and RX streamers
535 uhd::stream_args_t stream_args("sc16");
536 tx_stream = usrp_dev->get_tx_stream(stream_args);
537 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000538
kurtis.heimerl965e7572011-11-26 03:16:54 +0000539 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400540 tx_spp = tx_stream->get_max_num_samps();
541 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000542
543 // Set rates
Thomas Tsoucb69f082013-04-08 14:18:26 -0400544 desired_smpl_rt = select_rate(dev_type, sps);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400545 if (set_rates(desired_smpl_rt) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400546 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000547
548 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400549 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000550 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
551
552 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400553 double offset = get_dev_offset(dev_type, sps);
554 if (offset == 0.0) {
555 LOG(ERR) << "Unsupported configuration, no correction applied";
556 ts_offset = 0;
557 } else {
558 ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
559 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000560
kurtis.heimerl02d04052011-11-26 03:17:10 +0000561 // Initialize and shadow gain values
562 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000563
kurtis.heimerl965e7572011-11-26 03:16:54 +0000564 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000565 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000566
Thomas Tsoucb69f082013-04-08 14:18:26 -0400567 if (dev_type == USRP2)
568 return RESAMP;
569
570 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000571}
572
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000573bool uhd_device::flush_recv(size_t num_pkts)
574{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000575 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000576 size_t num_smpls;
577 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000578 float timeout;
579
580 // Use .01 sec instead of the default .1 sec
581 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000582
583 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400584 num_smpls = rx_stream->recv(buff, rx_spp, md,
585 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000586 if (!num_smpls) {
587 switch (md.error_code) {
588 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
589 return true;
590 default:
591 continue;
592 }
593 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000594 }
595
596 return true;
597}
598
kurtis.heimerl68292102011-11-26 03:17:28 +0000599void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000600{
kurtis.heimerl68292102011-11-26 03:17:28 +0000601 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
602 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000603
kurtis.heimerl68292102011-11-26 03:17:28 +0000604 flush_recv(50);
605
606 usrp_dev->set_time_now(ts);
607 aligned = false;
608
609 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
610 cmd.stream_now = true;
611 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000612}
613
kurtis.heimerl965e7572011-11-26 03:16:54 +0000614bool uhd_device::start()
615{
616 LOG(INFO) << "Starting USRP...";
617
618 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000619 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000620 return false;
621 }
622
623 setPriority();
624
625 // Start asynchronous event (underrun check) loop
626 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
627
628 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000629 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000630
kurtis.heimerl965e7572011-11-26 03:16:54 +0000631 // Display usrp time
632 double time_now = usrp_dev->get_time_now().get_real_secs();
633 LOG(INFO) << "The current time is " << time_now << " seconds";
634
635 started = true;
636 return true;
637}
638
639bool uhd_device::stop()
640{
641 uhd::stream_cmd_t stream_cmd =
642 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
643
644 usrp_dev->issue_stream_cmd(stream_cmd);
645
646 started = false;
647 return true;
648}
649
650void uhd_device::setPriority()
651{
652 uhd::set_thread_priority_safe();
653 return;
654}
655
kurtis.heimerld4be0742011-11-26 03:17:46 +0000656int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000657{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000658 uhd::time_spec_t ts;
659
kurtis.heimerld4be0742011-11-26 03:17:46 +0000660 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000661 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000662
663 switch (md.error_code) {
664 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000665 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000666 return ERROR_UNRECOVERABLE;
667 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
668 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
669 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
670 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
671 default:
672 return ERROR_UNHANDLED;
673 }
674 }
675
kurtis.heimerl965e7572011-11-26 03:16:54 +0000676 // Missing timestamp
677 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000678 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000679 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000680 }
681
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000682 ts = md.time_spec;
683
kurtis.heimerl965e7572011-11-26 03:16:54 +0000684 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000685 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000686 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000687 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
688 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000689 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000690 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000691 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000692 }
693
694 return 0;
695}
696
697int uhd_device::readSamples(short *buf, int len, bool *overrun,
698 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
699{
700 ssize_t rc;
701 uhd::time_spec_t ts;
702 uhd::rx_metadata_t metadata;
703 uint32_t pkt_buf[rx_spp];
704
705 if (skip_rx)
706 return 0;
707
708 // Shift read time with respect to transmit clock
709 timestamp += ts_offset;
710
711 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000712 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000713
714 // Check that timestamp is valid
715 rc = rx_smpl_buf->avail_smpls(timestamp);
716 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000717 LOG(ERR) << rx_smpl_buf->str_code(rc);
718 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000719 return 0;
720 }
721
722 // Receive samples from the usrp until we have enough
723 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400724 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000725 (void*)pkt_buf,
726 rx_spp,
727 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400728 0.1,
729 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730 rx_pkt_cnt++;
731
kurtis.heimerld4be0742011-11-26 03:17:46 +0000732 // Check for errors
733 rc = check_rx_md_err(metadata, num_smpls);
734 switch (rc) {
735 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000736 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
737 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000738 exit(-1);
739 case ERROR_TIMING:
740 restart(prev_ts);
741 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000742 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000743 }
744
kurtis.heimerl965e7572011-11-26 03:16:54 +0000745
746 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000747 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000748
749 rc = rx_smpl_buf->write(pkt_buf,
750 num_smpls,
751 metadata.time_spec);
752
753 // Continue on local overrun, exit on other errors
754 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000755 LOG(ERR) << rx_smpl_buf->str_code(rc);
756 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000757 if (rc != smpl_buf::ERROR_OVERFLOW)
758 return 0;
759 }
760 }
761
762 // We have enough samples
763 rc = rx_smpl_buf->read(buf, len, timestamp);
764 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000765 LOG(ERR) << rx_smpl_buf->str_code(rc);
766 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000767 return 0;
768 }
769
770 return len;
771}
772
773int uhd_device::writeSamples(short *buf, int len, bool *underrun,
774 unsigned long long timestamp,bool isControl)
775{
776 uhd::tx_metadata_t metadata;
777 metadata.has_time_spec = true;
778 metadata.start_of_burst = false;
779 metadata.end_of_burst = false;
780 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
781
782 // No control packets
783 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000784 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000785 return 0;
786 }
787
788 // Drop a fixed number of packets (magic value)
789 if (!aligned) {
790 drop_cnt++;
791
792 if (drop_cnt == 1) {
793 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000794 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000795 metadata.end_of_burst = true;
796 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000797 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000798 return len;
799 } else {
800 LOG(DEBUG) << "Aligning transmitter: start burst";
801 metadata.start_of_burst = true;
802 aligned = true;
803 drop_cnt = 0;
804 }
805 }
806
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400807 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000808
ttsoub371ed52012-01-09 18:11:34 +0000809 if (num_smpls != (unsigned) len) {
810 LOG(ALERT) << "UHD: Device send timed out";
811 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
812 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
813 exit(-1);
814 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000815
816 return num_smpls;
817}
818
819bool uhd_device::updateAlignment(TIMESTAMP timestamp)
820{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000821 return true;
822}
823
824bool uhd_device::setTxFreq(double wFreq)
825{
826 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000827 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000828 tx_freq = usrp_dev->get_tx_freq();
829
kurtis.heimerl965e7572011-11-26 03:16:54 +0000830 return true;
831}
832
833bool uhd_device::setRxFreq(double wFreq)
834{
835 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000836 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000837 rx_freq = usrp_dev->get_rx_freq();
838
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839 return true;
840}
841
842bool uhd_device::recv_async_msg()
843{
ttsou60dc4c92012-08-08 23:30:23 +0000844 uhd::async_metadata_t md;
845 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000846 return false;
847
848 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000849 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000850 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000851
852 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
853 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
854 LOG(ERR) << str_code(md);
855 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000856 }
857
858 return true;
859}
860
861std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
862{
863 std::ostringstream ost("UHD: ");
864
865 switch (metadata.error_code) {
866 case uhd::rx_metadata_t::ERROR_CODE_NONE:
867 ost << "No error";
868 break;
869 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
870 ost << "No packet received, implementation timed-out";
871 break;
872 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
873 ost << "A stream command was issued in the past";
874 break;
875 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
876 ost << "Expected another stream command";
877 break;
878 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
879 ost << "An internal receive buffer has filled";
880 break;
881 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
882 ost << "The packet could not be parsed";
883 break;
884 default:
885 ost << "Unknown error " << metadata.error_code;
886 }
887
888 if (metadata.has_time_spec)
889 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
890
891 return ost.str();
892}
893
894std::string uhd_device::str_code(uhd::async_metadata_t metadata)
895{
896 std::ostringstream ost("UHD: ");
897
898 switch (metadata.event_code) {
899 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
900 ost << "A packet was successfully transmitted";
901 break;
902 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
903 ost << "An internal send buffer has emptied";
904 break;
905 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
906 ost << "Packet loss between host and device";
907 break;
908 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
909 ost << "Packet time was too late or too early";
910 break;
911 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
912 ost << "Underflow occurred inside a packet";
913 break;
914 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
915 ost << "Packet loss within a burst";
916 break;
917 default:
918 ost << "Unknown error " << metadata.event_code;
919 }
920
921 if (metadata.has_time_spec)
922 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
923
924 return ost.str();
925}
926
927smpl_buf::smpl_buf(size_t len, double rate)
928 : buf_len(len), clk_rt(rate),
929 time_start(0), time_end(0), data_start(0), data_end(0)
930{
931 data = new uint32_t[len];
932}
933
934smpl_buf::~smpl_buf()
935{
936 delete[] data;
937}
938
939ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
940{
941 if (timestamp < time_start)
942 return ERROR_TIMESTAMP;
943 else if (timestamp >= time_end)
944 return 0;
945 else
946 return time_end - timestamp;
947}
948
949ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
950{
951 return avail_smpls(convert_time(timespec, clk_rt));
952}
953
954ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
955{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000956 int type_sz = 2 * sizeof(short);
957
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958 // Check for valid read
959 if (timestamp < time_start)
960 return ERROR_TIMESTAMP;
961 if (timestamp >= time_end)
962 return 0;
963 if (len >= buf_len)
964 return ERROR_READ;
965
966 // How many samples should be copied
967 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000968 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969 num_smpls = len;
970
971 // Starting index
972 size_t read_start = data_start + (timestamp - time_start);
973
974 // Read it
975 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000976 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000977 memcpy(buf, data + read_start, numBytes);
978 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000979 size_t first_cp = (buf_len - read_start) * type_sz;
980 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000981
982 memcpy(buf, data + read_start, first_cp);
983 memcpy((char*) buf + first_cp, data, second_cp);
984 }
985
986 data_start = (read_start + len) % buf_len;
987 time_start = timestamp + len;
988
989 if (time_start > time_end)
990 return ERROR_READ;
991 else
992 return num_smpls;
993}
994
995ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
996{
997 return read(buf, len, convert_time(ts, clk_rt));
998}
999
1000ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1001{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001002 int type_sz = 2 * sizeof(short);
1003
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 // Check for valid write
1005 if ((len == 0) || (len >= buf_len))
1006 return ERROR_WRITE;
1007 if ((timestamp + len) <= time_end)
1008 return ERROR_TIMESTAMP;
1009
1010 // Starting index
1011 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1012
1013 // Write it
1014 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001015 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001016 memcpy(data + write_start, buf, numBytes);
1017 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001018 size_t first_cp = (buf_len - write_start) * type_sz;
1019 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001020
1021 memcpy(data + write_start, buf, first_cp);
1022 memcpy(data, (char*) buf + first_cp, second_cp);
1023 }
1024
1025 data_end = (write_start + len) % buf_len;
1026 time_end = timestamp + len;
1027
1028 if (((write_start + len) > buf_len) && (data_end > data_start))
1029 return ERROR_OVERFLOW;
1030 else if (time_end <= time_start)
1031 return ERROR_WRITE;
1032 else
1033 return len;
1034}
1035
1036ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1037{
1038 return write(buf, len, convert_time(ts, clk_rt));
1039}
1040
1041std::string smpl_buf::str_status() const
1042{
1043 std::ostringstream ost("Sample buffer: ");
1044
1045 ost << "length = " << buf_len;
1046 ost << ", time_start = " << time_start;
1047 ost << ", time_end = " << time_end;
1048 ost << ", data_start = " << data_start;
1049 ost << ", data_end = " << data_end;
1050
1051 return ost.str();
1052}
1053
1054std::string smpl_buf::str_code(ssize_t code)
1055{
1056 switch (code) {
1057 case ERROR_TIMESTAMP:
1058 return "Sample buffer: Requested timestamp is not valid";
1059 case ERROR_READ:
1060 return "Sample buffer: Read error";
1061 case ERROR_WRITE:
1062 return "Sample buffer: Write error";
1063 case ERROR_OVERFLOW:
1064 return "Sample buffer: Overrun";
1065 default:
1066 return "Sample buffer: Unknown error";
1067 }
1068}
1069
Thomas Tsoucb69f082013-04-08 14:18:26 -04001070RadioDevice *RadioDevice::make(int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001071{
Thomas Tsoucb69f082013-04-08 14:18:26 -04001072 return new uhd_device(sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001073}