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
This commit is contained in:
parent
2876785f50
commit
7bef2346c4
|
@ -3,6 +3,8 @@ include $(top_srcdir)/Makefile.common
|
||||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/..
|
||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
|
||||||
|
|
||||||
|
noinst_HEADERS = UHDDevice.h smpl_buf.h
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libdevice.la
|
noinst_LTLIBRARIES = libdevice.la
|
||||||
|
|
||||||
libdevice_la_SOURCES = UHDDevice.cpp
|
libdevice_la_SOURCES = UHDDevice.cpp smpl_buf.cpp
|
||||||
|
|
|
@ -23,11 +23,9 @@
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include "radioDevice.h"
|
#include "radioDevice.h"
|
||||||
|
#include "UHDDevice.h"
|
||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
#include <uhd/version.hpp>
|
|
||||||
#include <uhd/property_tree.hpp>
|
|
||||||
#include <uhd/usrp/multi_usrp.hpp>
|
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
@ -58,20 +56,6 @@
|
||||||
*/
|
*/
|
||||||
#define UMTRX_VGA1_DEF -18
|
#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
|
* 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" } },
|
{ 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)
|
void *async_event_loop(uhd_device *dev)
|
||||||
{
|
{
|
||||||
set_selfthread_name("UHDAsyncEvent");
|
set_selfthread_name("UHDAsyncEvent");
|
||||||
|
@ -1386,166 +1186,6 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
|
||||||
return ost.str();
|
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,
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||||
InterfaceType iface, size_t chans, double lo_offset,
|
InterfaceType iface, size_t chans, double lo_offset,
|
||||||
const std::vector<std::string>& tx_paths,
|
const std::vector<std::string>& tx_paths,
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
|
@ -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";
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
};
|
Loading…
Reference in New Issue