aboutsummaryrefslogtreecommitdiffstats
path: root/Transceiver52M
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2019-04-29 17:23:21 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2019-05-03 13:50:47 +0200
commit7bef2346c41d2e02d221a7f501a295bd5cbbbc9b (patch)
tree1d4743fb993173eb70e740babfd1aa38a422f868 /Transceiver52M
parent2876785f5069a3b1d5c39f0a8af59fb497e5267a (diff)
cosmetic: uhd: Move smpl_buf out of UHDDevice, move UHDDevice class definition to .h
* move class definition to .h file, like we do for other devices. * move smpl_buf class to a different file inside uhd/. * Preparation work to have smpl_buf being used in a generic way for devices other than UHD (LMS). Change-Id: Ib4594320da9bb7f6e9f52e7d70d11ecd11106aae
Diffstat (limited to 'Transceiver52M')
-rw-r--r--Transceiver52M/device/uhd/Makefile.am4
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.cpp362
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.h171
-rw-r--r--Transceiver52M/device/uhd/smpl_buf.cpp186
-rw-r--r--Transceiver52M/device/uhd/smpl_buf.h94
5 files changed, 455 insertions, 362 deletions
diff --git a/Transceiver52M/device/uhd/Makefile.am b/Transceiver52M/device/uhd/Makefile.am
index bb34d2f..4fcc0d7 100644
--- a/Transceiver52M/device/uhd/Makefile.am
+++ b/Transceiver52M/device/uhd/Makefile.am
@@ -3,6 +3,8 @@ include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
+noinst_HEADERS = UHDDevice.h smpl_buf.h
+
noinst_LTLIBRARIES = libdevice.la
-libdevice_la_SOURCES = UHDDevice.cpp
+libdevice_la_SOURCES = UHDDevice.cpp smpl_buf.cpp
diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp
index 46284e5..67b7416 100644
--- a/Transceiver52M/device/uhd/UHDDevice.cpp
+++ b/Transceiver52M/device/uhd/UHDDevice.cpp
@@ -23,11 +23,9 @@
#include <map>
#include "radioDevice.h"
+#include "UHDDevice.h"
#include "Threads.h"
#include "Logger.h"
-#include <uhd/version.hpp>
-#include <uhd/property_tree.hpp>
-#include <uhd/usrp/multi_usrp.hpp>
#ifdef HAVE_CONFIG_H
#include "config.h"
@@ -58,20 +56,6 @@
*/
#define UMTRX_VGA1_DEF -18
-enum uhd_dev_type {
- USRP1,
- USRP2,
- B100,
- B200,
- B210,
- B2XX_MCBTS,
- E1XX,
- E3XX,
- X3XX,
- UMTRX,
- LIMESDR,
-};
-
/*
* USRP version dependent device timings
*/
@@ -136,190 +120,6 @@ static const std::map<dev_key, dev_desc> dev_param_map {
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
};
-/*
- Sample Buffer - Allows reading and writing of timed samples using osmo-trx
- 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(size_t ts) 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(size_t tx_sps, size_t rx_sps, InterfaceType type,
- size_t chans, double offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths);
- ~uhd_device();
-
- int open(const std::string &args, int ref, bool swap_channels);
- bool start();
- bool stop();
- bool restart();
- void setPriority(float prio);
- enum TxWindowType getWindowType() { return tx_window; }
-
- int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
- TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
-
- int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
- TIMESTAMP timestamp, bool isControl);
-
- bool updateAlignment(TIMESTAMP timestamp);
-
- bool setTxFreq(double wFreq, size_t chan);
- bool setRxFreq(double wFreq, size_t chan);
-
- TIMESTAMP initialWriteTimestamp();
- TIMESTAMP initialReadTimestamp();
-
- double fullScaleInputValue();
- double fullScaleOutputValue();
-
- double setRxGain(double db, size_t chan);
- double getRxGain(size_t chan);
- double maxRxGain(void) { return rx_gain_max; }
- double minRxGain(void) { return rx_gain_min; }
-
- double setTxGain(double db, size_t chan);
- double maxTxGain(void) { return tx_gain_max; }
- double minTxGain(void) { return tx_gain_min; }
-
- double getTxFreq(size_t chan);
- double getRxFreq(size_t chan);
- double getRxFreq();
-
- bool setRxAntenna(const std::string &ant, size_t chan);
- std::string getRxAntenna(size_t chan);
- bool setTxAntenna(const std::string &ant, size_t chan);
- std::string getTxAntenna(size_t chan);
-
- bool requiresRadioAlign();
-
- GSM::Time minLatency();
-
- inline double getSampleRate() { return tx_rate; }
- 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();
-
- enum err_code {
- ERROR_TIMING = -1,
- ERROR_TIMEOUT = -2,
- ERROR_UNRECOVERABLE = -3,
- ERROR_UNHANDLED = -4,
- };
-
-private:
- uhd::usrp::multi_usrp::sptr usrp_dev;
- uhd::tx_streamer::sptr tx_stream;
- uhd::rx_streamer::sptr rx_stream;
- enum TxWindowType tx_window;
- enum uhd_dev_type dev_type;
-
- double tx_rate, rx_rate;
-
- double tx_gain_min, tx_gain_max;
- double rx_gain_min, rx_gain_max;
-
- std::vector<double> tx_gains, rx_gains;
- std::vector<double> tx_freqs, rx_freqs;
- size_t tx_spp, rx_spp;
-
- bool started;
- bool aligned;
-
- size_t rx_pkt_cnt;
- size_t drop_cnt;
- uhd::time_spec_t prev_ts;
-
- TIMESTAMP ts_initial, ts_offset;
- std::vector<smpl_buf *> rx_buffers;
-
- void init_gains();
- void set_channels(bool swap);
- void set_rates();
- bool parse_dev_type();
- bool flush_recv(size_t num_pkts);
- int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
-
- std::string str_code(uhd::rx_metadata_t metadata);
- std::string str_code(uhd::async_metadata_t metadata);
-
- uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
- bool set_freq(double freq, size_t chan, bool tx);
-
- Thread *async_event_thrd;
- Mutex tune_lock;
-};
-
void *async_event_loop(uhd_device *dev)
{
set_selfthread_name("UHDAsyncEvent");
@@ -1386,166 +1186,6 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
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(timespec.to_ticks(clk_rt));
-}
-
-ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
-{
- int type_sz = 2 * sizeof(short);
-
- // 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)) % buf_len;
-
- // Read it
- if (read_start + num_smpls < buf_len) {
- size_t numBytes = len * type_sz;
- memcpy(buf, data + read_start, numBytes);
- } else {
- size_t first_cp = (buf_len - read_start) * type_sz;
- size_t second_cp = len * type_sz - 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, ts.to_ticks(clk_rt));
-}
-
-ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
-{
- int type_sz = 2 * sizeof(short);
-
- // Check for valid write
- if ((len == 0) || (len >= buf_len))
- return ERROR_WRITE;
- if ((timestamp + len) <= time_end)
- return ERROR_TIMESTAMP;
-
- if (timestamp < time_end) {
- LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
- uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
- LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
- // Do not return error here, because it's a rounding error and is not fatal
- }
- if (timestamp > time_end && time_end != 0) {
- LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
- uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
- LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
- // Do not return error here, because it's a rounding error and is not fatal
- }
-
- // 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 * type_sz;
- memcpy(data + write_start, buf, numBytes);
- } else {
- size_t first_cp = (buf_len - write_start) * type_sz;
- size_t second_cp = len * type_sz - 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 (!data_start)
- data_start = write_start;
-
- 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, ts.to_ticks(clk_rt));
-}
-
-std::string smpl_buf::str_status(size_t ts) const
-{
- std::ostringstream ost("Sample buffer: ");
-
- ost << "timestamp = " << ts;
- 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(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
diff --git a/Transceiver52M/device/uhd/UHDDevice.h b/Transceiver52M/device/uhd/UHDDevice.h
new file mode 100644
index 0000000..f5d0b33
--- /dev/null
+++ b/Transceiver52M/device/uhd/UHDDevice.h
@@ -0,0 +1,171 @@
+/*
+ * Device support for Ettus Research UHD driver
+ *
+ * Copyright 2010,2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ * Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Tom Tsou <tom.tsou@ettus.com>
+ *
+ * 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/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#pragma once
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "radioDevice.h"
+#include "smpl_buf.h"
+
+#include <uhd/version.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+
+
+enum uhd_dev_type {
+ USRP1,
+ USRP2,
+ B100,
+ B200,
+ B210,
+ B2XX_MCBTS,
+ E1XX,
+ E3XX,
+ X3XX,
+ UMTRX,
+ LIMESDR,
+};
+
+/*
+ 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(size_t tx_sps, size_t rx_sps, InterfaceType type,
+ size_t chans, double offset,
+ const std::vector<std::string>& tx_paths,
+ const std::vector<std::string>& rx_paths);
+ ~uhd_device();
+
+ int open(const std::string &args, int ref, bool swap_channels);
+ bool start();
+ bool stop();
+ bool restart();
+ void setPriority(float prio);
+ enum TxWindowType getWindowType() { return tx_window; }
+
+ int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
+ TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
+
+ int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
+ TIMESTAMP timestamp, bool isControl);
+
+ bool updateAlignment(TIMESTAMP timestamp);
+
+ bool setTxFreq(double wFreq, size_t chan);
+ bool setRxFreq(double wFreq, size_t chan);
+
+ TIMESTAMP initialWriteTimestamp();
+ TIMESTAMP initialReadTimestamp();
+
+ double fullScaleInputValue();
+ double fullScaleOutputValue();
+
+ double setRxGain(double db, size_t chan);
+ double getRxGain(size_t chan);
+ double maxRxGain(void) { return rx_gain_max; }
+ double minRxGain(void) { return rx_gain_min; }
+
+ double setTxGain(double db, size_t chan);
+ double maxTxGain(void) { return tx_gain_max; }
+ double minTxGain(void) { return tx_gain_min; }
+
+ double getTxFreq(size_t chan);
+ double getRxFreq(size_t chan);
+ double getRxFreq();
+
+ bool setRxAntenna(const std::string &ant, size_t chan);
+ std::string getRxAntenna(size_t chan);
+ bool setTxAntenna(const std::string &ant, size_t chan);
+ std::string getTxAntenna(size_t chan);
+
+ bool requiresRadioAlign();
+
+ GSM::Time minLatency();
+
+ inline double getSampleRate() { return tx_rate; }
+ 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();
+
+ enum err_code {
+ ERROR_TIMING = -1,
+ ERROR_TIMEOUT = -2,
+ ERROR_UNRECOVERABLE = -3,
+ ERROR_UNHANDLED = -4,
+ };
+
+private:
+ uhd::usrp::multi_usrp::sptr usrp_dev;
+ uhd::tx_streamer::sptr tx_stream;
+ uhd::rx_streamer::sptr rx_stream;
+ enum TxWindowType tx_window;
+ enum uhd_dev_type dev_type;
+
+ double tx_rate, rx_rate;
+
+ double tx_gain_min, tx_gain_max;
+ double rx_gain_min, rx_gain_max;
+
+ std::vector<double> tx_gains, rx_gains;
+ std::vector<double> tx_freqs, rx_freqs;
+ size_t tx_spp, rx_spp;
+
+ bool started;
+ bool aligned;
+
+ size_t rx_pkt_cnt;
+ size_t drop_cnt;
+ uhd::time_spec_t prev_ts;
+
+ TIMESTAMP ts_initial, ts_offset;
+ std::vector<smpl_buf *> rx_buffers;
+
+ void init_gains();
+ void set_channels(bool swap);
+ void set_rates();
+ bool parse_dev_type();
+ bool flush_recv(size_t num_pkts);
+ int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
+
+ std::string str_code(uhd::rx_metadata_t metadata);
+ std::string str_code(uhd::async_metadata_t metadata);
+
+ uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
+ bool set_freq(double freq, size_t chan, bool tx);
+
+ Thread *async_event_thrd;
+ Mutex tune_lock;
+};
diff --git a/Transceiver52M/device/uhd/smpl_buf.cpp b/Transceiver52M/device/uhd/smpl_buf.cpp
new file mode 100644
index 0000000..d8bdbc4
--- /dev/null
+++ b/Transceiver52M/device/uhd/smpl_buf.cpp
@@ -0,0 +1,186 @@
+/*
+ * Sample Buffer - Allows reading and writing of timed samples
+ *
+ * Copyright 2010,2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ * Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Tom Tsou <tom.tsou@ettus.com>
+ *
+ * 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/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#include "smpl_buf.h"
+#include <inttypes.h>
+
+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(timespec.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ int type_sz = 2 * sizeof(short);
+
+ // 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)) % buf_len;
+
+ // Read it
+ if (read_start + num_smpls < buf_len) {
+ size_t numBytes = len * type_sz;
+ memcpy(buf, data + read_start, numBytes);
+ } else {
+ size_t first_cp = (buf_len - read_start) * type_sz;
+ size_t second_cp = len * type_sz - 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, ts.to_ticks(clk_rt));
+}
+
+ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
+{
+ int type_sz = 2 * sizeof(short);
+
+ // Check for valid write
+ if ((len == 0) || (len >= buf_len))
+ return ERROR_WRITE;
+ if ((timestamp + len) <= time_end)
+ return ERROR_TIMESTAMP;
+
+ if (timestamp < time_end) {
+ LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
+ uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+ // Do not return error here, because it's a rounding error and is not fatal
+ }
+ if (timestamp > time_end && time_end != 0) {
+ LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
+ uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
+ // Do not return error here, because it's a rounding error and is not fatal
+ }
+
+ // 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 * type_sz;
+ memcpy(data + write_start, buf, numBytes);
+ } else {
+ size_t first_cp = (buf_len - write_start) * type_sz;
+ size_t second_cp = len * type_sz - 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 (!data_start)
+ data_start = write_start;
+
+ 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, ts.to_ticks(clk_rt));
+}
+
+std::string smpl_buf::str_status(size_t ts) const
+{
+ std::ostringstream ost("Sample buffer: ");
+
+ ost << "timestamp = " << ts;
+ 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";
+ }
+}
diff --git a/Transceiver52M/device/uhd/smpl_buf.h b/Transceiver52M/device/uhd/smpl_buf.h
new file mode 100644
index 0000000..6af97c2
--- /dev/null
+++ b/Transceiver52M/device/uhd/smpl_buf.h
@@ -0,0 +1,94 @@
+/*
+ * Sample Buffer - Allows reading and writing of timed samples
+ *
+ * Copyright 2010,2011 Free Software Foundation, Inc.
+ * Copyright (C) 2015 Ettus Research LLC
+ * Copyright 2019 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * Author: Tom Tsou <tom.tsou@ettus.com>
+ *
+ * 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/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+#pragma once
+
+#include <unistd.h>
+#include <uhd/types/time_spec.hpp>
+
+#include "radioDevice.h"
+
+/*
+ Sample Buffer - Allows reading and writing of timed samples using osmo-trx
+ 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(size_t ts) 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;
+};