diff --git a/Transceiver52M/device/uhd/Makefile.am b/Transceiver52M/device/uhd/Makefile.am index bb34d2f2..4fcc0d79 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 46284e56..67b7416d 100644 --- a/Transceiver52M/device/uhd/UHDDevice.cpp +++ b/Transceiver52M/device/uhd/UHDDevice.cpp @@ -23,11 +23,9 @@ #include #include "radioDevice.h" +#include "UHDDevice.h" #include "Threads.h" #include "Logger.h" -#include -#include -#include #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_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& tx_paths, - const std::vector& 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 &bufs, int len, bool *overrun, - TIMESTAMP timestamp, bool *underrun, unsigned *RSSI); - - int writeSamples(std::vector &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 tx_gains, rx_gains; - std::vector 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 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="<& tx_paths, diff --git a/Transceiver52M/device/uhd/UHDDevice.h b/Transceiver52M/device/uhd/UHDDevice.h new file mode 100644 index 00000000..f5d0b331 --- /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 + * + * Author: Tom Tsou + * + * 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 . + * 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 +#include +#include + + +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& tx_paths, + const std::vector& 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 &bufs, int len, bool *overrun, + TIMESTAMP timestamp, bool *underrun, unsigned *RSSI); + + int writeSamples(std::vector &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 tx_gains, rx_gains; + std::vector 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 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 00000000..d8bdbc48 --- /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 + * + * Author: Tom Tsou + * + * 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 . + * See the COPYING file in the main directory for details. + */ + +#include "smpl_buf.h" +#include + +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="< + * + * Author: Tom Tsou + * + * 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 . + * See the COPYING file in the main directory for details. + */ + +#pragma once + +#include +#include + +#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; +};