aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M/UHDDevice.cpp
diff options
context:
space:
mode:
authorkurtis.heimerl <kurtis.heimerl@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-11-26 03:16:54 +0000
committerkurtis.heimerl <kurtis.heimerl@19bc5d8c-e614-43d4-8b26-e1612bc8e597>2011-11-26 03:16:54 +0000
commit965e757c53d2abc805d927bb7d1d2b62d24bfe4a (patch)
treebd78ce2166e17b1f494f38e1d05ac735dac661cb /Transceiver52M/UHDDevice.cpp
parentd302868b13c4abe5616d5425b1bc47420023831c (diff)
uhd: add 52 MHz transceiver support
These are mostly identical changes as added to the non-52MHz implementation with the exception of sample rate. Signed-off-by: Thomas Tsou <ttsou@vt.edu> git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2634 19bc5d8c-e614-43d4-8b26-e1612bc8e597
Diffstat (limited to 'Transceiver52M/UHDDevice.cpp')
-rw-r--r--Transceiver52M/UHDDevice.cpp754
1 files changed, 754 insertions, 0 deletions
diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp
new file mode 100644
index 0000000..9781495
--- /dev/null
+++ b/Transceiver52M/UHDDevice.cpp
@@ -0,0 +1,754 @@
+/*
+* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+*
+* This software is distributed under the terms of the GNU Affero Public License.
+* See the COPYING file in the main directory for details.
+*
+* This use of this software may be subject to additional restrictions.
+* See the LEGAL file in the main directory for details.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "radioDevice.h"
+#include "Threads.h"
+#include "Logger.h"
+#include <uhd/usrp/single_usrp.hpp>
+#include <uhd/utils/thread_priority.hpp>
+
+/*
+ use_ext_ref - Enable external 10MHz clock reference
+
+ master_clk_rt - Master clock frequency
+
+ rx_smpl_offset - Timing correction in seconds between receive and
+ transmit timestamps. This value corrects for delays on
+ on the RF side of the timestamping point of the device.
+
+ smpl_buf_sz - The receive sample buffer size in bytes.
+*/
+const bool use_ext_ref = false;
+const double master_clk_rt = 52e6;
+const double rx_smpl_offset = .00005;
+const size_t smpl_buf_sz = (1 << 20);
+
+/** Timestamp conversion
+ @param timestamp a UHD or OpenBTS timestamp
+ @param rate sample rate
+ @return the converted timestamp
+*/
+uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
+{
+ double secs = (double) ticks / rate;
+ return uhd::time_spec_t(secs);
+}
+
+TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
+{
+ size_t ticks = ts.get_full_secs() * rate;
+ return ts.get_tick_count(rate) + ticks;
+}
+
+/*
+ Sample Buffer - Allows reading and writing of timed samples using OpenBTS
+ or UHD style timestamps. Time conversions are handled
+ internally or accessable through the static convert calls.
+*/
+class smpl_buf {
+public:
+ /** Sample buffer constructor
+ @param len number of 32-bit samples the buffer should hold
+ @param rate sample clockrate
+ @param timestamp
+ */
+ smpl_buf(size_t len, double rate);
+ ~smpl_buf();
+
+ /** Query number of samples available for reading
+ @param timestamp time of first sample
+ @return number of available samples or error
+ */
+ ssize_t avail_smpls(TIMESTAMP timestamp) const;
+ ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
+
+ /** Read and write
+ @param buf pointer to buffer
+ @param len number of samples desired to read or write
+ @param timestamp time of first stample
+ @return number of actual samples read or written or error
+ */
+ ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
+ ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
+ ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
+ ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
+
+ /** Buffer status string
+ @return a formatted string describing internal buffer state
+ */
+ std::string str_status() const;
+
+ /** Formatted error string
+ @param code an error code
+ @return a formatted error string
+ */
+ static std::string str_code(ssize_t code);
+
+ enum err_code {
+ ERROR_TIMESTAMP = -1,
+ ERROR_READ = -2,
+ ERROR_WRITE = -3,
+ ERROR_OVERFLOW = -4
+ };
+
+private:
+ uint32_t *data;
+ size_t buf_len;
+
+ double clk_rt;
+
+ TIMESTAMP time_start;
+ TIMESTAMP time_end;
+
+ size_t data_start;
+ size_t data_end;
+};
+
+/*
+ uhd_device - UHD implementation of the Device interface. Timestamped samples
+ are sent to and received from the device. An intermediate buffer
+ on the receive side collects and aligns packets of samples.
+ Events and errors such as underruns are reported asynchronously
+ by the device and received in a separate thread.
+*/
+class uhd_device : public RadioDevice {
+public:
+ uhd_device(double rate, bool skip_rx);
+ ~uhd_device();
+
+ bool open();
+ bool start();
+ bool stop();
+ void setPriority();
+
+ int readSamples(short *buf, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
+
+ int writeSamples(short *buf, int len, bool *underrun,
+ TIMESTAMP timestamp, bool isControl);
+
+ bool updateAlignment(TIMESTAMP timestamp);
+
+ bool setTxFreq(double wFreq);
+ bool setRxFreq(double wFreq);
+
+ inline TIMESTAMP initialWriteTimestamp() { return 0; }
+ inline TIMESTAMP initialReadTimestamp() { return 0; }
+
+ inline double fullScaleInputValue() { return 13500.0; }
+ inline double fullScaleOutputValue() { return 9450.0; }
+
+ double setRxGain(double dB) { return 0; }
+ double getRxGain(void) { return 0; }
+ double maxRxGain(void) { return 0; }
+ double minRxGain(void) { return 0; }
+
+ double setTxGain(double dB) { return 0; }
+ double maxTxGain(void) { return 0; }
+ double minTxGain(void) { return 0; }
+
+ double getTxFreq() { return 0; }
+ double getRxFreq() { return 0; }
+
+ inline double getSampleRate() { return actual_smpl_rt; }
+ inline double numberRead() { return rx_pkt_cnt; }
+ inline double numberWritten() { return 0; }
+
+ /** Receive and process asynchronous message
+ @return true if message received or false on timeout or error
+ */
+ bool recv_async_msg();
+
+private:
+ uhd::usrp::single_usrp::sptr usrp_dev;
+
+ double desired_smpl_rt;
+ double actual_smpl_rt;
+
+ size_t tx_spp;
+ size_t rx_spp;
+
+ bool started;
+ bool aligned;
+ bool skip_rx;
+
+ size_t rx_pkt_cnt;
+ size_t drop_cnt;
+ uhd::time_spec_t prev_ts;
+
+ TIMESTAMP ts_offset;
+ smpl_buf *rx_smpl_buf;
+
+ std::string str_code(uhd::rx_metadata_t metadata);
+ std::string str_code(uhd::async_metadata_t metadata);
+
+ Thread async_event_thrd;
+};
+
+void *async_event_loop(uhd_device *dev)
+{
+ while (1) {
+ dev->recv_async_msg();
+ pthread_testcancel();
+ }
+}
+
+uhd_device::uhd_device(double rate, bool skip_rx)
+ : desired_smpl_rt(rate), actual_smpl_rt(0), tx_spp(0), rx_spp(0),
+ started(false), aligned(true), rx_pkt_cnt(0), drop_cnt(0),
+ prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
+{
+ this->skip_rx = skip_rx;
+}
+
+uhd_device::~uhd_device()
+{
+ stop();
+
+ if (rx_smpl_buf)
+ delete rx_smpl_buf;
+}
+
+static double set_usrp_rates(uhd::usrp::single_usrp::sptr dev, double rate)
+{
+ double actual_rate;
+
+ dev->set_tx_rate(rate);
+ dev->set_rx_rate(rate);
+ actual_rate = dev->get_tx_rate();
+
+ if (actual_rate != rate) {
+ LOG(ERROR) << "Actual sample rate differs from desired rate";
+ return -1.0;
+ }
+ if (dev->get_rx_rate() != actual_rate) {
+ LOG(ERROR) << "Transmit and receive sample rates do not match";
+ return -1.0;
+ }
+
+ return actual_rate;
+}
+
+static void set_usrp_tx_gain(uhd::usrp::single_usrp::sptr dev, double)
+{
+ uhd::gain_range_t range = dev->get_tx_gain_range();
+ dev->set_tx_gain((range.start() + range.stop()) / 2);
+}
+
+static void set_usrp_rx_gain(uhd::usrp::single_usrp::sptr dev, double)
+{
+ uhd::gain_range_t range = dev->get_rx_gain_range();
+ dev->set_rx_gain((range.start() + range.stop()) / 2);
+}
+
+static void set_usrp_ref_clk(uhd::usrp::single_usrp::sptr dev, bool ext_clk)
+{
+ uhd::clock_config_t clk_cfg;
+
+ clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
+ clk_cfg.pps_polarity = uhd::clock_config_t::PPS_NEG;
+
+ if (ext_clk)
+ clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
+ else
+ clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
+
+ dev->set_clock_config(clk_cfg);
+}
+
+bool uhd_device::open()
+{
+ LOG(INFO) << "creating USRP device...";
+
+ // Use the first available USRP2 / N210
+ uhd::device_addr_t dev_addr("type=usrp2");
+ try {
+ usrp_dev = uhd::usrp::single_usrp::make(dev_addr);
+ }
+
+ catch(...) {
+ LOG(ERROR) << "USRP make failed";
+ return false;
+ }
+
+ // Set master clock rate
+ usrp_dev->set_master_clock_rate(master_clk_rt);
+
+ // Number of samples per over-the-wire packet
+ tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
+ rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
+
+ // Set rates
+ actual_smpl_rt = set_usrp_rates(usrp_dev, desired_smpl_rt);
+ if (actual_smpl_rt < 0)
+ return false;
+
+ // Create receive buffer
+ size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
+ rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
+
+ // Set receive chain sample offset
+ ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
+
+ // Set gains to midpoint
+ set_usrp_tx_gain(usrp_dev, 0.0);
+
+ // Set reference clock
+ set_usrp_ref_clk(usrp_dev, use_ext_ref);
+
+ // Print configuration
+ LOG(INFO) << usrp_dev->get_pp_string();
+
+ return true;
+}
+
+bool uhd_device::start()
+{
+ LOG(INFO) << "Starting USRP...";
+
+ if (started) {
+ LOG(ERROR) << "Device already started";
+ return false;
+ }
+
+ setPriority();
+
+ // Start asynchronous event (underrun check) loop
+ async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
+
+ // Start streaming
+ uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
+ cmd.stream_now = true;
+ usrp_dev->set_time_now(uhd::time_spec_t(0.0));
+
+ if (!skip_rx)
+ usrp_dev->issue_stream_cmd(cmd);
+
+ // Display usrp time
+ double time_now = usrp_dev->get_time_now().get_real_secs();
+ LOG(INFO) << "The current time is " << time_now << " seconds";
+
+ started = true;
+ return true;
+}
+
+bool uhd_device::stop()
+{
+ uhd::stream_cmd_t stream_cmd =
+ uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
+
+ usrp_dev->issue_stream_cmd(stream_cmd);
+
+ started = false;
+ return true;
+}
+
+void uhd_device::setPriority()
+{
+ uhd::set_thread_priority_safe();
+ return;
+}
+
+static int check_rx_md_err(uhd::rx_metadata_t &md, uhd::time_spec_t &prev_ts)
+{
+ // Missing timestamp
+ if (!md.has_time_spec) {
+ LOG(ERROR) << "UHD: Received packet missing timestamp";
+ return -1;
+ }
+
+ // Monotonicity check
+ if (md.time_spec < prev_ts) {
+ LOG(ERROR) << "Loss of monotonicity";
+ return -1;
+ } else {
+ prev_ts = md.time_spec;
+ }
+
+ return 0;
+}
+
+int uhd_device::readSamples(short *buf, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
+{
+ ssize_t rc;
+ uhd::time_spec_t ts;
+ uhd::rx_metadata_t metadata;
+ uint32_t pkt_buf[rx_spp];
+
+ if (skip_rx)
+ return 0;
+
+ // Shift read time with respect to transmit clock
+ timestamp += ts_offset;
+
+ ts = convert_time(timestamp, actual_smpl_rt);
+ LOG(DEEPDEBUG) << "Requested timestamp = " << ts.get_real_secs();
+
+ // Check that timestamp is valid
+ rc = rx_smpl_buf->avail_smpls(timestamp);
+ if (rc < 0) {
+ LOG(ERROR) << rx_smpl_buf->str_code(rc);
+ LOG(ERROR) << rx_smpl_buf->str_status();
+ return 0;
+ }
+
+ // Receive samples from the usrp until we have enough
+ while (rx_smpl_buf->avail_smpls(timestamp) < len) {
+ size_t num_smpls = usrp_dev->get_device()->recv(
+ (void*)pkt_buf,
+ rx_spp,
+ metadata,
+ uhd::io_type_t::COMPLEX_INT16,
+ uhd::device::RECV_MODE_ONE_PACKET);
+
+ rx_pkt_cnt++;
+
+ // Recv error in UHD
+ if (!num_smpls) {
+ LOG(ERROR) << str_code(metadata);
+ return 0;
+ }
+
+ // Other metadata timing checks
+ if (check_rx_md_err(metadata, prev_ts) < 0)
+ return 0;
+
+ ts = metadata.time_spec;
+ LOG(DEEPDEBUG) << "Received timestamp = " << ts.get_real_secs();
+
+ rc = rx_smpl_buf->write(pkt_buf,
+ num_smpls,
+ metadata.time_spec);
+
+ // Continue on local overrun, exit on other errors
+ if ((rc < 0)) {
+ LOG(ERROR) << rx_smpl_buf->str_code(rc);
+ LOG(ERROR) << rx_smpl_buf->str_status();
+ if (rc != smpl_buf::ERROR_OVERFLOW)
+ return 0;
+ }
+ }
+
+ // We have enough samples
+ rc = rx_smpl_buf->read(buf, len, timestamp);
+ if ((rc < 0) || (rc != len)) {
+ LOG(ERROR) << rx_smpl_buf->str_code(rc);
+ LOG(ERROR) << rx_smpl_buf->str_status();
+ return 0;
+ }
+
+ return len;
+}
+
+int uhd_device::writeSamples(short *buf, int len, bool *underrun,
+ unsigned long long timestamp,bool isControl)
+{
+ uhd::tx_metadata_t metadata;
+ metadata.has_time_spec = true;
+ metadata.start_of_burst = false;
+ metadata.end_of_burst = false;
+ metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
+
+ // No control packets
+ if (isControl) {
+ LOG(ERROR) << "Control packets not supported";
+ return 0;
+ }
+
+ // Drop a fixed number of packets (magic value)
+ if (!aligned) {
+ drop_cnt++;
+
+ if (drop_cnt == 1) {
+ LOG(DEBUG) << "Aligning transmitter: stop burst";
+ metadata.end_of_burst = true;
+ } else if (drop_cnt < 30) {
+ LOG(DEEPDEBUG) << "Aligning transmitter: packet advance";
+ *underrun = true;
+ return len;
+ } else {
+ LOG(DEBUG) << "Aligning transmitter: start burst";
+ metadata.start_of_burst = true;
+ aligned = true;
+ drop_cnt = 0;
+ }
+ }
+
+ size_t num_smpls = usrp_dev->get_device()->send(buf,
+ len,
+ metadata,
+ uhd::io_type_t::COMPLEX_INT16,
+ uhd::device::SEND_MODE_FULL_BUFF);
+
+ if (num_smpls != (unsigned)len)
+ LOG(ERROR) << "UHD: Sent fewer samples than requested";
+
+ return num_smpls;
+}
+
+bool uhd_device::updateAlignment(TIMESTAMP timestamp)
+{
+ /* NOP */
+ return true;
+}
+
+bool uhd_device::setTxFreq(double wFreq)
+{
+ uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
+ LOG(INFO) << tr.to_pp_string();
+ return true;
+}
+
+bool uhd_device::setRxFreq(double wFreq)
+{
+ uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
+ LOG(INFO) << tr.to_pp_string();
+ return true;
+}
+
+bool uhd_device::recv_async_msg()
+{
+ uhd::async_metadata_t metadata;
+ if (!usrp_dev->get_device()->recv_async_msg(metadata))
+ return false;
+
+ // Assume that any error requires resynchronization
+ if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
+ aligned = false;
+ LOG(INFO) << str_code(metadata);
+ }
+
+ return true;
+}
+
+std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
+{
+ std::ostringstream ost("UHD: ");
+
+ switch (metadata.error_code) {
+ case uhd::rx_metadata_t::ERROR_CODE_NONE:
+ ost << "No error";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
+ ost << "No packet received, implementation timed-out";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
+ ost << "A stream command was issued in the past";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
+ ost << "Expected another stream command";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
+ ost << "An internal receive buffer has filled";
+ break;
+ case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
+ ost << "The packet could not be parsed";
+ break;
+ default:
+ ost << "Unknown error " << metadata.error_code;
+ }
+
+ if (metadata.has_time_spec)
+ ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+ return ost.str();
+}
+
+std::string uhd_device::str_code(uhd::async_metadata_t metadata)
+{
+ std::ostringstream ost("UHD: ");
+
+ switch (metadata.event_code) {
+ case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
+ ost << "A packet was successfully transmitted";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
+ ost << "An internal send buffer has emptied";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
+ ost << "Packet loss between host and device";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
+ ost << "Packet time was too late or too early";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
+ ost << "Underflow occurred inside a packet";
+ break;
+ case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
+ ost << "Packet loss within a burst";
+ break;
+ default:
+ ost << "Unknown error " << metadata.event_code;
+ }
+
+ if (metadata.has_time_spec)
+ ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
+
+ return ost.str();
+}
+
+smpl_buf::smpl_buf(size_t len, double rate)
+ : buf_len(len), clk_rt(rate),
+ time_start(0), time_end(0), data_start(0), data_end(0)
+{
+ data = new uint32_t[len];
+}
+
+smpl_buf::~smpl_buf()
+{
+ delete[] data;
+}
+
+ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
+{
+ if (timestamp < time_start)
+ return ERROR_TIMESTAMP;
+ else if (timestamp >= time_end)
+ return 0;
+ else
+ return time_end - timestamp;
+}
+
+ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
+{
+ return avail_smpls(convert_time(timespec, clk_rt));
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ // Check for valid read
+ if (timestamp < time_start)
+ return ERROR_TIMESTAMP;
+ if (timestamp >= time_end)
+ return 0;
+ if (len >= buf_len)
+ return ERROR_READ;
+
+ // How many samples should be copied
+ size_t num_smpls = time_end - timestamp;
+ if (num_smpls > len);
+ num_smpls = len;
+
+ // Starting index
+ size_t read_start = data_start + (timestamp - time_start);
+
+ // Read it
+ if (read_start + num_smpls < buf_len) {
+ size_t numBytes = len * 2 * sizeof(short);
+ memcpy(buf, data + read_start, numBytes);
+ } else {
+ size_t first_cp = (buf_len - read_start) * 2 * sizeof(short);
+ size_t second_cp = len * 2 * sizeof(short) - first_cp;
+
+ memcpy(buf, data + read_start, first_cp);
+ memcpy((char*) buf + first_cp, data, second_cp);
+ }
+
+ data_start = (read_start + len) % buf_len;
+ time_start = timestamp + len;
+
+ if (time_start > time_end)
+ return ERROR_READ;
+ else
+ return num_smpls;
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
+{
+ return read(buf, len, convert_time(ts, clk_rt));
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ // Check for valid write
+ if ((len == 0) || (len >= buf_len))
+ return ERROR_WRITE;
+ if ((timestamp + len) <= time_end)
+ return ERROR_TIMESTAMP;
+
+ // Starting index
+ size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
+
+ // Write it
+ if ((write_start + len) < buf_len) {
+ size_t numBytes = len * 2 * sizeof(short);
+ memcpy(data + write_start, buf, numBytes);
+ } else {
+ size_t first_cp = (buf_len - write_start) * 2 * sizeof(short);
+ size_t second_cp = len * 2 * sizeof(short) - first_cp;
+
+ memcpy(data + write_start, buf, first_cp);
+ memcpy(data, (char*) buf + first_cp, second_cp);
+ }
+
+ data_end = (write_start + len) % buf_len;
+ time_end = timestamp + len;
+
+ if (((write_start + len) > buf_len) && (data_end > data_start))
+ return ERROR_OVERFLOW;
+ else if (time_end <= time_start)
+ return ERROR_WRITE;
+ else
+ return len;
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
+{
+ return write(buf, len, convert_time(ts, clk_rt));
+}
+
+std::string smpl_buf::str_status() const
+{
+ std::ostringstream ost("Sample buffer: ");
+
+ ost << "length = " << buf_len;
+ ost << ", time_start = " << time_start;
+ ost << ", time_end = " << time_end;
+ ost << ", data_start = " << data_start;
+ ost << ", data_end = " << data_end;
+
+ return ost.str();
+}
+
+std::string smpl_buf::str_code(ssize_t code)
+{
+ switch (code) {
+ case ERROR_TIMESTAMP:
+ return "Sample buffer: Requested timestamp is not valid";
+ case ERROR_READ:
+ return "Sample buffer: Read error";
+ case ERROR_WRITE:
+ return "Sample buffer: Write error";
+ case ERROR_OVERFLOW:
+ return "Sample buffer: Overrun";
+ default:
+ return "Sample buffer: Unknown error";
+ }
+}
+
+RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
+{
+ return new uhd_device(smpl_rt, skip_rx);
+}