blob: 575cb0179302261b6bdf0d587d5f7c6bed92d64e [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
kurtis.heimerld3e25902011-11-26 03:18:13 +0000386 // Set master clock rate
Thomas Tsou02d88d12013-04-05 15:36:30 -0400387 usrp_dev->set_master_clock_rate(clk_rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000388 actual_clk_rt = usrp_dev->get_master_clock_rate();
389
Thomas Tsou02d88d12013-04-05 15:36:30 -0400390 if (actual_clk_rt != clk_rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000391 LOG(ALERT) << "Failed to set master clock rate";
392 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400393 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000394 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400395
396 return 0;
397}
398
399int uhd_device::set_rates(double rate)
400{
Thomas Tsoucb69f082013-04-08 14:18:26 -0400401 double offset_limit = 10.0;
402 double tx_offset, rx_offset;
403
Thomas Tsou02d88d12013-04-05 15:36:30 -0400404 // B100 is the only device where we set FPGA clocking
405 if (dev_type == B100) {
406 if (set_master_clk(B100_CLK_RT) < 0)
407 return -1;
408 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000409
410 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000411 usrp_dev->set_tx_rate(rate);
412 usrp_dev->set_rx_rate(rate);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400413 actual_smpl_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000414
Thomas Tsoucb69f082013-04-08 14:18:26 -0400415 tx_offset = actual_smpl_rt - rate;
416 rx_offset = usrp_dev->get_rx_rate() - rate;
417 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000418 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400419 LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/"
420 << usrp_dev->get_rx_rate() << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400421 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000422 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000423
Thomas Tsou02d88d12013-04-05 15:36:30 -0400424 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000425}
426
kurtis.heimerl02d04052011-11-26 03:17:10 +0000427double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000428{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000429 usrp_dev->set_tx_gain(db);
430 tx_gain = usrp_dev->get_tx_gain();
431
432 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
433
434 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000435}
436
kurtis.heimerl02d04052011-11-26 03:17:10 +0000437double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000438{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000439 usrp_dev->set_rx_gain(db);
440 rx_gain = usrp_dev->get_rx_gain();
441
442 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
443
444 return rx_gain;
445}
446
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000447/*
448 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400449 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000450 deal with the transport latency. Reject the USRP1 because UHD doesn't
451 support timestamped samples with it.
452 */
453bool uhd_device::parse_dev_type()
454{
455 std::string mboard_str, dev_str;
456 uhd::property_tree::sptr prop_tree;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400457 size_t usrp1_str, usrp2_str, b100_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000458
459 prop_tree = usrp_dev->get_device()->get_tree();
460 dev_str = prop_tree->access<std::string>("/name").get();
461 mboard_str = usrp_dev->get_mboard_name();
462
463 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400464 usrp2_str = dev_str.find("USRP2");
465 b100_str = mboard_str.find("B100");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000466
467 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000468 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
469 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400470 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000471 return false;
472 }
473
Thomas Tsoucb69f082013-04-08 14:18:26 -0400474 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400475 tx_window = TX_WINDOW_USRP1;
476 LOG(INFO) << "Using USRP1 type transmit window for "
477 << dev_str << " " << mboard_str;
478 dev_type = B100;
479 return true;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400480 } else if (usrp2_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400481 dev_type = USRP2;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400482 } else {
483 LOG(ALERT) << "Unknown UHD device type";
484 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000485 }
486
Thomas Tsou02d88d12013-04-05 15:36:30 -0400487 tx_window = TX_WINDOW_FIXED;
488 LOG(INFO) << "Using fixed transmit window for "
489 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000490 return true;
491}
492
Thomas Tsoucb69f082013-04-08 14:18:26 -0400493int uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000494{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000495 // Register msg handler
496 uhd::msg::register_handler(&uhd_msg_handler);
497
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000498 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000499 uhd::device_addr_t addr(args);
500 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000501 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000502 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400503 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000504 }
505
506 // Use the first found device
507 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000508 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000509 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000510 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000511 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400512 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000513 }
514
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000515 // Check for a valid device type and set bus type
516 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400517 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000518
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000519#ifdef EXTREF
520 set_ref_clk(true);
521#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400522 // Create TX and RX streamers
523 uhd::stream_args_t stream_args("sc16");
524 tx_stream = usrp_dev->get_tx_stream(stream_args);
525 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000526
kurtis.heimerl965e7572011-11-26 03:16:54 +0000527 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400528 tx_spp = tx_stream->get_max_num_samps();
529 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000530
531 // Set rates
Thomas Tsoucb69f082013-04-08 14:18:26 -0400532 desired_smpl_rt = select_rate(dev_type, sps);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400533 if (set_rates(desired_smpl_rt) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400534 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000535
536 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400537 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000538 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
539
540 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400541 double offset = get_dev_offset(dev_type, sps);
542 if (offset == 0.0) {
543 LOG(ERR) << "Unsupported configuration, no correction applied";
544 ts_offset = 0;
545 } else {
546 ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
547 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000548
kurtis.heimerl02d04052011-11-26 03:17:10 +0000549 // Initialize and shadow gain values
550 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000551
kurtis.heimerl965e7572011-11-26 03:16:54 +0000552 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000553 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000554
Thomas Tsoucb69f082013-04-08 14:18:26 -0400555 if (dev_type == USRP2)
556 return RESAMP;
557
558 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000559}
560
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000561bool uhd_device::flush_recv(size_t num_pkts)
562{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000563 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000564 size_t num_smpls;
565 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000566 float timeout;
567
568 // Use .01 sec instead of the default .1 sec
569 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000570
571 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400572 num_smpls = rx_stream->recv(buff, rx_spp, md,
573 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000574 if (!num_smpls) {
575 switch (md.error_code) {
576 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
577 return true;
578 default:
579 continue;
580 }
581 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000582 }
583
584 return true;
585}
586
kurtis.heimerl68292102011-11-26 03:17:28 +0000587void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000588{
kurtis.heimerl68292102011-11-26 03:17:28 +0000589 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
590 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000591
kurtis.heimerl68292102011-11-26 03:17:28 +0000592 flush_recv(50);
593
594 usrp_dev->set_time_now(ts);
595 aligned = false;
596
597 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
598 cmd.stream_now = true;
599 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000600}
601
kurtis.heimerl965e7572011-11-26 03:16:54 +0000602bool uhd_device::start()
603{
604 LOG(INFO) << "Starting USRP...";
605
606 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000607 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000608 return false;
609 }
610
611 setPriority();
612
613 // Start asynchronous event (underrun check) loop
614 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
615
616 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000617 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000618
kurtis.heimerl965e7572011-11-26 03:16:54 +0000619 // Display usrp time
620 double time_now = usrp_dev->get_time_now().get_real_secs();
621 LOG(INFO) << "The current time is " << time_now << " seconds";
622
623 started = true;
624 return true;
625}
626
627bool uhd_device::stop()
628{
629 uhd::stream_cmd_t stream_cmd =
630 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
631
632 usrp_dev->issue_stream_cmd(stream_cmd);
633
634 started = false;
635 return true;
636}
637
638void uhd_device::setPriority()
639{
640 uhd::set_thread_priority_safe();
641 return;
642}
643
kurtis.heimerld4be0742011-11-26 03:17:46 +0000644int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000645{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000646 uhd::time_spec_t ts;
647
kurtis.heimerld4be0742011-11-26 03:17:46 +0000648 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000649 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000650
651 switch (md.error_code) {
652 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000653 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000654 return ERROR_UNRECOVERABLE;
655 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
656 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
657 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
658 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
659 default:
660 return ERROR_UNHANDLED;
661 }
662 }
663
kurtis.heimerl965e7572011-11-26 03:16:54 +0000664 // Missing timestamp
665 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000666 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000667 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000668 }
669
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000670 ts = md.time_spec;
671
kurtis.heimerl965e7572011-11-26 03:16:54 +0000672 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000673 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000674 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000675 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
676 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000677 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000678 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000679 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000680 }
681
682 return 0;
683}
684
685int uhd_device::readSamples(short *buf, int len, bool *overrun,
686 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
687{
688 ssize_t rc;
689 uhd::time_spec_t ts;
690 uhd::rx_metadata_t metadata;
691 uint32_t pkt_buf[rx_spp];
692
693 if (skip_rx)
694 return 0;
695
696 // Shift read time with respect to transmit clock
697 timestamp += ts_offset;
698
699 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000700 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000701
702 // Check that timestamp is valid
703 rc = rx_smpl_buf->avail_smpls(timestamp);
704 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000705 LOG(ERR) << rx_smpl_buf->str_code(rc);
706 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000707 return 0;
708 }
709
710 // Receive samples from the usrp until we have enough
711 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400712 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000713 (void*)pkt_buf,
714 rx_spp,
715 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400716 0.1,
717 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000718 rx_pkt_cnt++;
719
kurtis.heimerld4be0742011-11-26 03:17:46 +0000720 // Check for errors
721 rc = check_rx_md_err(metadata, num_smpls);
722 switch (rc) {
723 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000724 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
725 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000726 exit(-1);
727 case ERROR_TIMING:
728 restart(prev_ts);
729 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000730 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000731 }
732
kurtis.heimerl965e7572011-11-26 03:16:54 +0000733
734 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000735 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000736
737 rc = rx_smpl_buf->write(pkt_buf,
738 num_smpls,
739 metadata.time_spec);
740
741 // Continue on local overrun, exit on other errors
742 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000743 LOG(ERR) << rx_smpl_buf->str_code(rc);
744 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000745 if (rc != smpl_buf::ERROR_OVERFLOW)
746 return 0;
747 }
748 }
749
750 // We have enough samples
751 rc = rx_smpl_buf->read(buf, len, timestamp);
752 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000753 LOG(ERR) << rx_smpl_buf->str_code(rc);
754 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000755 return 0;
756 }
757
758 return len;
759}
760
761int uhd_device::writeSamples(short *buf, int len, bool *underrun,
762 unsigned long long timestamp,bool isControl)
763{
764 uhd::tx_metadata_t metadata;
765 metadata.has_time_spec = true;
766 metadata.start_of_burst = false;
767 metadata.end_of_burst = false;
768 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
769
770 // No control packets
771 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000772 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000773 return 0;
774 }
775
776 // Drop a fixed number of packets (magic value)
777 if (!aligned) {
778 drop_cnt++;
779
780 if (drop_cnt == 1) {
781 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000782 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000783 metadata.end_of_burst = true;
784 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000785 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000786 return len;
787 } else {
788 LOG(DEBUG) << "Aligning transmitter: start burst";
789 metadata.start_of_burst = true;
790 aligned = true;
791 drop_cnt = 0;
792 }
793 }
794
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400795 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000796
ttsoub371ed52012-01-09 18:11:34 +0000797 if (num_smpls != (unsigned) len) {
798 LOG(ALERT) << "UHD: Device send timed out";
799 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
800 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
801 exit(-1);
802 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000803
804 return num_smpls;
805}
806
807bool uhd_device::updateAlignment(TIMESTAMP timestamp)
808{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000809 return true;
810}
811
812bool uhd_device::setTxFreq(double wFreq)
813{
814 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000815 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000816 tx_freq = usrp_dev->get_tx_freq();
817
kurtis.heimerl965e7572011-11-26 03:16:54 +0000818 return true;
819}
820
821bool uhd_device::setRxFreq(double wFreq)
822{
823 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000824 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000825 rx_freq = usrp_dev->get_rx_freq();
826
kurtis.heimerl965e7572011-11-26 03:16:54 +0000827 return true;
828}
829
830bool uhd_device::recv_async_msg()
831{
ttsou60dc4c92012-08-08 23:30:23 +0000832 uhd::async_metadata_t md;
833 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000834 return false;
835
836 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000837 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000838 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000839
840 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
841 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
842 LOG(ERR) << str_code(md);
843 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000844 }
845
846 return true;
847}
848
849std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
850{
851 std::ostringstream ost("UHD: ");
852
853 switch (metadata.error_code) {
854 case uhd::rx_metadata_t::ERROR_CODE_NONE:
855 ost << "No error";
856 break;
857 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
858 ost << "No packet received, implementation timed-out";
859 break;
860 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
861 ost << "A stream command was issued in the past";
862 break;
863 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
864 ost << "Expected another stream command";
865 break;
866 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
867 ost << "An internal receive buffer has filled";
868 break;
869 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
870 ost << "The packet could not be parsed";
871 break;
872 default:
873 ost << "Unknown error " << metadata.error_code;
874 }
875
876 if (metadata.has_time_spec)
877 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
878
879 return ost.str();
880}
881
882std::string uhd_device::str_code(uhd::async_metadata_t metadata)
883{
884 std::ostringstream ost("UHD: ");
885
886 switch (metadata.event_code) {
887 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
888 ost << "A packet was successfully transmitted";
889 break;
890 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
891 ost << "An internal send buffer has emptied";
892 break;
893 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
894 ost << "Packet loss between host and device";
895 break;
896 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
897 ost << "Packet time was too late or too early";
898 break;
899 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
900 ost << "Underflow occurred inside a packet";
901 break;
902 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
903 ost << "Packet loss within a burst";
904 break;
905 default:
906 ost << "Unknown error " << metadata.event_code;
907 }
908
909 if (metadata.has_time_spec)
910 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
911
912 return ost.str();
913}
914
915smpl_buf::smpl_buf(size_t len, double rate)
916 : buf_len(len), clk_rt(rate),
917 time_start(0), time_end(0), data_start(0), data_end(0)
918{
919 data = new uint32_t[len];
920}
921
922smpl_buf::~smpl_buf()
923{
924 delete[] data;
925}
926
927ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
928{
929 if (timestamp < time_start)
930 return ERROR_TIMESTAMP;
931 else if (timestamp >= time_end)
932 return 0;
933 else
934 return time_end - timestamp;
935}
936
937ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
938{
939 return avail_smpls(convert_time(timespec, clk_rt));
940}
941
942ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
943{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000944 int type_sz = 2 * sizeof(short);
945
kurtis.heimerl965e7572011-11-26 03:16:54 +0000946 // Check for valid read
947 if (timestamp < time_start)
948 return ERROR_TIMESTAMP;
949 if (timestamp >= time_end)
950 return 0;
951 if (len >= buf_len)
952 return ERROR_READ;
953
954 // How many samples should be copied
955 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000956 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000957 num_smpls = len;
958
959 // Starting index
960 size_t read_start = data_start + (timestamp - time_start);
961
962 // Read it
963 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000964 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000965 memcpy(buf, data + read_start, numBytes);
966 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000967 size_t first_cp = (buf_len - read_start) * type_sz;
968 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969
970 memcpy(buf, data + read_start, first_cp);
971 memcpy((char*) buf + first_cp, data, second_cp);
972 }
973
974 data_start = (read_start + len) % buf_len;
975 time_start = timestamp + len;
976
977 if (time_start > time_end)
978 return ERROR_READ;
979 else
980 return num_smpls;
981}
982
983ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
984{
985 return read(buf, len, convert_time(ts, clk_rt));
986}
987
988ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
989{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000990 int type_sz = 2 * sizeof(short);
991
kurtis.heimerl965e7572011-11-26 03:16:54 +0000992 // Check for valid write
993 if ((len == 0) || (len >= buf_len))
994 return ERROR_WRITE;
995 if ((timestamp + len) <= time_end)
996 return ERROR_TIMESTAMP;
997
998 // Starting index
999 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1000
1001 // Write it
1002 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001003 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 memcpy(data + write_start, buf, numBytes);
1005 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001006 size_t first_cp = (buf_len - write_start) * type_sz;
1007 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001008
1009 memcpy(data + write_start, buf, first_cp);
1010 memcpy(data, (char*) buf + first_cp, second_cp);
1011 }
1012
1013 data_end = (write_start + len) % buf_len;
1014 time_end = timestamp + len;
1015
1016 if (((write_start + len) > buf_len) && (data_end > data_start))
1017 return ERROR_OVERFLOW;
1018 else if (time_end <= time_start)
1019 return ERROR_WRITE;
1020 else
1021 return len;
1022}
1023
1024ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1025{
1026 return write(buf, len, convert_time(ts, clk_rt));
1027}
1028
1029std::string smpl_buf::str_status() const
1030{
1031 std::ostringstream ost("Sample buffer: ");
1032
1033 ost << "length = " << buf_len;
1034 ost << ", time_start = " << time_start;
1035 ost << ", time_end = " << time_end;
1036 ost << ", data_start = " << data_start;
1037 ost << ", data_end = " << data_end;
1038
1039 return ost.str();
1040}
1041
1042std::string smpl_buf::str_code(ssize_t code)
1043{
1044 switch (code) {
1045 case ERROR_TIMESTAMP:
1046 return "Sample buffer: Requested timestamp is not valid";
1047 case ERROR_READ:
1048 return "Sample buffer: Read error";
1049 case ERROR_WRITE:
1050 return "Sample buffer: Write error";
1051 case ERROR_OVERFLOW:
1052 return "Sample buffer: Overrun";
1053 default:
1054 return "Sample buffer: Unknown error";
1055 }
1056}
1057
Thomas Tsoucb69f082013-04-08 14:18:26 -04001058RadioDevice *RadioDevice::make(int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001059{
Thomas Tsoucb69f082013-04-08 14:18:26 -04001060 return new uhd_device(sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001061}