From 2128a308eb06aab9a94f479794d334705ae0505e Mon Sep 17 00:00:00 2001 From: Pau Espin Pedrol Date: Tue, 24 Apr 2018 16:16:06 +0200 Subject: Move device specific files to device subdir Change-Id: Ib42fef14bf4c7b779f44d99711a35c18b32a4c21 --- Transceiver52M/Makefile.am | 40 +- Transceiver52M/UHDDevice.cpp | 1576 ---------------------------- Transceiver52M/USRPDevice.cpp | 648 ------------ Transceiver52M/USRPDevice.h | 204 ---- Transceiver52M/device/Makefile.am | 11 + Transceiver52M/device/radioDevice.h | 160 +++ Transceiver52M/device/uhd/Makefile.am | 8 + Transceiver52M/device/uhd/UHDDevice.cpp | 1576 ++++++++++++++++++++++++++++ Transceiver52M/device/usrp1/Makefile.am | 10 + Transceiver52M/device/usrp1/USRPDevice.cpp | 648 ++++++++++++ Transceiver52M/device/usrp1/USRPDevice.h | 204 ++++ Transceiver52M/radioDevice.h | 160 --- configure.ac | 3 + 13 files changed, 2639 insertions(+), 2609 deletions(-) delete mode 100644 Transceiver52M/UHDDevice.cpp delete mode 100644 Transceiver52M/USRPDevice.cpp delete mode 100644 Transceiver52M/USRPDevice.h create mode 100644 Transceiver52M/device/Makefile.am create mode 100644 Transceiver52M/device/radioDevice.h create mode 100644 Transceiver52M/device/uhd/Makefile.am create mode 100644 Transceiver52M/device/uhd/UHDDevice.cpp create mode 100644 Transceiver52M/device/usrp1/Makefile.am create mode 100644 Transceiver52M/device/usrp1/USRPDevice.cpp create mode 100644 Transceiver52M/device/usrp1/USRPDevice.h delete mode 100644 Transceiver52M/radioDevice.h diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 06b2f26..26f7510 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -21,17 +21,11 @@ include $(top_srcdir)/Makefile.common -SUBDIRS = arch +SUBDIRS = arch device -AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common +AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -if USRP1 -AM_CPPFLAGS += $(USRP_CFLAGS) -else -AM_CPPFLAGS += $(UHD_CFLAGS) -endif - rev2dir = $(datadir)/usrp/rev2 rev4dir = $(datadir)/usrp/rev4 @@ -42,7 +36,7 @@ EXTRA_DIST = \ README \ README.Talgorithm -noinst_LTLIBRARIES = libtransceiver.la +noinst_LTLIBRARIES = libtransceiver_common.la COMMON_SOURCES = \ radioInterface.cpp \ @@ -56,33 +50,28 @@ COMMON_SOURCES = \ Channelizer.cpp \ Synthesis.cpp -libtransceiver_la_SOURCES = \ +libtransceiver_common_la_SOURCES = \ $(COMMON_SOURCES) \ Resampler.cpp \ radioInterfaceResamp.cpp \ radioInterfaceMulti.cpp -bin_PROGRAMS = osmo-trx - noinst_HEADERS = \ Complex.h \ radioInterface.h \ radioVector.h \ radioClock.h \ - radioDevice.h \ radioBuffer.h \ sigProcLib.h \ signalVector.h \ Transceiver.h \ - USRPDevice.h \ Resampler.h \ ChannelizerBase.h \ Channelizer.h \ Synthesis.h -osmo_trx_SOURCES = osmo-trx.cpp -osmo_trx_LDADD = \ - libtransceiver.la \ +COMMON_LDADD = \ + libtransceiver_common.la \ $(ARCH_LA) \ $(GSM_LA) \ $(COMMON_LA) \ @@ -91,10 +80,19 @@ osmo_trx_LDADD = \ $(LIBOSMOCTRL_LIBS) \ $(LIBOSMOVTY_LIBS) +bin_PROGRAMS = osmo-trx +osmo_trx_SOURCES = osmo-trx.cpp + if USRP1 -libtransceiver_la_SOURCES += USRPDevice.cpp -osmo_trx_LDADD += $(USRP_LIBS) +osmo_trx_LDADD = \ + $(COMMON_LDADD) \ + $(USRP_LIBS) \ + $(builddir)/device/usrp1/libdevice.la +osmo_trx_CPPFLAGS = $(AM_CPPFLAGS) $(USRP_CFLAGS) else -libtransceiver_la_SOURCES += UHDDevice.cpp -osmo_trx_LDADD += $(UHD_LIBS) +osmo_trx_LDADD = \ + $(COMMON_LDADD) \ + $(UHD_LIBS) \ + $(builddir)/device/uhd/libdevice.la +osmo_trx_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) endif diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp deleted file mode 100644 index 4466da4..0000000 --- a/Transceiver52M/UHDDevice.cpp +++ /dev/null @@ -1,1576 +0,0 @@ -/* - * Device support for Ettus Research UHD driver - * - * Copyright 2010,2011 Free Software Foundation, Inc. - * Copyright (C) 2015 Ettus Research LLC - * - * 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 -#include "radioDevice.h" -#include "Threads.h" -#include "Logger.h" -#include -#include -#include -#include - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#ifndef USE_UHD_3_11 -#include -#endif - -#define USRP_TX_AMPL 0.3 -#define UMTRX_TX_AMPL 0.7 -#define LIMESDR_TX_AMPL 0.3 -#define SAMPLE_BUF_SZ (1 << 20) - -/* - * UHD timeout value on streaming (re)start - * - * Allow some time for streaming to commence after the start command is issued, - * but consider a wait beyond one second to be a definite error condition. - */ -#define UHD_RESTART_TIMEOUT 1.0 - -/* - * UmTRX specific settings - */ -#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 - */ -#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11) -#define B2XX_TIMING_1SPS 1.7153e-4 -#define B2XX_TIMING_4SPS 1.1696e-4 -#define B2XX_TIMING_4_4SPS 6.18462e-5 -#define B2XX_TIMING_MCBTS 7e-5 -#else -#define B2XX_TIMING_1SPS 9.9692e-5 -#define B2XX_TIMING_4SPS 6.9248e-5 -#define B2XX_TIMING_4_4SPS 4.52308e-5 -#define B2XX_TIMING_MCBTS 6.42452e-5 -#endif - -/* - * Tx / Rx sample offset values. In a perfect world, there is no group delay - * though analog components, and behaviour through digital filters exactly - * matches calculated values. In reality, there are unaccounted factors, - * which are captured in these empirically measured (using a loopback test) - * timing correction values. - * - * Notes: - * USRP1 with timestamps is not supported by UHD. - */ - -/* Device Type, Tx-SPS, Rx-SPS */ -typedef std::tuple dev_key; - -/* Device parameter descriptor */ -struct dev_desc { - unsigned channels; - double mcr; - double rate; - double offset; - std::string str; -}; - -static const std::map dev_param_map { - { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } }, - { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } }, - { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } }, - { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } }, - { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } }, - { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } }, - { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } }, - { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } }, - { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } }, - { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } }, - { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } }, - { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } }, - { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } }, - { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } }, - { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } }, - { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } }, - { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } }, - { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } }, - { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } }, - { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} }, - { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } }, - { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } }, - { 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); - - 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; - - size_t tx_sps, rx_sps, chans; - double tx_rate, rx_rate; - - double tx_gain_min, tx_gain_max; - double rx_gain_min, rx_gain_max; - double offset; - - std::vector tx_gains, rx_gains; - std::vector tx_freqs, rx_freqs; - std::vector tx_paths, rx_paths; - 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 set_antennas(); - 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; - InterfaceType iface; - Mutex tune_lock; -}; - -void *async_event_loop(uhd_device *dev) -{ - dev->setPriority(0.43); - - while (1) { - dev->recv_async_msg(); - pthread_testcancel(); - } - - return NULL; -} - -#ifndef USE_UHD_3_11 -/* - Catch and drop underrun 'U' and overrun 'O' messages from stdout - since we already report using the logging facility. Direct - everything else appropriately. - */ -void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg) -{ - switch (type) { - case uhd::msg::status: - LOG(INFO) << msg; - break; - case uhd::msg::warning: - LOG(WARNING) << msg; - break; - case uhd::msg::error: - LOG(ERR) << msg; - break; - case uhd::msg::fastpath: - break; - } -} -#endif - -static void thread_enable_cancel(bool cancel) -{ - cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); -} - -uhd_device::uhd_device(size_t tx_sps, size_t rx_sps, - InterfaceType iface, size_t chans, double offset, - const std::vector& tx_paths, - const std::vector& rx_paths) - : tx_gain_min(0.0), tx_gain_max(0.0), - rx_gain_min(0.0), rx_gain_max(0.0), - tx_spp(0), rx_spp(0), - started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), - prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) -{ - this->tx_sps = tx_sps; - this->rx_sps = rx_sps; - this->chans = chans; - this->offset = offset; - this->iface = iface; - this->tx_paths = tx_paths; - this->rx_paths = rx_paths; -} - -uhd_device::~uhd_device() -{ - stop(); - - for (size_t i = 0; i < rx_buffers.size(); i++) - delete rx_buffers[i]; -} - -void uhd_device::init_gains() -{ - uhd::gain_range_t range; - - if (dev_type == UMTRX) { - std::vector gain_stages = usrp_dev->get_tx_gain_names(0); - if (gain_stages[0] == "VGA") { - LOG(WARNING) << "Update your UHD version for a proper Tx gain support"; - } - if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { - range = usrp_dev->get_tx_gain_range(); - tx_gain_min = range.start(); - tx_gain_max = range.stop(); - } else { - range = usrp_dev->get_tx_gain_range("VGA2"); - tx_gain_min = UMTRX_VGA1_DEF + range.start(); - tx_gain_max = UMTRX_VGA1_DEF + range.stop(); - } - } else { - range = usrp_dev->get_tx_gain_range(); - tx_gain_min = range.start(); - tx_gain_max = range.stop(); - } - LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]"; - - range = usrp_dev->get_rx_gain_range(); - rx_gain_min = range.start(); - rx_gain_max = range.stop(); - LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; - - for (size_t i = 0; i < tx_gains.size(); i++) { - double gain = (tx_gain_min + tx_gain_max) / 2; - LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain; - usrp_dev->set_tx_gain(gain, i); - tx_gains[i] = usrp_dev->get_tx_gain(i); - } - - for (size_t i = 0; i < rx_gains.size(); i++) { - double gain = (rx_gain_min + rx_gain_max) / 2; - LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain; - usrp_dev->set_rx_gain(gain, i); - rx_gains[i] = usrp_dev->get_rx_gain(i); - } - - return; - -} - -void uhd_device::set_rates() -{ - dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)); - if (desc.mcr != 0.0) - usrp_dev->set_master_clock_rate(desc.mcr); - - tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate; - rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate; - - usrp_dev->set_tx_rate(tx_rate); - usrp_dev->set_rx_rate(rx_rate); - tx_rate = usrp_dev->get_tx_rate(); - rx_rate = usrp_dev->get_rx_rate(); - - ts_offset = static_cast(desc.offset * rx_rate); - LOG(INFO) << "Rates configured for " << desc.str; -} - -bool uhd_device::set_antennas() -{ - unsigned int i; - - for (i = 0; i < tx_paths.size(); i++) { - if (tx_paths[i] == "") - continue; - LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; - if (!setTxAntenna(tx_paths[i], i)) { - LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; - return false; - } - } - - for (i = 0; i < rx_paths.size(); i++) { - if (rx_paths[i] == "") - continue; - LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; - if (!setRxAntenna(rx_paths[i], i)) { - LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; - return false; - } - } - LOG(INFO) << "Antennas configured successfully"; - return true; -} - -double uhd_device::setTxGain(double db, size_t chan) -{ - if (iface == MULTI_ARFCN) - chan = 0; - - if (chan >= tx_gains.size()) { - LOG(ALERT) << "Requested non-existent channel" << chan; - return 0.0f; - } - - if (dev_type == UMTRX) { - std::vector gain_stages = usrp_dev->get_tx_gain_names(0); - if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { - usrp_dev->set_tx_gain(db, chan); - } else { - // New UHD versions support split configuration of - // Tx gain stages. We utilize this to set the gain - // configuration, optimal for the Tx signal quality. - // From our measurements, VGA1 must be 18dB plus-minus - // one and VGA2 is the best when 23dB or lower. - usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan); - usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan); - } - } else { - usrp_dev->set_tx_gain(db, chan); - } - - tx_gains[chan] = usrp_dev->get_tx_gain(chan); - - LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)"; - - return tx_gains[chan]; -} - -double uhd_device::setRxGain(double db, size_t chan) -{ - if (chan >= rx_gains.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return 0.0f; - } - - usrp_dev->set_rx_gain(db, chan); - rx_gains[chan] = usrp_dev->get_rx_gain(chan); - - LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)"; - - return rx_gains[chan]; -} - -double uhd_device::getRxGain(size_t chan) -{ - if (iface == MULTI_ARFCN) - chan = 0; - - if (chan >= rx_gains.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return 0.0f; - } - - return rx_gains[chan]; -} - -/* - Parse the UHD device tree and mboard name to find out what device we're - dealing with. We need the window type so that the transceiver knows how to - deal with the transport latency. Reject the USRP1 because UHD doesn't - support timestamped samples with it. - */ -bool uhd_device::parse_dev_type() -{ - uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree(); - std::string devString = prop_tree->access("/name").get(); - std::string mboardString = usrp_dev->get_mboard_name(); - - const std::map> devStringMap { - { "B100", { B100, TX_WINDOW_USRP1 } }, - { "B200", { B200, TX_WINDOW_USRP1 } }, - { "B200mini", { B200, TX_WINDOW_USRP1 } }, - { "B205mini", { B200, TX_WINDOW_USRP1 } }, - { "B210", { B210, TX_WINDOW_USRP1 } }, - { "E100", { E1XX, TX_WINDOW_FIXED } }, - { "E110", { E1XX, TX_WINDOW_FIXED } }, - { "E310", { E3XX, TX_WINDOW_FIXED } }, - { "E3XX", { E3XX, TX_WINDOW_FIXED } }, - { "X300", { X3XX, TX_WINDOW_FIXED } }, - { "X310", { X3XX, TX_WINDOW_FIXED } }, - { "USRP2", { USRP2, TX_WINDOW_FIXED } }, - { "UmTRX", { UMTRX, TX_WINDOW_FIXED } }, - { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } }, - }; - - // Compare UHD motherboard and device strings */ - auto mapIter = devStringMap.begin(); - while (mapIter != devStringMap.end()) { - if (devString.find(mapIter->first) != std::string::npos || - mboardString.find(mapIter->first) != std::string::npos) { - dev_type = std::get<0>(mapIter->second); - tx_window = std::get<1>(mapIter->second); - return true; - } - mapIter++; - } - - LOG(ALERT) << "Unsupported device " << devString; - return false; -} - -/* - * Check for UHD version > 3.9.0 for E3XX support - */ -static bool uhd_e3xx_version_chk() -{ - std::string ver = uhd::get_version_string(); - std::string major_str(ver.begin(), ver.begin() + 3); - std::string minor_str(ver.begin() + 4, ver.begin() + 7); - - int major_val = atoi(major_str.c_str()); - int minor_val = atoi(minor_str.c_str()); - - if (major_val < 3) - return false; - if (minor_val < 9) - return false; - - return true; -} - -void uhd_device::set_channels(bool swap) -{ - if (iface == MULTI_ARFCN) { - if (dev_type != B200 && dev_type != B210) - throw std::invalid_argument("Device does not support MCBTS"); - dev_type = B2XX_MCBTS; - chans = 1; - } - - if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels) - throw std::invalid_argument("Device does not support number of requested channels"); - - std::string subdev_string; - switch (dev_type) { - case B210: - case E3XX: - if (chans == 1) - subdev_string = swap ? "A:B" : "A:A"; - else if (chans == 2) - subdev_string = swap ? "A:B A:A" : "A:A A:B"; - break; - case X3XX: - case UMTRX: - if (chans == 1) - subdev_string = swap ? "B:0" : "A:0"; - else if (chans == 2) - subdev_string = swap ? "B:0 A:0" : "A:0 B:0"; - break; - default: - break; - } - - if (!subdev_string.empty()) { - uhd::usrp::subdev_spec_t spec(subdev_string); - usrp_dev->set_tx_subdev_spec(spec); - usrp_dev->set_rx_subdev_spec(spec); - } -} - -int uhd_device::open(const std::string &args, int ref, bool swap_channels) -{ - const char *refstr; - - // Find UHD devices - uhd::device_addr_t addr(args); - uhd::device_addrs_t dev_addrs = uhd::device::find(addr); - if (dev_addrs.size() == 0) { - LOG(ALERT) << "No UHD devices found with address '" << args << "'"; - return -1; - } - - // Use the first found device - LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string(); - try { - usrp_dev = uhd::usrp::multi_usrp::make(addr); - } catch(...) { - LOG(ALERT) << "UHD make failed, device " << args; - return -1; - } - - // Check for a valid device type and set bus type - if (!parse_dev_type()) - return -1; - - if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) { - LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater"; - return -1; - } - - try { - set_channels(swap_channels); - } catch (const std::exception &e) { - LOG(ALERT) << "Channel setting failed - " << e.what(); - return -1; - } - - if (!set_antennas()) { - LOG(ALERT) << "UHD antenna setting failed"; - return -1; - } - - tx_freqs.resize(chans); - rx_freqs.resize(chans); - tx_gains.resize(chans); - rx_gains.resize(chans); - rx_buffers.resize(chans); - - switch (ref) { - case REF_INTERNAL: - refstr = "internal"; - break; - case REF_EXTERNAL: - refstr = "external"; - break; - case REF_GPS: - refstr = "gpsdo"; - break; - default: - LOG(ALERT) << "Invalid reference type"; - return -1; - } - - usrp_dev->set_clock_source(refstr); - - try { - set_rates(); - } catch (const std::exception &e) { - LOG(ALERT) << "UHD rate setting failed - " << e.what(); - return -1; - } - - // Set RF frontend bandwidth - if (dev_type == UMTRX) { - // Setting LMS6002D LPF to 500kHz gives us the best signal quality - for (size_t i = 0; i < chans; i++) { - usrp_dev->set_tx_bandwidth(500*1000*2, i); - usrp_dev->set_rx_bandwidth(500*1000*2, i); - } - } else if (dev_type == LIMESDR) { - for (size_t i = 0; i < chans; i++) { - usrp_dev->set_tx_bandwidth(5e6, i); - usrp_dev->set_rx_bandwidth(5e6, i); - } - } - - /* Create TX and RX streamers */ - uhd::stream_args_t stream_args("sc16"); - for (size_t i = 0; i < chans; i++) - stream_args.channels.push_back(i); - - tx_stream = usrp_dev->get_tx_stream(stream_args); - rx_stream = usrp_dev->get_rx_stream(stream_args); - - /* Number of samples per over-the-wire packet */ - tx_spp = tx_stream->get_max_num_samps(); - rx_spp = rx_stream->get_max_num_samps(); - - // Create receive buffer - size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); - for (size_t i = 0; i < rx_buffers.size(); i++) - rx_buffers[i] = new smpl_buf(buf_len, rx_rate); - - // Initialize and shadow gain values - init_gains(); - - // Print configuration - LOG(INFO) << "\n" << usrp_dev->get_pp_string(); - - if (iface == MULTI_ARFCN) - return MULTI_ARFCN; - - switch (dev_type) { - case B100: - return RESAMP_64M; - case USRP2: - case X3XX: - return RESAMP_100M; - case B200: - case B210: - case E1XX: - case E3XX: - case LIMESDR: - default: - break; - } - - return NORMAL; -} - -bool uhd_device::flush_recv(size_t num_pkts) -{ - uhd::rx_metadata_t md; - size_t num_smpls; - float timeout = UHD_RESTART_TIMEOUT; - - std::vector > - pkt_bufs(chans, std::vector(2 * rx_spp)); - - std::vector pkt_ptrs; - for (size_t i = 0; i < pkt_bufs.size(); i++) - pkt_ptrs.push_back(&pkt_bufs[i].front()); - - ts_initial = 0; - while (!ts_initial || (num_pkts-- > 0)) { - num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md, - timeout, true); - if (!num_smpls) { - switch (md.error_code) { - case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: - LOG(ALERT) << "Device timed out"; - return false; - default: - continue; - } - } - - ts_initial = md.time_spec.to_ticks(rx_rate); - } - - LOG(INFO) << "Initial timestamp " << ts_initial << std::endl; - - return true; -} - -bool uhd_device::restart() -{ - /* Allow 100 ms delay to align multi-channel streams */ - double delay = 0.1; - - aligned = false; - - uhd::time_spec_t current = usrp_dev->get_time_now(); - - uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; - cmd.stream_now = false; - cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay); - - usrp_dev->issue_stream_cmd(cmd); - - return flush_recv(10); -} - -bool uhd_device::start() -{ - LOG(INFO) << "Starting USRP..."; - - if (started) { - LOG(ERR) << "Device already started"; - return false; - } - -#ifndef USE_UHD_3_11 - // Register msg handler - uhd::msg::register_handler(&uhd_msg_handler); -#endif - // Start asynchronous event (underrun check) loop - async_event_thrd = new Thread(); - async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this); - - // Start streaming - if (!restart()) - return false; - - // 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() -{ - if (!started) - return false; - - uhd::stream_cmd_t stream_cmd = - uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; - - usrp_dev->issue_stream_cmd(stream_cmd); - - async_event_thrd->cancel(); - async_event_thrd->join(); - delete async_event_thrd; - - started = false; - return true; -} - -void uhd_device::setPriority(float prio) -{ - uhd::set_thread_priority_safe(prio); - return; -} - -int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls) -{ - if (!num_smpls) { - LOG(ERR) << str_code(md); - - switch (md.error_code) { - case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: - LOG(ALERT) << "UHD: Receive timed out"; - return ERROR_TIMEOUT; - case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: - case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: - case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN: - case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET: - default: - return ERROR_UNHANDLED; - } - } - - // Missing timestamp - if (!md.has_time_spec) { - LOG(ALERT) << "UHD: Received packet missing timestamp"; - return ERROR_UNRECOVERABLE; - } - - // Monotonicity check - if (md.time_spec < prev_ts) { - LOG(ALERT) << "UHD: Loss of monotonic time"; - LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", " - << "Previous time: " << prev_ts.get_real_secs(); - return ERROR_TIMING; - } - - // Workaround for UHD tick rounding bug - TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate); - if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1) - md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate); - - prev_ts = md.time_spec; - - return 0; -} - -int uhd_device::readSamples(std::vector &bufs, int len, bool *overrun, - TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) -{ - ssize_t rc; - uhd::time_spec_t ts; - uhd::rx_metadata_t metadata; - - if (bufs.size() != chans) { - LOG(ALERT) << "Invalid channel combination " << bufs.size(); - return -1; - } - - *overrun = false; - *underrun = false; - - // Shift read time with respect to transmit clock - timestamp += ts_offset; - - ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate); - LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs(); - - // Check that timestamp is valid - rc = rx_buffers[0]->avail_smpls(timestamp); - if (rc < 0) { - LOG(ERR) << rx_buffers[0]->str_code(rc); - LOG(ERR) << rx_buffers[0]->str_status(timestamp); - return 0; - } - - // Create vector buffer - std::vector > - pkt_bufs(chans, std::vector(2 * rx_spp)); - - std::vector pkt_ptrs; - for (size_t i = 0; i < pkt_bufs.size(); i++) - pkt_ptrs.push_back(&pkt_bufs[i].front()); - - // Receive samples from the usrp until we have enough - while (rx_buffers[0]->avail_smpls(timestamp) < len) { - thread_enable_cancel(false); - size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, - metadata, 0.1, true); - thread_enable_cancel(true); - - rx_pkt_cnt++; - - // Check for errors - rc = check_rx_md_err(metadata, num_smpls); - switch (rc) { - case ERROR_UNRECOVERABLE: - LOG(ALERT) << "UHD: Version " << uhd::get_version_string(); - LOG(ALERT) << "UHD: Unrecoverable error, exiting..."; - exit(-1); - case ERROR_TIMEOUT: - // Assume stopping condition - return 0; - case ERROR_TIMING: - restart(); - case ERROR_UNHANDLED: - continue; - } - - ts = metadata.time_spec; - LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs(); - - for (size_t i = 0; i < rx_buffers.size(); i++) { - rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(), - num_smpls, - metadata.time_spec); - - // Continue on local overrun, exit on other errors - if ((rc < 0)) { - LOG(ERR) << rx_buffers[i]->str_code(rc); - LOG(ERR) << rx_buffers[i]->str_status(timestamp); - if (rc != smpl_buf::ERROR_OVERFLOW) - return 0; - } - } - } - - // We have enough samples - for (size_t i = 0; i < rx_buffers.size(); i++) { - rc = rx_buffers[i]->read(bufs[i], len, timestamp); - if ((rc < 0) || (rc != len)) { - LOG(ERR) << rx_buffers[i]->str_code(rc); - LOG(ERR) << rx_buffers[i]->str_status(timestamp); - return 0; - } - } - - return len; -} - -int uhd_device::writeSamples(std::vector &bufs, 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 = uhd::time_spec_t::from_ticks(timestamp, tx_rate); - - *underrun = false; - - // No control packets - if (isControl) { - LOG(ERR) << "Control packets not supported"; - return 0; - } - - if (bufs.size() != chans) { - LOG(ALERT) << "Invalid channel combination " << bufs.size(); - return -1; - } - - // Drop a fixed number of packets (magic value) - if (!aligned) { - drop_cnt++; - - if (drop_cnt == 1) { - LOG(DEBUG) << "Aligning transmitter: stop burst"; - *underrun = true; - metadata.end_of_burst = true; - } else if (drop_cnt < 30) { - LOG(DEBUG) << "Aligning transmitter: packet advance"; - return len; - } else { - LOG(DEBUG) << "Aligning transmitter: start burst"; - metadata.start_of_burst = true; - aligned = true; - drop_cnt = 0; - } - } - - thread_enable_cancel(false); - size_t num_smpls = tx_stream->send(bufs, len, metadata); - thread_enable_cancel(true); - - if (num_smpls != (unsigned) len) { - LOG(ALERT) << "UHD: Device send timed out"; - } - - return num_smpls; -} - -bool uhd_device::updateAlignment(TIMESTAMP timestamp) -{ - return true; -} - -uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) -{ - double rf_spread, rf_freq; - std::vector freqs; - uhd::tune_request_t treq(freq); - - if (dev_type == UMTRX) { - if (offset != 0.0) - return uhd::tune_request_t(freq, offset); - - // Don't use DSP tuning, because LMS6002D PLL steps are small enough. - // We end up with DSP tuning just for 2-3Hz, which is meaningless and - // only distort the signal (because cordic is not ideal). - treq.target_freq = freq; - treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; - treq.rf_freq = freq; - treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL; - treq.dsp_freq = 0.0; - return treq; - } else if (chans == 1) { - if (offset == 0.0) - return treq; - - return uhd::tune_request_t(freq, offset); - } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) { - LOG(ALERT) << chans << " channels unsupported"; - return treq; - } - - if (tx) - freqs = tx_freqs; - else - freqs = rx_freqs; - - /* Tune directly if other channel isn't tuned */ - if (freqs[!chan] < 10.0) - return treq; - - /* Find center frequency between channels */ - rf_spread = fabs(freqs[!chan] - freq); - if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { - LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n"; - return treq; - } - - rf_freq = (freqs[!chan] + freq) / 2.0f; - - treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; - treq.target_freq = freq; - treq.rf_freq = rf_freq; - - return treq; -} - -bool uhd_device::set_freq(double freq, size_t chan, bool tx) -{ - std::vector freqs; - uhd::tune_result_t tres; - uhd::tune_request_t treq = select_freq(freq, chan, tx); - - if (tx) { - tres = usrp_dev->set_tx_freq(treq, chan); - tx_freqs[chan] = usrp_dev->get_tx_freq(chan); - } else { - tres = usrp_dev->set_rx_freq(treq, chan); - rx_freqs[chan] = usrp_dev->get_rx_freq(chan); - } - LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; - - if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) - return true; - - /* Manual RF policy means we intentionally tuned with a baseband - * offset for dual-channel purposes. Now retune the other channel - * with the opposite corresponding frequency offset - */ - if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) { - if (tx) { - treq = select_freq(tx_freqs[!chan], !chan, true); - tres = usrp_dev->set_tx_freq(treq, !chan); - tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan); - } else { - treq = select_freq(rx_freqs[!chan], !chan, false); - tres = usrp_dev->set_rx_freq(treq, !chan); - rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan); - - } - LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; - } - - return true; -} - -bool uhd_device::setTxFreq(double wFreq, size_t chan) -{ - if (chan >= tx_freqs.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - ScopedLock lock(tune_lock); - - return set_freq(wFreq, chan, true); -} - -bool uhd_device::setRxFreq(double wFreq, size_t chan) -{ - if (chan >= rx_freqs.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - ScopedLock lock(tune_lock); - - return set_freq(wFreq, chan, false); -} - -double uhd_device::getTxFreq(size_t chan) -{ - if (chan >= tx_freqs.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return 0.0; - } - - return tx_freqs[chan]; -} - -double uhd_device::getRxFreq(size_t chan) -{ - if (chan >= rx_freqs.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return 0.0; - } - - return rx_freqs[chan]; -} - -bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) -{ - std::vector avail; - if (chan >= rx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - - avail = usrp_dev->get_rx_antennas(chan); - if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { - LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; - LOG(INFO) << "Available Rx antennas: "; - for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) - LOG(INFO) << "- '" << *i << "'"; - return false; - } - usrp_dev->set_rx_antenna(ant, chan); - rx_paths[chan] = usrp_dev->get_rx_antenna(chan); - - if (ant != rx_paths[chan]) { - LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; - return false; - } - - return true; -} - -std::string uhd_device::getRxAntenna(size_t chan) -{ - if (chan >= rx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return ""; - } - return usrp_dev->get_rx_antenna(chan); -} - -bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) -{ - std::vector avail; - if (chan >= tx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - - avail = usrp_dev->get_tx_antennas(chan); - if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { - LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; - LOG(INFO) << "Available Tx antennas: "; - for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) - LOG(INFO) << "- '" << *i << "'"; - return false; - } - usrp_dev->set_tx_antenna(ant, chan); - tx_paths[chan] = usrp_dev->get_tx_antenna(chan); - - if (ant != tx_paths[chan]) { - LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; - return false; - } - - return true; -} - -std::string uhd_device::getTxAntenna(size_t chan) -{ - if (chan >= tx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return ""; - } - return usrp_dev->get_tx_antenna(chan); -} - -/* - * Only allow sampling the Rx path lower than Tx and not vice-versa. - * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed - * combination. - */ -TIMESTAMP uhd_device::initialWriteTimestamp() -{ - if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps)) - return ts_initial; - else - return ts_initial * tx_sps; -} - -TIMESTAMP uhd_device::initialReadTimestamp() -{ - return ts_initial; -} - -double uhd_device::fullScaleInputValue() -{ - if (dev_type == LIMESDR) - return (double) SHRT_MAX * LIMESDR_TX_AMPL; - if (dev_type == UMTRX) - return (double) SHRT_MAX * UMTRX_TX_AMPL; - else - return (double) SHRT_MAX * USRP_TX_AMPL; -} - -double uhd_device::fullScaleOutputValue() -{ - return (double) SHRT_MAX; -} - -bool uhd_device::recv_async_msg() -{ - uhd::async_metadata_t md; - - thread_enable_cancel(false); - bool rc = usrp_dev->get_device()->recv_async_msg(md); - thread_enable_cancel(true); - if (!rc) - return false; - - // Assume that any error requires resynchronization - if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { - aligned = false; - - if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) && - (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) { - LOG(ERR) << str_code(md); - } - } - - 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_ALIGNMENT: - ost << "Multi-channel alignment failed"; - 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(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) { - LOG(ERR) << "Overwriting old buffer data: timestamp="<& tx_paths, - const std::vector& rx_paths) -{ - return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths); -} diff --git a/Transceiver52M/USRPDevice.cpp b/Transceiver52M/USRPDevice.cpp deleted file mode 100644 index f7f24e9..0000000 --- a/Transceiver52M/USRPDevice.cpp +++ /dev/null @@ -1,648 +0,0 @@ -/* -* Copyright 2008, 2009 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 . - -*/ - - -/* - Compilation Flags - - SWLOOPBACK compile for software loopback testing -*/ - - -#include -#include -#include -#include "Logger.h" -#include "Threads.h" -#include "USRPDevice.h" - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -using namespace std; - -enum dboardConfigType { - TXA_RXB, - TXB_RXA, - TXA_RXA, - TXB_RXB -}; - -#ifdef SINGLEDB -const dboardConfigType dboardConfig = TXA_RXA; -#else -const dboardConfigType dboardConfig = TXA_RXB; -#endif - -const double USRPDevice::masterClockRate = 52.0e6; - -USRPDevice::USRPDevice(size_t sps) -{ - LOG(INFO) << "creating USRP device..."; - - this->sps = sps; - decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); - actualSampleRate = masterClockRate/decimRate; - rxGain = 0; - - /* - * Undetermined delay b/w ping response timestamp and true - * receive timestamp. Values are empirically measured. With - * split sample rate Tx/Rx - 4/1 sps we need to need to - * compensate for advance rather than delay. - */ - if (sps == 1) - pingOffset = 272; - else if (sps == 4) - pingOffset = 269 - 7500; - else - pingOffset = 0; - -#ifdef SWLOOPBACK - samplePeriod = 1.0e6/actualSampleRate; - loopbackBufferSize = 0; - gettimeofday(&lastReadTime,NULL); - firstRead = false; -#endif -} - -int USRPDevice::open(const std::string &, int, bool) -{ - writeLock.unlock(); - - LOG(INFO) << "opening USRP device.."; -#ifndef SWLOOPBACK - string rbf = "std_inband.rbf"; - //string rbf = "inband_1rxhb_1tx.rbf"; - m_uRx.reset(); - if (!skipRx) { - try { - m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make( - 0, decimRate * sps, 1, -1, - usrp_standard_rx::FPGA_MODE_NORMAL, - 1024, 16 * 8, rbf)); - m_uRx->set_fpga_master_clock_freq(masterClockRate); - } - - catch(...) { - LOG(ALERT) << "make failed on Rx"; - m_uRx.reset(); - return -1; - } - - if (m_uRx->fpga_master_clock_freq() != masterClockRate) - { - LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() - << ", desired clock freq = " << masterClockRate; - m_uRx.reset(); - return -1; - } - } - - try { - m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make( - 0, decimRate * 2, 1, -1, - 1024, 16 * 8, rbf)); - m_uTx->set_fpga_master_clock_freq(masterClockRate); - } - - catch(...) { - LOG(ALERT) << "make failed on Tx"; - m_uTx.reset(); - return -1; - } - - if (m_uTx->fpga_master_clock_freq() != masterClockRate) - { - LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() - << ", desired clock freq = " << masterClockRate; - m_uTx.reset(); - return -1; - } - - if (!skipRx) m_uRx->stop(); - m_uTx->stop(); - -#endif - - switch (dboardConfig) { - case TXA_RXB: - txSubdevSpec = usrp_subdev_spec(0,0); - rxSubdevSpec = usrp_subdev_spec(1,0); - break; - case TXB_RXA: - txSubdevSpec = usrp_subdev_spec(1,0); - rxSubdevSpec = usrp_subdev_spec(0,0); - break; - case TXA_RXA: - txSubdevSpec = usrp_subdev_spec(0,0); - rxSubdevSpec = usrp_subdev_spec(0,0); - break; - case TXB_RXB: - txSubdevSpec = usrp_subdev_spec(1,0); - rxSubdevSpec = usrp_subdev_spec(1,0); - break; - default: - txSubdevSpec = usrp_subdev_spec(0,0); - rxSubdevSpec = usrp_subdev_spec(1,0); - } - - m_dbTx = m_uTx->selected_subdev(txSubdevSpec); - m_dbRx = m_uRx->selected_subdev(rxSubdevSpec); - - samplesRead = 0; - samplesWritten = 0; - started = false; - - return NORMAL; -} - - - -bool USRPDevice::start() -{ - LOG(INFO) << "starting USRP..."; -#ifndef SWLOOPBACK - if (!m_uRx && !skipRx) return false; - if (!m_uTx) return false; - - if (!skipRx) m_uRx->stop(); - m_uTx->stop(); - - writeLock.lock(); - // power up and configure daughterboards - m_dbTx->set_enable(true); - m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec)); - m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec)); - - if (!m_dbRx->select_rx_antenna(1)) - m_dbRx->select_rx_antenna(0); - - writeLock.unlock(); - - // Set gains to midpoint - setTxGain((minTxGain() + maxTxGain()) / 2); - setRxGain((minRxGain() + maxRxGain()) / 2); - - data = new short[currDataSize]; - dataStart = 0; - dataEnd = 0; - timeStart = 0; - timeEnd = 0; - timestampOffset = 0; - latestWriteTimestamp = 0; - lastPktTimestamp = 0; - hi32Timestamp = 0; - isAligned = false; - - - if (!skipRx) - started = (m_uRx->start() && m_uTx->start()); - else - started = m_uTx->start(); - return started; -#else - gettimeofday(&lastReadTime,NULL); - return true; -#endif -} - -bool USRPDevice::stop() -{ -#ifndef SWLOOPBACK - if (!m_uRx) return false; - if (!m_uTx) return false; - - delete[] currData; - - started = !(m_uRx->stop() && m_uTx->stop()); - return !started; -#else - return true; -#endif -} - -double USRPDevice::maxTxGain() -{ - return m_dbTx->gain_max(); -} - -double USRPDevice::minTxGain() -{ - return m_dbTx->gain_min(); -} - -double USRPDevice::maxRxGain() -{ - return m_dbRx->gain_max(); -} - -double USRPDevice::minRxGain() -{ - return m_dbRx->gain_min(); -} - -double USRPDevice::setTxGain(double dB, size_t chan) -{ - if (chan) { - LOG(ALERT) << "Invalid channel " << chan; - return 0.0; - } - - writeLock.lock(); - if (dB > maxTxGain()) - dB = maxTxGain(); - if (dB < minTxGain()) - dB = minTxGain(); - - LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; - - if (!m_dbTx->set_gain(dB)) - LOG(ERR) << "Error setting TX gain"; - - writeLock.unlock(); - - return dB; -} - - -double USRPDevice::setRxGain(double dB, size_t chan) -{ - if (chan) { - LOG(ALERT) << "Invalid channel " << chan; - return 0.0; - } - - dB = 47.0; - - writeLock.lock(); - if (dB > maxRxGain()) - dB = maxRxGain(); - if (dB < minRxGain()) - dB = minRxGain(); - - LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; - - if (!m_dbRx->set_gain(dB)) - LOG(ERR) << "Error setting RX gain"; - - writeLock.unlock(); - - return dB; -} - -bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) -{ - if (chan >= rx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - LOG(ALERT) << "Not implemented"; - return true; -} - -std::string USRPDevice::getRxAntenna(size_t chan) -{ - if (chan >= rx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return ""; - } - LOG(ALERT) << "Not implemented"; - return ""; -} - -bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) -{ - if (chan >= tx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return false; - } - LOG(ALERT) << "Not implemented"; - return true; -} - -std::string USRPDevice::getTxAntenna(size_t chan) -{ - if (chan >= tx_paths.size()) { - LOG(ALERT) << "Requested non-existent channel " << chan; - return ""; - } - LOG(ALERT) << "Not implemented"; - return ""; -} - - -// NOTE: Assumes sequential reads -int USRPDevice::readSamples(std::vector &bufs, int len, bool *overrun, - TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) -{ -#ifndef SWLOOPBACK - if (!m_uRx) - return 0; - - short *buf = bufs[0]; - - timestamp += timestampOffset; - - if (timestamp + len < timeStart) { - memset(buf,0,len*2*sizeof(short)); - return len; - } - - if (underrun) *underrun = false; - - uint32_t readBuf[2000]; - - while (1) { - //guestimate USB read size - int readLen=0; - { - int numSamplesNeeded = timestamp + len - timeEnd; - if (numSamplesNeeded <=0) break; - readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0)); - if (readLen > 8000) readLen= (8000/512)*512; - } - - // read USRP packets, parse and save A/D data as needed - readLen = m_uRx->read((void *)readBuf,readLen,overrun); - for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { - // tmpBuf points to start of a USB packet - uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4); - TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]); - uint32_t word0 = usrp_to_host_u32(tmpBuf[0]); - uint32_t chan = (word0 >> 16) & 0x1f; - unsigned payloadSz = word0 & 0x1ff; - LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; - - bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); - if (incrementHi32 && (timeStart!=0)) { - LOG(DEBUG) << "high 32 increment!!!"; - hi32Timestamp++; - } - pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; - lastPktTimestamp = pktTimestamp; - - if (chan == 0x01f) { - // control reply, check to see if its ping reply - uint32_t word2 = usrp_to_host_u32(tmpBuf[2]); - if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { - timestamp -= timestampOffset; - timestampOffset = pktTimestamp - pingTimestamp + pingOffset; - LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; - timestamp += timestampOffset; - isAligned = true; - } - continue; - } - if (chan != 0) { - LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; - continue; - } - if ((word0 >> 28) & 0x04) { - if (underrun) *underrun = true; - LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; - } - if (RSSI) *RSSI = (word0 >> 21) & 0x3f; - - if (!isAligned) continue; - - unsigned cursorStart = pktTimestamp - timeStart + dataStart; - while (cursorStart*2 > currDataSize) { - cursorStart -= currDataSize/2; - } - if (cursorStart*2 + payloadSz/2 > currDataSize) { - // need to circle around buffer - memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short)); - memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short)); - } - else { - memcpy(data+cursorStart*2,tmpBuf+2,payloadSz); - } - if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd) - timeEnd = pktTimestamp+payloadSz/2/sizeof(short); - - LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; - - } - } - - // copy desired data to buf - unsigned bufStart = dataStart+(timestamp-timeStart); - if (bufStart + len < currDataSize/2) { - LOG(DEBUG) << "bufStart: " << bufStart; - memcpy(buf,data+bufStart*2,len*2*sizeof(short)); - memset(data+bufStart*2,0,len*2*sizeof(short)); - } - else { - LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; - unsigned firstLength = (currDataSize/2-bufStart); - LOG(DEBUG) << "firstLength: " << firstLength; - memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short)); - memset(data+bufStart*2,0,firstLength*2*sizeof(short)); - memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short)); - memset(data,0,(len-firstLength)*2*sizeof(short)); - } - dataStart = (bufStart + len) % (currDataSize/2); - timeStart = timestamp + len; - - return len; - -#else - if (loopbackBufferSize < 2) return 0; - int numSamples = 0; - struct timeval currTime; - gettimeofday(&currTime,NULL); - double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 + - (currTime.tv_usec - lastReadTime.tv_usec); - if (timeElapsed < samplePeriod) {return 0;} - int numSamplesToRead = (int) floor(timeElapsed/samplePeriod); - if (numSamplesToRead < len) return 0; - - if (numSamplesToRead > len) numSamplesToRead = len; - if (numSamplesToRead > loopbackBufferSize/2) { - firstRead =false; - numSamplesToRead = loopbackBufferSize/2; - } - memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead); - loopbackBufferSize -= 2*numSamplesToRead; - memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead, - sizeof(short)*loopbackBufferSize); - numSamples = numSamplesToRead; - if (firstRead) { - int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod); - lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000; - lastReadTime.tv_usec = new_usec % 1000000; - } - else { - gettimeofday(&lastReadTime,NULL); - firstRead = true; - } - samplesRead += numSamples; - - return numSamples; -#endif -} - -int USRPDevice::writeSamples(std::vector &bufs, int len, - bool *underrun, unsigned long long timestamp, - bool isControl) -{ - writeLock.lock(); - -#ifndef SWLOOPBACK - if (!m_uTx) - return 0; - - short *buf = bufs[0]; - - static uint32_t outData[128*20]; - - for (int i = 0; i < len*2; i++) { - buf[i] = host_to_usrp_short(buf[i]); - } - - int numWritten = 0; - unsigned isStart = 1; - unsigned RSSI = 0; - unsigned CHAN = (isControl) ? 0x01f : 0x00; - len = len*2*sizeof(short); - int numPkts = (int) ceil((float)len/(float)504); - unsigned isEnd = (numPkts < 2); - uint32_t *outPkt = outData; - int pktNum = 0; - while (numWritten < len) { - // pkt is pointer to start of a USB packet - uint32_t *pkt = outPkt + pktNum*128; - isEnd = (len - numWritten <= 504); - unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504; - pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen; - pkt[1] = timestamp & 0x0ffffffffll; - memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen); - numWritten += payloadLen; - timestamp += payloadLen/2/sizeof(short); - isStart = 0; - pkt[0] = host_to_usrp_u32(pkt[0]); - pkt[1] = host_to_usrp_u32(pkt[1]); - pktNum++; - } - m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL); - - samplesWritten += len/2/sizeof(short); - writeLock.unlock(); - - return len/2/sizeof(short); -#else - int retVal = len; - memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len); - samplesWritten += retVal; - loopbackBufferSize += retVal*2; - - return retVal; -#endif -} - -bool USRPDevice::updateAlignment(TIMESTAMP timestamp) -{ -#ifndef SWLOOPBACK - short data[] = {0x00,0x02,0x00,0x00}; - uint32_t *wordPtr = (uint32_t *) data; - *wordPtr = host_to_usrp_u32(*wordPtr); - bool tmpUnderrun; - - std::vector buf(1, data); - if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) { - pingTimestamp = timestamp; - return true; - } - return false; -#else - return true; -#endif -} - -#ifndef SWLOOPBACK -bool USRPDevice::setTxFreq(double wFreq, size_t chan) -{ - usrp_tune_result result; - - if (chan) { - LOG(ALERT) << "Invalid channel " << chan; - return false; - } - - if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { - LOG(INFO) << "set TX: " << wFreq << std::endl - << " baseband freq: " << result.baseband_freq << std::endl - << " DDC freq: " << result.dxc_freq << std::endl - << " residual freq: " << result.residual_freq; - return true; - } - else { - LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl - << " baseband freq: " << result.baseband_freq << std::endl - << " DDC freq: " << result.dxc_freq << std::endl - << " residual freq: " << result.residual_freq; - return false; - } -} - -bool USRPDevice::setRxFreq(double wFreq, size_t chan) -{ - usrp_tune_result result; - - if (chan) { - LOG(ALERT) << "Invalid channel " << chan; - return false; - } - - if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { - LOG(INFO) << "set RX: " << wFreq << std::endl - << " baseband freq: " << result.baseband_freq << std::endl - << " DDC freq: " << result.dxc_freq << std::endl - << " residual freq: " << result.residual_freq; - return true; - } - else { - LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl - << " baseband freq: " << result.baseband_freq << std::endl - << " DDC freq: " << result.dxc_freq << std::endl - << " residual freq: " << result.residual_freq; - return false; - } - -} - -#else -bool USRPDevice::setTxFreq(double wFreq) { return true;}; -bool USRPDevice::setRxFreq(double wFreq) { return true;}; -#endif - -RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, - InterfaceType iface, size_t chans, double offset, - const std::vector& tx_paths, - const std::vector& rx_paths) -{ - return new USRPDevice(tx_sps); -} diff --git a/Transceiver52M/USRPDevice.h b/Transceiver52M/USRPDevice.h deleted file mode 100644 index f981643..0000000 --- a/Transceiver52M/USRPDevice.h +++ /dev/null @@ -1,204 +0,0 @@ -/* -* Copyright 2008 Free Software Foundation, Inc. -* -* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. -* -* This use of this software may be subject to additional restrictions. -* See the LEGAL file in the main directory for details. - - 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. - -*/ - -#ifndef _USRP_DEVICE_H_ -#define _USRP_DEVICE_H_ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include "radioDevice.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -typedef boost::shared_ptr usrp_standard_tx_sptr; -typedef boost::shared_ptr usrp_standard_rx_sptr; - -/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ -class USRPDevice: public RadioDevice { - -private: - - static const double masterClockRate; ///< the USRP clock rate - double desiredSampleRate; ///< the desired sampling rate - usrp_standard_rx_sptr m_uRx; ///< the USRP receiver - usrp_standard_tx_sptr m_uTx; ///< the USRP transmitter - - db_base_sptr m_dbRx; ///< rx daughterboard - db_base_sptr m_dbTx; ///< tx daughterboard - usrp_subdev_spec rxSubdevSpec; - usrp_subdev_spec txSubdevSpec; - - int sps; - double actualSampleRate; ///< the actual USRP sampling rate - unsigned int decimRate; ///< the USRP decimation rate - - unsigned long long samplesRead; ///< number of samples read from USRP - unsigned long long samplesWritten; ///< number of samples sent to USRP - - bool started; ///< flag indicates USRP has started - bool skipRx; ///< set if USRP is transmit-only. - - static const unsigned int currDataSize_log2 = 21; - static const unsigned long currDataSize = (1 << currDataSize_log2); - short *data; - unsigned long dataStart; - unsigned long dataEnd; - TIMESTAMP timeStart; - TIMESTAMP timeEnd; - bool isAligned; - - Mutex writeLock; - - short *currData; ///< internal data buffer when reading from USRP - TIMESTAMP currTimestamp; ///< timestamp of internal data buffer - unsigned currLen; ///< size of internal data buffer - - TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks - TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command - TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response - - long long pingOffset; - unsigned long hi32Timestamp; - unsigned long lastPktTimestamp; - - double rxGain; - -#ifdef SWLOOPBACK - short loopbackBuffer[1000000]; - int loopbackBufferSize; - double samplePeriod; - - struct timeval startTime; - struct timeval lastReadTime; - bool firstRead; -#endif - - public: - - /** Object constructor */ - USRPDevice(size_t sps); - - /** Instantiate the USRP */ - int open(const std::string &, int, bool); - - /** Start the USRP */ - bool start(); - - /** Stop the USRP */ - bool stop(); - - /** Set priority not supported */ - void setPriority(float prio = 0.5) { } - - enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; } - - /** - Read samples from the USRP. - @param buf preallocated buf to contain read result - @param len number of samples desired - @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough - @param timestamp The timestamp of the first samples to be read - @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough - @param RSSI The received signal strength of the read result - @return The number of samples actually read - */ - int readSamples(std::vector &buf, int len, bool *overrun, - TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL, - unsigned *RSSI = NULL); - /** - Write samples to the USRP. - @param buf Contains the data to be written. - @param len number of samples to write. - @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough - @param timestamp The timestamp of the first sample of the data buffer. - @param isControl Set if data is a control packet, e.g. a ping command - @return The number of samples actually written - */ - int writeSamples(std::vector &bufs, int len, bool *underrun, - TIMESTAMP timestamp = 0xffffffff, bool isControl = false); - - /** Update the alignment between the read and write timestamps */ - bool updateAlignment(TIMESTAMP timestamp); - - /** Set the transmitter frequency */ - bool setTxFreq(double wFreq, size_t chan = 0); - - /** Set the receiver frequency */ - bool setRxFreq(double wFreq, size_t chan = 0); - - /** Returns the starting write Timestamp*/ - TIMESTAMP initialWriteTimestamp(void) { return 20000;} - - /** Returns the starting read Timestamp*/ - TIMESTAMP initialReadTimestamp(void) { return 20000;} - - /** returns the full-scale transmit amplitude **/ - double fullScaleInputValue() {return 13500.0;} - - /** returns the full-scale receive amplitude **/ - double fullScaleOutputValue() {return 9450.0;} - - /** sets the receive chan gain, returns the gain setting **/ - double setRxGain(double dB, size_t chan = 0); - - /** get the current receive gain */ - double getRxGain(size_t chan = 0) { return rxGain; } - - /** return maximum Rx Gain **/ - double maxRxGain(void); - - /** return minimum Rx Gain **/ - double minRxGain(void); - - /** sets the transmit chan gain, returns the gain setting **/ - double setTxGain(double dB, size_t chan = 0); - - /** return maximum Tx Gain **/ - double maxTxGain(void); - - /** return minimum Rx Gain **/ - double minTxGain(void); - - /** sets the RX path to use, returns true if successful and false otherwise */ - bool setRxAntenna(const std::string &ant, size_t chan = 0); - - /* return the used RX path */ - std::string getRxAntenna(size_t chan = 0); - - /** sets the RX path to use, returns true if successful and false otherwise */ - bool setTxAntenna(const std::string &ant, size_t chan = 0); - - /* return the used RX path */ - std::string getTxAntenna(size_t chan = 0); - - /** Return internal status values */ - inline double getTxFreq(size_t chan = 0) { return 0; } - inline double getRxFreq(size_t chan = 0) { return 0; } - inline double getSampleRate() { return actualSampleRate; } - inline double numberRead() { return samplesRead; } - inline double numberWritten() { return samplesWritten; } - - std::vector tx_paths, rx_paths; -}; - -#endif // _USRP_DEVICE_H_ diff --git a/Transceiver52M/device/Makefile.am b/Transceiver52M/device/Makefile.am new file mode 100644 index 0000000..8575328 --- /dev/null +++ b/Transceiver52M/device/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/Makefile.common + +noinst_HEADERS = radioDevice.h + +SUBDIRS = + +if USRP1 +SUBDIRS += usrp1 +else +SUBDIRS += uhd +endif diff --git a/Transceiver52M/device/radioDevice.h b/Transceiver52M/device/radioDevice.h new file mode 100644 index 0000000..9913de0 --- /dev/null +++ b/Transceiver52M/device/radioDevice.h @@ -0,0 +1,160 @@ +/* +* Copyright 2008 Free Software Foundation, Inc. +* +* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + + 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. + +*/ + +#ifndef __RADIO_DEVICE_H__ +#define __RADIO_DEVICE_H__ + +#include +#include + +extern "C" { +#include "config_defs.h" +} + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#define GSMRATE (1625e3/6) +#define MCBTS_SPACING 800000.0 + +/** a 64-bit virtual timestamp for radio data */ +typedef unsigned long long TIMESTAMP; + +/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ +class RadioDevice { + + public: + /* Available transport bus types */ + enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; + + /* Radio interface types */ + enum InterfaceType { + NORMAL, + RESAMP_64M, + RESAMP_100M, + MULTI_ARFCN, + }; + + static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, + size_t chans = 1, double offset = 0.0, + const std::vector& tx_paths = std::vector(1, ""), + const std::vector& rx_paths = std::vector(1, "")); + + /** Initialize the USRP */ + virtual int open(const std::string &args, int ref, bool swap_channels)=0; + + virtual ~RadioDevice() { } + + /** Start the USRP */ + virtual bool start()=0; + + /** Stop the USRP */ + virtual bool stop()=0; + + /** Get the Tx window type */ + virtual enum TxWindowType getWindowType()=0; + + /** Enable thread priority */ + virtual void setPriority(float prio = 0.5) = 0; + + /** + Read samples from the radio. + @param buf preallocated buf to contain read result + @param len number of samples desired + @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough + @param timestamp The timestamp of the first samples to be read + @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough + @param RSSI The received signal strength of the read result + @return The number of samples actually read + */ + virtual int readSamples(std::vector &bufs, int len, bool *overrun, + TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0, + unsigned *RSSI = 0) = 0; + /** + Write samples to the radio. + @param buf Contains the data to be written. + @param len number of samples to write. + @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough + @param timestamp The timestamp of the first sample of the data buffer. + @param isControl Set if data is a control packet, e.g. a ping command + @return The number of samples actually written + */ + virtual int writeSamples(std::vector &bufs, int len, bool *underrun, + TIMESTAMP timestamp, bool isControl = false) = 0; + + /** Update the alignment between the read and write timestamps */ + virtual bool updateAlignment(TIMESTAMP timestamp)=0; + + /** Set the transmitter frequency */ + virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0; + + /** Set the receiver frequency */ + virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0; + + /** Returns the starting write Timestamp*/ + virtual TIMESTAMP initialWriteTimestamp(void)=0; + + /** Returns the starting read Timestamp*/ + virtual TIMESTAMP initialReadTimestamp(void)=0; + + /** returns the full-scale transmit amplitude **/ + virtual double fullScaleInputValue()=0; + + /** returns the full-scale receive amplitude **/ + virtual double fullScaleOutputValue()=0; + + /** sets the receive chan gain, returns the gain setting **/ + virtual double setRxGain(double dB, size_t chan = 0) = 0; + + /** gets the current receive gain **/ + virtual double getRxGain(size_t chan = 0) = 0; + + /** return maximum Rx Gain **/ + virtual double maxRxGain(void) = 0; + + /** return minimum Rx Gain **/ + virtual double minRxGain(void) = 0; + + /** sets the transmit chan gain, returns the gain setting **/ + virtual double setTxGain(double dB, size_t chan = 0) = 0; + + /** return maximum Tx Gain **/ + virtual double maxTxGain(void) = 0; + + /** return minimum Tx Gain **/ + virtual double minTxGain(void) = 0; + + /** sets the RX path to use, returns true if successful and false otherwise */ + virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0; + + /** return the used RX path */ + virtual std::string getRxAntenna(size_t chan = 0) = 0; + + /** sets the RX path to use, returns true if successful and false otherwise */ + virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0; + + /** return the used RX path */ + virtual std::string getTxAntenna(size_t chan = 0) = 0; + + /** Return internal status values */ + virtual double getTxFreq(size_t chan = 0) = 0; + virtual double getRxFreq(size_t chan = 0) = 0; + virtual double getSampleRate()=0; + virtual double numberRead()=0; + virtual double numberWritten()=0; + +}; + +#endif diff --git a/Transceiver52M/device/uhd/Makefile.am b/Transceiver52M/device/uhd/Makefile.am new file mode 100644 index 0000000..bb34d2f --- /dev/null +++ b/Transceiver52M/device/uhd/Makefile.am @@ -0,0 +1,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_LTLIBRARIES = libdevice.la + +libdevice_la_SOURCES = UHDDevice.cpp diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp new file mode 100644 index 0000000..4466da4 --- /dev/null +++ b/Transceiver52M/device/uhd/UHDDevice.cpp @@ -0,0 +1,1576 @@ +/* + * Device support for Ettus Research UHD driver + * + * Copyright 2010,2011 Free Software Foundation, Inc. + * Copyright (C) 2015 Ettus Research LLC + * + * 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 +#include "radioDevice.h" +#include "Threads.h" +#include "Logger.h" +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef USE_UHD_3_11 +#include +#endif + +#define USRP_TX_AMPL 0.3 +#define UMTRX_TX_AMPL 0.7 +#define LIMESDR_TX_AMPL 0.3 +#define SAMPLE_BUF_SZ (1 << 20) + +/* + * UHD timeout value on streaming (re)start + * + * Allow some time for streaming to commence after the start command is issued, + * but consider a wait beyond one second to be a definite error condition. + */ +#define UHD_RESTART_TIMEOUT 1.0 + +/* + * UmTRX specific settings + */ +#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 + */ +#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11) +#define B2XX_TIMING_1SPS 1.7153e-4 +#define B2XX_TIMING_4SPS 1.1696e-4 +#define B2XX_TIMING_4_4SPS 6.18462e-5 +#define B2XX_TIMING_MCBTS 7e-5 +#else +#define B2XX_TIMING_1SPS 9.9692e-5 +#define B2XX_TIMING_4SPS 6.9248e-5 +#define B2XX_TIMING_4_4SPS 4.52308e-5 +#define B2XX_TIMING_MCBTS 6.42452e-5 +#endif + +/* + * Tx / Rx sample offset values. In a perfect world, there is no group delay + * though analog components, and behaviour through digital filters exactly + * matches calculated values. In reality, there are unaccounted factors, + * which are captured in these empirically measured (using a loopback test) + * timing correction values. + * + * Notes: + * USRP1 with timestamps is not supported by UHD. + */ + +/* Device Type, Tx-SPS, Rx-SPS */ +typedef std::tuple dev_key; + +/* Device parameter descriptor */ +struct dev_desc { + unsigned channels; + double mcr; + double rate; + double offset; + std::string str; +}; + +static const std::map dev_param_map { + { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } }, + { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } }, + { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } }, + { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } }, + { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } }, + { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } }, + { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } }, + { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } }, + { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } }, + { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } }, + { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } }, + { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } }, + { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } }, + { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } }, + { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } }, + { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } }, + { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } }, + { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } }, + { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } }, + { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} }, + { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } }, + { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } }, + { 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); + + 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; + + size_t tx_sps, rx_sps, chans; + double tx_rate, rx_rate; + + double tx_gain_min, tx_gain_max; + double rx_gain_min, rx_gain_max; + double offset; + + std::vector tx_gains, rx_gains; + std::vector tx_freqs, rx_freqs; + std::vector tx_paths, rx_paths; + 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 set_antennas(); + 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; + InterfaceType iface; + Mutex tune_lock; +}; + +void *async_event_loop(uhd_device *dev) +{ + dev->setPriority(0.43); + + while (1) { + dev->recv_async_msg(); + pthread_testcancel(); + } + + return NULL; +} + +#ifndef USE_UHD_3_11 +/* + Catch and drop underrun 'U' and overrun 'O' messages from stdout + since we already report using the logging facility. Direct + everything else appropriately. + */ +void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg) +{ + switch (type) { + case uhd::msg::status: + LOG(INFO) << msg; + break; + case uhd::msg::warning: + LOG(WARNING) << msg; + break; + case uhd::msg::error: + LOG(ERR) << msg; + break; + case uhd::msg::fastpath: + break; + } +} +#endif + +static void thread_enable_cancel(bool cancel) +{ + cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); +} + +uhd_device::uhd_device(size_t tx_sps, size_t rx_sps, + InterfaceType iface, size_t chans, double offset, + const std::vector& tx_paths, + const std::vector& rx_paths) + : tx_gain_min(0.0), tx_gain_max(0.0), + rx_gain_min(0.0), rx_gain_max(0.0), + tx_spp(0), rx_spp(0), + started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), + prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) +{ + this->tx_sps = tx_sps; + this->rx_sps = rx_sps; + this->chans = chans; + this->offset = offset; + this->iface = iface; + this->tx_paths = tx_paths; + this->rx_paths = rx_paths; +} + +uhd_device::~uhd_device() +{ + stop(); + + for (size_t i = 0; i < rx_buffers.size(); i++) + delete rx_buffers[i]; +} + +void uhd_device::init_gains() +{ + uhd::gain_range_t range; + + if (dev_type == UMTRX) { + std::vector gain_stages = usrp_dev->get_tx_gain_names(0); + if (gain_stages[0] == "VGA") { + LOG(WARNING) << "Update your UHD version for a proper Tx gain support"; + } + if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { + range = usrp_dev->get_tx_gain_range(); + tx_gain_min = range.start(); + tx_gain_max = range.stop(); + } else { + range = usrp_dev->get_tx_gain_range("VGA2"); + tx_gain_min = UMTRX_VGA1_DEF + range.start(); + tx_gain_max = UMTRX_VGA1_DEF + range.stop(); + } + } else { + range = usrp_dev->get_tx_gain_range(); + tx_gain_min = range.start(); + tx_gain_max = range.stop(); + } + LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]"; + + range = usrp_dev->get_rx_gain_range(); + rx_gain_min = range.start(); + rx_gain_max = range.stop(); + LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; + + for (size_t i = 0; i < tx_gains.size(); i++) { + double gain = (tx_gain_min + tx_gain_max) / 2; + LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain; + usrp_dev->set_tx_gain(gain, i); + tx_gains[i] = usrp_dev->get_tx_gain(i); + } + + for (size_t i = 0; i < rx_gains.size(); i++) { + double gain = (rx_gain_min + rx_gain_max) / 2; + LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain; + usrp_dev->set_rx_gain(gain, i); + rx_gains[i] = usrp_dev->get_rx_gain(i); + } + + return; + +} + +void uhd_device::set_rates() +{ + dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)); + if (desc.mcr != 0.0) + usrp_dev->set_master_clock_rate(desc.mcr); + + tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate; + rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate; + + usrp_dev->set_tx_rate(tx_rate); + usrp_dev->set_rx_rate(rx_rate); + tx_rate = usrp_dev->get_tx_rate(); + rx_rate = usrp_dev->get_rx_rate(); + + ts_offset = static_cast(desc.offset * rx_rate); + LOG(INFO) << "Rates configured for " << desc.str; +} + +bool uhd_device::set_antennas() +{ + unsigned int i; + + for (i = 0; i < tx_paths.size(); i++) { + if (tx_paths[i] == "") + continue; + LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; + if (!setTxAntenna(tx_paths[i], i)) { + LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; + return false; + } + } + + for (i = 0; i < rx_paths.size(); i++) { + if (rx_paths[i] == "") + continue; + LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; + if (!setRxAntenna(rx_paths[i], i)) { + LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; + return false; + } + } + LOG(INFO) << "Antennas configured successfully"; + return true; +} + +double uhd_device::setTxGain(double db, size_t chan) +{ + if (iface == MULTI_ARFCN) + chan = 0; + + if (chan >= tx_gains.size()) { + LOG(ALERT) << "Requested non-existent channel" << chan; + return 0.0f; + } + + if (dev_type == UMTRX) { + std::vector gain_stages = usrp_dev->get_tx_gain_names(0); + if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { + usrp_dev->set_tx_gain(db, chan); + } else { + // New UHD versions support split configuration of + // Tx gain stages. We utilize this to set the gain + // configuration, optimal for the Tx signal quality. + // From our measurements, VGA1 must be 18dB plus-minus + // one and VGA2 is the best when 23dB or lower. + usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan); + usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan); + } + } else { + usrp_dev->set_tx_gain(db, chan); + } + + tx_gains[chan] = usrp_dev->get_tx_gain(chan); + + LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)"; + + return tx_gains[chan]; +} + +double uhd_device::setRxGain(double db, size_t chan) +{ + if (chan >= rx_gains.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return 0.0f; + } + + usrp_dev->set_rx_gain(db, chan); + rx_gains[chan] = usrp_dev->get_rx_gain(chan); + + LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)"; + + return rx_gains[chan]; +} + +double uhd_device::getRxGain(size_t chan) +{ + if (iface == MULTI_ARFCN) + chan = 0; + + if (chan >= rx_gains.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return 0.0f; + } + + return rx_gains[chan]; +} + +/* + Parse the UHD device tree and mboard name to find out what device we're + dealing with. We need the window type so that the transceiver knows how to + deal with the transport latency. Reject the USRP1 because UHD doesn't + support timestamped samples with it. + */ +bool uhd_device::parse_dev_type() +{ + uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree(); + std::string devString = prop_tree->access("/name").get(); + std::string mboardString = usrp_dev->get_mboard_name(); + + const std::map> devStringMap { + { "B100", { B100, TX_WINDOW_USRP1 } }, + { "B200", { B200, TX_WINDOW_USRP1 } }, + { "B200mini", { B200, TX_WINDOW_USRP1 } }, + { "B205mini", { B200, TX_WINDOW_USRP1 } }, + { "B210", { B210, TX_WINDOW_USRP1 } }, + { "E100", { E1XX, TX_WINDOW_FIXED } }, + { "E110", { E1XX, TX_WINDOW_FIXED } }, + { "E310", { E3XX, TX_WINDOW_FIXED } }, + { "E3XX", { E3XX, TX_WINDOW_FIXED } }, + { "X300", { X3XX, TX_WINDOW_FIXED } }, + { "X310", { X3XX, TX_WINDOW_FIXED } }, + { "USRP2", { USRP2, TX_WINDOW_FIXED } }, + { "UmTRX", { UMTRX, TX_WINDOW_FIXED } }, + { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } }, + }; + + // Compare UHD motherboard and device strings */ + auto mapIter = devStringMap.begin(); + while (mapIter != devStringMap.end()) { + if (devString.find(mapIter->first) != std::string::npos || + mboardString.find(mapIter->first) != std::string::npos) { + dev_type = std::get<0>(mapIter->second); + tx_window = std::get<1>(mapIter->second); + return true; + } + mapIter++; + } + + LOG(ALERT) << "Unsupported device " << devString; + return false; +} + +/* + * Check for UHD version > 3.9.0 for E3XX support + */ +static bool uhd_e3xx_version_chk() +{ + std::string ver = uhd::get_version_string(); + std::string major_str(ver.begin(), ver.begin() + 3); + std::string minor_str(ver.begin() + 4, ver.begin() + 7); + + int major_val = atoi(major_str.c_str()); + int minor_val = atoi(minor_str.c_str()); + + if (major_val < 3) + return false; + if (minor_val < 9) + return false; + + return true; +} + +void uhd_device::set_channels(bool swap) +{ + if (iface == MULTI_ARFCN) { + if (dev_type != B200 && dev_type != B210) + throw std::invalid_argument("Device does not support MCBTS"); + dev_type = B2XX_MCBTS; + chans = 1; + } + + if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels) + throw std::invalid_argument("Device does not support number of requested channels"); + + std::string subdev_string; + switch (dev_type) { + case B210: + case E3XX: + if (chans == 1) + subdev_string = swap ? "A:B" : "A:A"; + else if (chans == 2) + subdev_string = swap ? "A:B A:A" : "A:A A:B"; + break; + case X3XX: + case UMTRX: + if (chans == 1) + subdev_string = swap ? "B:0" : "A:0"; + else if (chans == 2) + subdev_string = swap ? "B:0 A:0" : "A:0 B:0"; + break; + default: + break; + } + + if (!subdev_string.empty()) { + uhd::usrp::subdev_spec_t spec(subdev_string); + usrp_dev->set_tx_subdev_spec(spec); + usrp_dev->set_rx_subdev_spec(spec); + } +} + +int uhd_device::open(const std::string &args, int ref, bool swap_channels) +{ + const char *refstr; + + // Find UHD devices + uhd::device_addr_t addr(args); + uhd::device_addrs_t dev_addrs = uhd::device::find(addr); + if (dev_addrs.size() == 0) { + LOG(ALERT) << "No UHD devices found with address '" << args << "'"; + return -1; + } + + // Use the first found device + LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string(); + try { + usrp_dev = uhd::usrp::multi_usrp::make(addr); + } catch(...) { + LOG(ALERT) << "UHD make failed, device " << args; + return -1; + } + + // Check for a valid device type and set bus type + if (!parse_dev_type()) + return -1; + + if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) { + LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater"; + return -1; + } + + try { + set_channels(swap_channels); + } catch (const std::exception &e) { + LOG(ALERT) << "Channel setting failed - " << e.what(); + return -1; + } + + if (!set_antennas()) { + LOG(ALERT) << "UHD antenna setting failed"; + return -1; + } + + tx_freqs.resize(chans); + rx_freqs.resize(chans); + tx_gains.resize(chans); + rx_gains.resize(chans); + rx_buffers.resize(chans); + + switch (ref) { + case REF_INTERNAL: + refstr = "internal"; + break; + case REF_EXTERNAL: + refstr = "external"; + break; + case REF_GPS: + refstr = "gpsdo"; + break; + default: + LOG(ALERT) << "Invalid reference type"; + return -1; + } + + usrp_dev->set_clock_source(refstr); + + try { + set_rates(); + } catch (const std::exception &e) { + LOG(ALERT) << "UHD rate setting failed - " << e.what(); + return -1; + } + + // Set RF frontend bandwidth + if (dev_type == UMTRX) { + // Setting LMS6002D LPF to 500kHz gives us the best signal quality + for (size_t i = 0; i < chans; i++) { + usrp_dev->set_tx_bandwidth(500*1000*2, i); + usrp_dev->set_rx_bandwidth(500*1000*2, i); + } + } else if (dev_type == LIMESDR) { + for (size_t i = 0; i < chans; i++) { + usrp_dev->set_tx_bandwidth(5e6, i); + usrp_dev->set_rx_bandwidth(5e6, i); + } + } + + /* Create TX and RX streamers */ + uhd::stream_args_t stream_args("sc16"); + for (size_t i = 0; i < chans; i++) + stream_args.channels.push_back(i); + + tx_stream = usrp_dev->get_tx_stream(stream_args); + rx_stream = usrp_dev->get_rx_stream(stream_args); + + /* Number of samples per over-the-wire packet */ + tx_spp = tx_stream->get_max_num_samps(); + rx_spp = rx_stream->get_max_num_samps(); + + // Create receive buffer + size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); + for (size_t i = 0; i < rx_buffers.size(); i++) + rx_buffers[i] = new smpl_buf(buf_len, rx_rate); + + // Initialize and shadow gain values + init_gains(); + + // Print configuration + LOG(INFO) << "\n" << usrp_dev->get_pp_string(); + + if (iface == MULTI_ARFCN) + return MULTI_ARFCN; + + switch (dev_type) { + case B100: + return RESAMP_64M; + case USRP2: + case X3XX: + return RESAMP_100M; + case B200: + case B210: + case E1XX: + case E3XX: + case LIMESDR: + default: + break; + } + + return NORMAL; +} + +bool uhd_device::flush_recv(size_t num_pkts) +{ + uhd::rx_metadata_t md; + size_t num_smpls; + float timeout = UHD_RESTART_TIMEOUT; + + std::vector > + pkt_bufs(chans, std::vector(2 * rx_spp)); + + std::vector pkt_ptrs; + for (size_t i = 0; i < pkt_bufs.size(); i++) + pkt_ptrs.push_back(&pkt_bufs[i].front()); + + ts_initial = 0; + while (!ts_initial || (num_pkts-- > 0)) { + num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md, + timeout, true); + if (!num_smpls) { + switch (md.error_code) { + case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: + LOG(ALERT) << "Device timed out"; + return false; + default: + continue; + } + } + + ts_initial = md.time_spec.to_ticks(rx_rate); + } + + LOG(INFO) << "Initial timestamp " << ts_initial << std::endl; + + return true; +} + +bool uhd_device::restart() +{ + /* Allow 100 ms delay to align multi-channel streams */ + double delay = 0.1; + + aligned = false; + + uhd::time_spec_t current = usrp_dev->get_time_now(); + + uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + cmd.stream_now = false; + cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay); + + usrp_dev->issue_stream_cmd(cmd); + + return flush_recv(10); +} + +bool uhd_device::start() +{ + LOG(INFO) << "Starting USRP..."; + + if (started) { + LOG(ERR) << "Device already started"; + return false; + } + +#ifndef USE_UHD_3_11 + // Register msg handler + uhd::msg::register_handler(&uhd_msg_handler); +#endif + // Start asynchronous event (underrun check) loop + async_event_thrd = new Thread(); + async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this); + + // Start streaming + if (!restart()) + return false; + + // 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() +{ + if (!started) + return false; + + uhd::stream_cmd_t stream_cmd = + uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS; + + usrp_dev->issue_stream_cmd(stream_cmd); + + async_event_thrd->cancel(); + async_event_thrd->join(); + delete async_event_thrd; + + started = false; + return true; +} + +void uhd_device::setPriority(float prio) +{ + uhd::set_thread_priority_safe(prio); + return; +} + +int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls) +{ + if (!num_smpls) { + LOG(ERR) << str_code(md); + + switch (md.error_code) { + case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: + LOG(ALERT) << "UHD: Receive timed out"; + return ERROR_TIMEOUT; + case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: + case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: + case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN: + case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET: + default: + return ERROR_UNHANDLED; + } + } + + // Missing timestamp + if (!md.has_time_spec) { + LOG(ALERT) << "UHD: Received packet missing timestamp"; + return ERROR_UNRECOVERABLE; + } + + // Monotonicity check + if (md.time_spec < prev_ts) { + LOG(ALERT) << "UHD: Loss of monotonic time"; + LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", " + << "Previous time: " << prev_ts.get_real_secs(); + return ERROR_TIMING; + } + + // Workaround for UHD tick rounding bug + TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate); + if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1) + md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate); + + prev_ts = md.time_spec; + + return 0; +} + +int uhd_device::readSamples(std::vector &bufs, int len, bool *overrun, + TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) +{ + ssize_t rc; + uhd::time_spec_t ts; + uhd::rx_metadata_t metadata; + + if (bufs.size() != chans) { + LOG(ALERT) << "Invalid channel combination " << bufs.size(); + return -1; + } + + *overrun = false; + *underrun = false; + + // Shift read time with respect to transmit clock + timestamp += ts_offset; + + ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate); + LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs(); + + // Check that timestamp is valid + rc = rx_buffers[0]->avail_smpls(timestamp); + if (rc < 0) { + LOG(ERR) << rx_buffers[0]->str_code(rc); + LOG(ERR) << rx_buffers[0]->str_status(timestamp); + return 0; + } + + // Create vector buffer + std::vector > + pkt_bufs(chans, std::vector(2 * rx_spp)); + + std::vector pkt_ptrs; + for (size_t i = 0; i < pkt_bufs.size(); i++) + pkt_ptrs.push_back(&pkt_bufs[i].front()); + + // Receive samples from the usrp until we have enough + while (rx_buffers[0]->avail_smpls(timestamp) < len) { + thread_enable_cancel(false); + size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, + metadata, 0.1, true); + thread_enable_cancel(true); + + rx_pkt_cnt++; + + // Check for errors + rc = check_rx_md_err(metadata, num_smpls); + switch (rc) { + case ERROR_UNRECOVERABLE: + LOG(ALERT) << "UHD: Version " << uhd::get_version_string(); + LOG(ALERT) << "UHD: Unrecoverable error, exiting..."; + exit(-1); + case ERROR_TIMEOUT: + // Assume stopping condition + return 0; + case ERROR_TIMING: + restart(); + case ERROR_UNHANDLED: + continue; + } + + ts = metadata.time_spec; + LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs(); + + for (size_t i = 0; i < rx_buffers.size(); i++) { + rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(), + num_smpls, + metadata.time_spec); + + // Continue on local overrun, exit on other errors + if ((rc < 0)) { + LOG(ERR) << rx_buffers[i]->str_code(rc); + LOG(ERR) << rx_buffers[i]->str_status(timestamp); + if (rc != smpl_buf::ERROR_OVERFLOW) + return 0; + } + } + } + + // We have enough samples + for (size_t i = 0; i < rx_buffers.size(); i++) { + rc = rx_buffers[i]->read(bufs[i], len, timestamp); + if ((rc < 0) || (rc != len)) { + LOG(ERR) << rx_buffers[i]->str_code(rc); + LOG(ERR) << rx_buffers[i]->str_status(timestamp); + return 0; + } + } + + return len; +} + +int uhd_device::writeSamples(std::vector &bufs, 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 = uhd::time_spec_t::from_ticks(timestamp, tx_rate); + + *underrun = false; + + // No control packets + if (isControl) { + LOG(ERR) << "Control packets not supported"; + return 0; + } + + if (bufs.size() != chans) { + LOG(ALERT) << "Invalid channel combination " << bufs.size(); + return -1; + } + + // Drop a fixed number of packets (magic value) + if (!aligned) { + drop_cnt++; + + if (drop_cnt == 1) { + LOG(DEBUG) << "Aligning transmitter: stop burst"; + *underrun = true; + metadata.end_of_burst = true; + } else if (drop_cnt < 30) { + LOG(DEBUG) << "Aligning transmitter: packet advance"; + return len; + } else { + LOG(DEBUG) << "Aligning transmitter: start burst"; + metadata.start_of_burst = true; + aligned = true; + drop_cnt = 0; + } + } + + thread_enable_cancel(false); + size_t num_smpls = tx_stream->send(bufs, len, metadata); + thread_enable_cancel(true); + + if (num_smpls != (unsigned) len) { + LOG(ALERT) << "UHD: Device send timed out"; + } + + return num_smpls; +} + +bool uhd_device::updateAlignment(TIMESTAMP timestamp) +{ + return true; +} + +uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) +{ + double rf_spread, rf_freq; + std::vector freqs; + uhd::tune_request_t treq(freq); + + if (dev_type == UMTRX) { + if (offset != 0.0) + return uhd::tune_request_t(freq, offset); + + // Don't use DSP tuning, because LMS6002D PLL steps are small enough. + // We end up with DSP tuning just for 2-3Hz, which is meaningless and + // only distort the signal (because cordic is not ideal). + treq.target_freq = freq; + treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; + treq.rf_freq = freq; + treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL; + treq.dsp_freq = 0.0; + return treq; + } else if (chans == 1) { + if (offset == 0.0) + return treq; + + return uhd::tune_request_t(freq, offset); + } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) { + LOG(ALERT) << chans << " channels unsupported"; + return treq; + } + + if (tx) + freqs = tx_freqs; + else + freqs = rx_freqs; + + /* Tune directly if other channel isn't tuned */ + if (freqs[!chan] < 10.0) + return treq; + + /* Find center frequency between channels */ + rf_spread = fabs(freqs[!chan] - freq); + if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { + LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n"; + return treq; + } + + rf_freq = (freqs[!chan] + freq) / 2.0f; + + treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL; + treq.target_freq = freq; + treq.rf_freq = rf_freq; + + return treq; +} + +bool uhd_device::set_freq(double freq, size_t chan, bool tx) +{ + std::vector freqs; + uhd::tune_result_t tres; + uhd::tune_request_t treq = select_freq(freq, chan, tx); + + if (tx) { + tres = usrp_dev->set_tx_freq(treq, chan); + tx_freqs[chan] = usrp_dev->get_tx_freq(chan); + } else { + tres = usrp_dev->set_rx_freq(treq, chan); + rx_freqs[chan] = usrp_dev->get_rx_freq(chan); + } + LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; + + if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) + return true; + + /* Manual RF policy means we intentionally tuned with a baseband + * offset for dual-channel purposes. Now retune the other channel + * with the opposite corresponding frequency offset + */ + if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) { + if (tx) { + treq = select_freq(tx_freqs[!chan], !chan, true); + tres = usrp_dev->set_tx_freq(treq, !chan); + tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan); + } else { + treq = select_freq(rx_freqs[!chan], !chan, false); + tres = usrp_dev->set_rx_freq(treq, !chan); + rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan); + + } + LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; + } + + return true; +} + +bool uhd_device::setTxFreq(double wFreq, size_t chan) +{ + if (chan >= tx_freqs.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + ScopedLock lock(tune_lock); + + return set_freq(wFreq, chan, true); +} + +bool uhd_device::setRxFreq(double wFreq, size_t chan) +{ + if (chan >= rx_freqs.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + ScopedLock lock(tune_lock); + + return set_freq(wFreq, chan, false); +} + +double uhd_device::getTxFreq(size_t chan) +{ + if (chan >= tx_freqs.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return 0.0; + } + + return tx_freqs[chan]; +} + +double uhd_device::getRxFreq(size_t chan) +{ + if (chan >= rx_freqs.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return 0.0; + } + + return rx_freqs[chan]; +} + +bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) +{ + std::vector avail; + if (chan >= rx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + + avail = usrp_dev->get_rx_antennas(chan); + if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { + LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; + LOG(INFO) << "Available Rx antennas: "; + for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) + LOG(INFO) << "- '" << *i << "'"; + return false; + } + usrp_dev->set_rx_antenna(ant, chan); + rx_paths[chan] = usrp_dev->get_rx_antenna(chan); + + if (ant != rx_paths[chan]) { + LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; + return false; + } + + return true; +} + +std::string uhd_device::getRxAntenna(size_t chan) +{ + if (chan >= rx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return ""; + } + return usrp_dev->get_rx_antenna(chan); +} + +bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) +{ + std::vector avail; + if (chan >= tx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + + avail = usrp_dev->get_tx_antennas(chan); + if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { + LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; + LOG(INFO) << "Available Tx antennas: "; + for (std::vector::const_iterator i = avail.begin(); i != avail.end(); ++i) + LOG(INFO) << "- '" << *i << "'"; + return false; + } + usrp_dev->set_tx_antenna(ant, chan); + tx_paths[chan] = usrp_dev->get_tx_antenna(chan); + + if (ant != tx_paths[chan]) { + LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; + return false; + } + + return true; +} + +std::string uhd_device::getTxAntenna(size_t chan) +{ + if (chan >= tx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return ""; + } + return usrp_dev->get_tx_antenna(chan); +} + +/* + * Only allow sampling the Rx path lower than Tx and not vice-versa. + * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed + * combination. + */ +TIMESTAMP uhd_device::initialWriteTimestamp() +{ + if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps)) + return ts_initial; + else + return ts_initial * tx_sps; +} + +TIMESTAMP uhd_device::initialReadTimestamp() +{ + return ts_initial; +} + +double uhd_device::fullScaleInputValue() +{ + if (dev_type == LIMESDR) + return (double) SHRT_MAX * LIMESDR_TX_AMPL; + if (dev_type == UMTRX) + return (double) SHRT_MAX * UMTRX_TX_AMPL; + else + return (double) SHRT_MAX * USRP_TX_AMPL; +} + +double uhd_device::fullScaleOutputValue() +{ + return (double) SHRT_MAX; +} + +bool uhd_device::recv_async_msg() +{ + uhd::async_metadata_t md; + + thread_enable_cancel(false); + bool rc = usrp_dev->get_device()->recv_async_msg(md); + thread_enable_cancel(true); + if (!rc) + return false; + + // Assume that any error requires resynchronization + if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { + aligned = false; + + if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) && + (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) { + LOG(ERR) << str_code(md); + } + } + + 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_ALIGNMENT: + ost << "Multi-channel alignment failed"; + 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(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) { + LOG(ERR) << "Overwriting old buffer data: timestamp="<& tx_paths, + const std::vector& rx_paths) +{ + return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths); +} diff --git a/Transceiver52M/device/usrp1/Makefile.am b/Transceiver52M/device/usrp1/Makefile.am new file mode 100644 index 0000000..d99874a --- /dev/null +++ b/Transceiver52M/device/usrp1/Makefile.am @@ -0,0 +1,10 @@ +include $(top_srcdir)/Makefile.common + +AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. +AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS) + +noinst_HEADERS = USRPDevice.h + +noinst_LTLIBRARIES = libdevice.la + +libdevice_la_SOURCES = USRPDevice.cpp diff --git a/Transceiver52M/device/usrp1/USRPDevice.cpp b/Transceiver52M/device/usrp1/USRPDevice.cpp new file mode 100644 index 0000000..f7f24e9 --- /dev/null +++ b/Transceiver52M/device/usrp1/USRPDevice.cpp @@ -0,0 +1,648 @@ +/* +* Copyright 2008, 2009 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 . + +*/ + + +/* + Compilation Flags + + SWLOOPBACK compile for software loopback testing +*/ + + +#include +#include +#include +#include "Logger.h" +#include "Threads.h" +#include "USRPDevice.h" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +using namespace std; + +enum dboardConfigType { + TXA_RXB, + TXB_RXA, + TXA_RXA, + TXB_RXB +}; + +#ifdef SINGLEDB +const dboardConfigType dboardConfig = TXA_RXA; +#else +const dboardConfigType dboardConfig = TXA_RXB; +#endif + +const double USRPDevice::masterClockRate = 52.0e6; + +USRPDevice::USRPDevice(size_t sps) +{ + LOG(INFO) << "creating USRP device..."; + + this->sps = sps; + decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); + actualSampleRate = masterClockRate/decimRate; + rxGain = 0; + + /* + * Undetermined delay b/w ping response timestamp and true + * receive timestamp. Values are empirically measured. With + * split sample rate Tx/Rx - 4/1 sps we need to need to + * compensate for advance rather than delay. + */ + if (sps == 1) + pingOffset = 272; + else if (sps == 4) + pingOffset = 269 - 7500; + else + pingOffset = 0; + +#ifdef SWLOOPBACK + samplePeriod = 1.0e6/actualSampleRate; + loopbackBufferSize = 0; + gettimeofday(&lastReadTime,NULL); + firstRead = false; +#endif +} + +int USRPDevice::open(const std::string &, int, bool) +{ + writeLock.unlock(); + + LOG(INFO) << "opening USRP device.."; +#ifndef SWLOOPBACK + string rbf = "std_inband.rbf"; + //string rbf = "inband_1rxhb_1tx.rbf"; + m_uRx.reset(); + if (!skipRx) { + try { + m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make( + 0, decimRate * sps, 1, -1, + usrp_standard_rx::FPGA_MODE_NORMAL, + 1024, 16 * 8, rbf)); + m_uRx->set_fpga_master_clock_freq(masterClockRate); + } + + catch(...) { + LOG(ALERT) << "make failed on Rx"; + m_uRx.reset(); + return -1; + } + + if (m_uRx->fpga_master_clock_freq() != masterClockRate) + { + LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() + << ", desired clock freq = " << masterClockRate; + m_uRx.reset(); + return -1; + } + } + + try { + m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make( + 0, decimRate * 2, 1, -1, + 1024, 16 * 8, rbf)); + m_uTx->set_fpga_master_clock_freq(masterClockRate); + } + + catch(...) { + LOG(ALERT) << "make failed on Tx"; + m_uTx.reset(); + return -1; + } + + if (m_uTx->fpga_master_clock_freq() != masterClockRate) + { + LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() + << ", desired clock freq = " << masterClockRate; + m_uTx.reset(); + return -1; + } + + if (!skipRx) m_uRx->stop(); + m_uTx->stop(); + +#endif + + switch (dboardConfig) { + case TXA_RXB: + txSubdevSpec = usrp_subdev_spec(0,0); + rxSubdevSpec = usrp_subdev_spec(1,0); + break; + case TXB_RXA: + txSubdevSpec = usrp_subdev_spec(1,0); + rxSubdevSpec = usrp_subdev_spec(0,0); + break; + case TXA_RXA: + txSubdevSpec = usrp_subdev_spec(0,0); + rxSubdevSpec = usrp_subdev_spec(0,0); + break; + case TXB_RXB: + txSubdevSpec = usrp_subdev_spec(1,0); + rxSubdevSpec = usrp_subdev_spec(1,0); + break; + default: + txSubdevSpec = usrp_subdev_spec(0,0); + rxSubdevSpec = usrp_subdev_spec(1,0); + } + + m_dbTx = m_uTx->selected_subdev(txSubdevSpec); + m_dbRx = m_uRx->selected_subdev(rxSubdevSpec); + + samplesRead = 0; + samplesWritten = 0; + started = false; + + return NORMAL; +} + + + +bool USRPDevice::start() +{ + LOG(INFO) << "starting USRP..."; +#ifndef SWLOOPBACK + if (!m_uRx && !skipRx) return false; + if (!m_uTx) return false; + + if (!skipRx) m_uRx->stop(); + m_uTx->stop(); + + writeLock.lock(); + // power up and configure daughterboards + m_dbTx->set_enable(true); + m_uTx->set_mux(m_uTx->determine_tx_mux_value(txSubdevSpec)); + m_uRx->set_mux(m_uRx->determine_rx_mux_value(rxSubdevSpec)); + + if (!m_dbRx->select_rx_antenna(1)) + m_dbRx->select_rx_antenna(0); + + writeLock.unlock(); + + // Set gains to midpoint + setTxGain((minTxGain() + maxTxGain()) / 2); + setRxGain((minRxGain() + maxRxGain()) / 2); + + data = new short[currDataSize]; + dataStart = 0; + dataEnd = 0; + timeStart = 0; + timeEnd = 0; + timestampOffset = 0; + latestWriteTimestamp = 0; + lastPktTimestamp = 0; + hi32Timestamp = 0; + isAligned = false; + + + if (!skipRx) + started = (m_uRx->start() && m_uTx->start()); + else + started = m_uTx->start(); + return started; +#else + gettimeofday(&lastReadTime,NULL); + return true; +#endif +} + +bool USRPDevice::stop() +{ +#ifndef SWLOOPBACK + if (!m_uRx) return false; + if (!m_uTx) return false; + + delete[] currData; + + started = !(m_uRx->stop() && m_uTx->stop()); + return !started; +#else + return true; +#endif +} + +double USRPDevice::maxTxGain() +{ + return m_dbTx->gain_max(); +} + +double USRPDevice::minTxGain() +{ + return m_dbTx->gain_min(); +} + +double USRPDevice::maxRxGain() +{ + return m_dbRx->gain_max(); +} + +double USRPDevice::minRxGain() +{ + return m_dbRx->gain_min(); +} + +double USRPDevice::setTxGain(double dB, size_t chan) +{ + if (chan) { + LOG(ALERT) << "Invalid channel " << chan; + return 0.0; + } + + writeLock.lock(); + if (dB > maxTxGain()) + dB = maxTxGain(); + if (dB < minTxGain()) + dB = minTxGain(); + + LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; + + if (!m_dbTx->set_gain(dB)) + LOG(ERR) << "Error setting TX gain"; + + writeLock.unlock(); + + return dB; +} + + +double USRPDevice::setRxGain(double dB, size_t chan) +{ + if (chan) { + LOG(ALERT) << "Invalid channel " << chan; + return 0.0; + } + + dB = 47.0; + + writeLock.lock(); + if (dB > maxRxGain()) + dB = maxRxGain(); + if (dB < minRxGain()) + dB = minRxGain(); + + LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; + + if (!m_dbRx->set_gain(dB)) + LOG(ERR) << "Error setting RX gain"; + + writeLock.unlock(); + + return dB; +} + +bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) +{ + if (chan >= rx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + LOG(ALERT) << "Not implemented"; + return true; +} + +std::string USRPDevice::getRxAntenna(size_t chan) +{ + if (chan >= rx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return ""; + } + LOG(ALERT) << "Not implemented"; + return ""; +} + +bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) +{ + if (chan >= tx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return false; + } + LOG(ALERT) << "Not implemented"; + return true; +} + +std::string USRPDevice::getTxAntenna(size_t chan) +{ + if (chan >= tx_paths.size()) { + LOG(ALERT) << "Requested non-existent channel " << chan; + return ""; + } + LOG(ALERT) << "Not implemented"; + return ""; +} + + +// NOTE: Assumes sequential reads +int USRPDevice::readSamples(std::vector &bufs, int len, bool *overrun, + TIMESTAMP timestamp, bool *underrun, unsigned *RSSI) +{ +#ifndef SWLOOPBACK + if (!m_uRx) + return 0; + + short *buf = bufs[0]; + + timestamp += timestampOffset; + + if (timestamp + len < timeStart) { + memset(buf,0,len*2*sizeof(short)); + return len; + } + + if (underrun) *underrun = false; + + uint32_t readBuf[2000]; + + while (1) { + //guestimate USB read size + int readLen=0; + { + int numSamplesNeeded = timestamp + len - timeEnd; + if (numSamplesNeeded <=0) break; + readLen = 512 * ((int) ceil((float) numSamplesNeeded/126.0)); + if (readLen > 8000) readLen= (8000/512)*512; + } + + // read USRP packets, parse and save A/D data as needed + readLen = m_uRx->read((void *)readBuf,readLen,overrun); + for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { + // tmpBuf points to start of a USB packet + uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4); + TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]); + uint32_t word0 = usrp_to_host_u32(tmpBuf[0]); + uint32_t chan = (word0 >> 16) & 0x1f; + unsigned payloadSz = word0 & 0x1ff; + LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; + + bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); + if (incrementHi32 && (timeStart!=0)) { + LOG(DEBUG) << "high 32 increment!!!"; + hi32Timestamp++; + } + pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; + lastPktTimestamp = pktTimestamp; + + if (chan == 0x01f) { + // control reply, check to see if its ping reply + uint32_t word2 = usrp_to_host_u32(tmpBuf[2]); + if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { + timestamp -= timestampOffset; + timestampOffset = pktTimestamp - pingTimestamp + pingOffset; + LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; + timestamp += timestampOffset; + isAligned = true; + } + continue; + } + if (chan != 0) { + LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; + continue; + } + if ((word0 >> 28) & 0x04) { + if (underrun) *underrun = true; + LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; + } + if (RSSI) *RSSI = (word0 >> 21) & 0x3f; + + if (!isAligned) continue; + + unsigned cursorStart = pktTimestamp - timeStart + dataStart; + while (cursorStart*2 > currDataSize) { + cursorStart -= currDataSize/2; + } + if (cursorStart*2 + payloadSz/2 > currDataSize) { + // need to circle around buffer + memcpy(data+cursorStart*2,tmpBuf+2,(currDataSize-cursorStart*2)*sizeof(short)); + memcpy(data,tmpBuf+2+(currDataSize/2-cursorStart),payloadSz-(currDataSize-cursorStart*2)*sizeof(short)); + } + else { + memcpy(data+cursorStart*2,tmpBuf+2,payloadSz); + } + if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd) + timeEnd = pktTimestamp+payloadSz/2/sizeof(short); + + LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; + + } + } + + // copy desired data to buf + unsigned bufStart = dataStart+(timestamp-timeStart); + if (bufStart + len < currDataSize/2) { + LOG(DEBUG) << "bufStart: " << bufStart; + memcpy(buf,data+bufStart*2,len*2*sizeof(short)); + memset(data+bufStart*2,0,len*2*sizeof(short)); + } + else { + LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; + unsigned firstLength = (currDataSize/2-bufStart); + LOG(DEBUG) << "firstLength: " << firstLength; + memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short)); + memset(data+bufStart*2,0,firstLength*2*sizeof(short)); + memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short)); + memset(data,0,(len-firstLength)*2*sizeof(short)); + } + dataStart = (bufStart + len) % (currDataSize/2); + timeStart = timestamp + len; + + return len; + +#else + if (loopbackBufferSize < 2) return 0; + int numSamples = 0; + struct timeval currTime; + gettimeofday(&currTime,NULL); + double timeElapsed = (currTime.tv_sec - lastReadTime.tv_sec)*1.0e6 + + (currTime.tv_usec - lastReadTime.tv_usec); + if (timeElapsed < samplePeriod) {return 0;} + int numSamplesToRead = (int) floor(timeElapsed/samplePeriod); + if (numSamplesToRead < len) return 0; + + if (numSamplesToRead > len) numSamplesToRead = len; + if (numSamplesToRead > loopbackBufferSize/2) { + firstRead =false; + numSamplesToRead = loopbackBufferSize/2; + } + memcpy(buf,loopbackBuffer,sizeof(short)*2*numSamplesToRead); + loopbackBufferSize -= 2*numSamplesToRead; + memcpy(loopbackBuffer,loopbackBuffer+2*numSamplesToRead, + sizeof(short)*loopbackBufferSize); + numSamples = numSamplesToRead; + if (firstRead) { + int new_usec = lastReadTime.tv_usec + (int) round((double) numSamplesToRead * samplePeriod); + lastReadTime.tv_sec = lastReadTime.tv_sec + new_usec/1000000; + lastReadTime.tv_usec = new_usec % 1000000; + } + else { + gettimeofday(&lastReadTime,NULL); + firstRead = true; + } + samplesRead += numSamples; + + return numSamples; +#endif +} + +int USRPDevice::writeSamples(std::vector &bufs, int len, + bool *underrun, unsigned long long timestamp, + bool isControl) +{ + writeLock.lock(); + +#ifndef SWLOOPBACK + if (!m_uTx) + return 0; + + short *buf = bufs[0]; + + static uint32_t outData[128*20]; + + for (int i = 0; i < len*2; i++) { + buf[i] = host_to_usrp_short(buf[i]); + } + + int numWritten = 0; + unsigned isStart = 1; + unsigned RSSI = 0; + unsigned CHAN = (isControl) ? 0x01f : 0x00; + len = len*2*sizeof(short); + int numPkts = (int) ceil((float)len/(float)504); + unsigned isEnd = (numPkts < 2); + uint32_t *outPkt = outData; + int pktNum = 0; + while (numWritten < len) { + // pkt is pointer to start of a USB packet + uint32_t *pkt = outPkt + pktNum*128; + isEnd = (len - numWritten <= 504); + unsigned payloadLen = ((len - numWritten) < 504) ? (len-numWritten) : 504; + pkt[0] = (isStart << 12 | isEnd << 11 | (RSSI & 0x3f) << 5 | CHAN) << 16 | payloadLen; + pkt[1] = timestamp & 0x0ffffffffll; + memcpy(pkt+2,buf+(numWritten/sizeof(short)),payloadLen); + numWritten += payloadLen; + timestamp += payloadLen/2/sizeof(short); + isStart = 0; + pkt[0] = host_to_usrp_u32(pkt[0]); + pkt[1] = host_to_usrp_u32(pkt[1]); + pktNum++; + } + m_uTx->write((const void*) outPkt,sizeof(uint32_t)*128*numPkts,NULL); + + samplesWritten += len/2/sizeof(short); + writeLock.unlock(); + + return len/2/sizeof(short); +#else + int retVal = len; + memcpy(loopbackBuffer+loopbackBufferSize,buf,sizeof(short)*2*len); + samplesWritten += retVal; + loopbackBufferSize += retVal*2; + + return retVal; +#endif +} + +bool USRPDevice::updateAlignment(TIMESTAMP timestamp) +{ +#ifndef SWLOOPBACK + short data[] = {0x00,0x02,0x00,0x00}; + uint32_t *wordPtr = (uint32_t *) data; + *wordPtr = host_to_usrp_u32(*wordPtr); + bool tmpUnderrun; + + std::vector buf(1, data); + if (writeSamples(buf, 1, &tmpUnderrun, timestamp & 0x0ffffffffll, true)) { + pingTimestamp = timestamp; + return true; + } + return false; +#else + return true; +#endif +} + +#ifndef SWLOOPBACK +bool USRPDevice::setTxFreq(double wFreq, size_t chan) +{ + usrp_tune_result result; + + if (chan) { + LOG(ALERT) << "Invalid channel " << chan; + return false; + } + + if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { + LOG(INFO) << "set TX: " << wFreq << std::endl + << " baseband freq: " << result.baseband_freq << std::endl + << " DDC freq: " << result.dxc_freq << std::endl + << " residual freq: " << result.residual_freq; + return true; + } + else { + LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl + << " baseband freq: " << result.baseband_freq << std::endl + << " DDC freq: " << result.dxc_freq << std::endl + << " residual freq: " << result.residual_freq; + return false; + } +} + +bool USRPDevice::setRxFreq(double wFreq, size_t chan) +{ + usrp_tune_result result; + + if (chan) { + LOG(ALERT) << "Invalid channel " << chan; + return false; + } + + if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { + LOG(INFO) << "set RX: " << wFreq << std::endl + << " baseband freq: " << result.baseband_freq << std::endl + << " DDC freq: " << result.dxc_freq << std::endl + << " residual freq: " << result.residual_freq; + return true; + } + else { + LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl + << " baseband freq: " << result.baseband_freq << std::endl + << " DDC freq: " << result.dxc_freq << std::endl + << " residual freq: " << result.residual_freq; + return false; + } + +} + +#else +bool USRPDevice::setTxFreq(double wFreq) { return true;}; +bool USRPDevice::setRxFreq(double wFreq) { return true;}; +#endif + +RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, + InterfaceType iface, size_t chans, double offset, + const std::vector& tx_paths, + const std::vector& rx_paths) +{ + return new USRPDevice(tx_sps); +} diff --git a/Transceiver52M/device/usrp1/USRPDevice.h b/Transceiver52M/device/usrp1/USRPDevice.h new file mode 100644 index 0000000..f981643 --- /dev/null +++ b/Transceiver52M/device/usrp1/USRPDevice.h @@ -0,0 +1,204 @@ +/* +* Copyright 2008 Free Software Foundation, Inc. +* +* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + + 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. + +*/ + +#ifndef _USRP_DEVICE_H_ +#define _USRP_DEVICE_H_ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "radioDevice.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +typedef boost::shared_ptr usrp_standard_tx_sptr; +typedef boost::shared_ptr usrp_standard_rx_sptr; + +/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ +class USRPDevice: public RadioDevice { + +private: + + static const double masterClockRate; ///< the USRP clock rate + double desiredSampleRate; ///< the desired sampling rate + usrp_standard_rx_sptr m_uRx; ///< the USRP receiver + usrp_standard_tx_sptr m_uTx; ///< the USRP transmitter + + db_base_sptr m_dbRx; ///< rx daughterboard + db_base_sptr m_dbTx; ///< tx daughterboard + usrp_subdev_spec rxSubdevSpec; + usrp_subdev_spec txSubdevSpec; + + int sps; + double actualSampleRate; ///< the actual USRP sampling rate + unsigned int decimRate; ///< the USRP decimation rate + + unsigned long long samplesRead; ///< number of samples read from USRP + unsigned long long samplesWritten; ///< number of samples sent to USRP + + bool started; ///< flag indicates USRP has started + bool skipRx; ///< set if USRP is transmit-only. + + static const unsigned int currDataSize_log2 = 21; + static const unsigned long currDataSize = (1 << currDataSize_log2); + short *data; + unsigned long dataStart; + unsigned long dataEnd; + TIMESTAMP timeStart; + TIMESTAMP timeEnd; + bool isAligned; + + Mutex writeLock; + + short *currData; ///< internal data buffer when reading from USRP + TIMESTAMP currTimestamp; ///< timestamp of internal data buffer + unsigned currLen; ///< size of internal data buffer + + TIMESTAMP timestampOffset; ///< timestamp offset b/w Tx and Rx blocks + TIMESTAMP latestWriteTimestamp; ///< timestamp of most recent ping command + TIMESTAMP pingTimestamp; ///< timestamp of most recent ping response + + long long pingOffset; + unsigned long hi32Timestamp; + unsigned long lastPktTimestamp; + + double rxGain; + +#ifdef SWLOOPBACK + short loopbackBuffer[1000000]; + int loopbackBufferSize; + double samplePeriod; + + struct timeval startTime; + struct timeval lastReadTime; + bool firstRead; +#endif + + public: + + /** Object constructor */ + USRPDevice(size_t sps); + + /** Instantiate the USRP */ + int open(const std::string &, int, bool); + + /** Start the USRP */ + bool start(); + + /** Stop the USRP */ + bool stop(); + + /** Set priority not supported */ + void setPriority(float prio = 0.5) { } + + enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; } + + /** + Read samples from the USRP. + @param buf preallocated buf to contain read result + @param len number of samples desired + @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough + @param timestamp The timestamp of the first samples to be read + @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough + @param RSSI The received signal strength of the read result + @return The number of samples actually read + */ + int readSamples(std::vector &buf, int len, bool *overrun, + TIMESTAMP timestamp = 0xffffffff, bool *underrun = NULL, + unsigned *RSSI = NULL); + /** + Write samples to the USRP. + @param buf Contains the data to be written. + @param len number of samples to write. + @param underrun Set if USRP does not have data to transmit, e.g. data not being sent fast enough + @param timestamp The timestamp of the first sample of the data buffer. + @param isControl Set if data is a control packet, e.g. a ping command + @return The number of samples actually written + */ + int writeSamples(std::vector &bufs, int len, bool *underrun, + TIMESTAMP timestamp = 0xffffffff, bool isControl = false); + + /** Update the alignment between the read and write timestamps */ + bool updateAlignment(TIMESTAMP timestamp); + + /** Set the transmitter frequency */ + bool setTxFreq(double wFreq, size_t chan = 0); + + /** Set the receiver frequency */ + bool setRxFreq(double wFreq, size_t chan = 0); + + /** Returns the starting write Timestamp*/ + TIMESTAMP initialWriteTimestamp(void) { return 20000;} + + /** Returns the starting read Timestamp*/ + TIMESTAMP initialReadTimestamp(void) { return 20000;} + + /** returns the full-scale transmit amplitude **/ + double fullScaleInputValue() {return 13500.0;} + + /** returns the full-scale receive amplitude **/ + double fullScaleOutputValue() {return 9450.0;} + + /** sets the receive chan gain, returns the gain setting **/ + double setRxGain(double dB, size_t chan = 0); + + /** get the current receive gain */ + double getRxGain(size_t chan = 0) { return rxGain; } + + /** return maximum Rx Gain **/ + double maxRxGain(void); + + /** return minimum Rx Gain **/ + double minRxGain(void); + + /** sets the transmit chan gain, returns the gain setting **/ + double setTxGain(double dB, size_t chan = 0); + + /** return maximum Tx Gain **/ + double maxTxGain(void); + + /** return minimum Rx Gain **/ + double minTxGain(void); + + /** sets the RX path to use, returns true if successful and false otherwise */ + bool setRxAntenna(const std::string &ant, size_t chan = 0); + + /* return the used RX path */ + std::string getRxAntenna(size_t chan = 0); + + /** sets the RX path to use, returns true if successful and false otherwise */ + bool setTxAntenna(const std::string &ant, size_t chan = 0); + + /* return the used RX path */ + std::string getTxAntenna(size_t chan = 0); + + /** Return internal status values */ + inline double getTxFreq(size_t chan = 0) { return 0; } + inline double getRxFreq(size_t chan = 0) { return 0; } + inline double getSampleRate() { return actualSampleRate; } + inline double numberRead() { return samplesRead; } + inline double numberWritten() { return samplesWritten; } + + std::vector tx_paths, rx_paths; +}; + +#endif // _USRP_DEVICE_H_ diff --git a/Transceiver52M/radioDevice.h b/Transceiver52M/radioDevice.h deleted file mode 100644 index 9913de0..0000000 --- a/Transceiver52M/radioDevice.h +++ /dev/null @@ -1,160 +0,0 @@ -/* -* Copyright 2008 Free Software Foundation, Inc. -* -* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. -* -* This use of this software may be subject to additional restrictions. -* See the LEGAL file in the main directory for details. - - 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. - -*/ - -#ifndef __RADIO_DEVICE_H__ -#define __RADIO_DEVICE_H__ - -#include -#include - -extern "C" { -#include "config_defs.h" -} - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#define GSMRATE (1625e3/6) -#define MCBTS_SPACING 800000.0 - -/** a 64-bit virtual timestamp for radio data */ -typedef unsigned long long TIMESTAMP; - -/** A class to handle a USRP rev 4, with a two RFX900 daughterboards */ -class RadioDevice { - - public: - /* Available transport bus types */ - enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; - - /* Radio interface types */ - enum InterfaceType { - NORMAL, - RESAMP_64M, - RESAMP_100M, - MULTI_ARFCN, - }; - - static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, - size_t chans = 1, double offset = 0.0, - const std::vector& tx_paths = std::vector(1, ""), - const std::vector& rx_paths = std::vector(1, "")); - - /** Initialize the USRP */ - virtual int open(const std::string &args, int ref, bool swap_channels)=0; - - virtual ~RadioDevice() { } - - /** Start the USRP */ - virtual bool start()=0; - - /** Stop the USRP */ - virtual bool stop()=0; - - /** Get the Tx window type */ - virtual enum TxWindowType getWindowType()=0; - - /** Enable thread priority */ - virtual void setPriority(float prio = 0.5) = 0; - - /** - Read samples from the radio. - @param buf preallocated buf to contain read result - @param len number of samples desired - @param overrun Set if read buffer has been overrun, e.g. data not being read fast enough - @param timestamp The timestamp of the first samples to be read - @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough - @param RSSI The received signal strength of the read result - @return The number of samples actually read - */ - virtual int readSamples(std::vector &bufs, int len, bool *overrun, - TIMESTAMP timestamp = 0xffffffff, bool *underrun = 0, - unsigned *RSSI = 0) = 0; - /** - Write samples to the radio. - @param buf Contains the data to be written. - @param len number of samples to write. - @param underrun Set if radio does not have data to transmit, e.g. data not being sent fast enough - @param timestamp The timestamp of the first sample of the data buffer. - @param isControl Set if data is a control packet, e.g. a ping command - @return The number of samples actually written - */ - virtual int writeSamples(std::vector &bufs, int len, bool *underrun, - TIMESTAMP timestamp, bool isControl = false) = 0; - - /** Update the alignment between the read and write timestamps */ - virtual bool updateAlignment(TIMESTAMP timestamp)=0; - - /** Set the transmitter frequency */ - virtual bool setTxFreq(double wFreq, size_t chan = 0) = 0; - - /** Set the receiver frequency */ - virtual bool setRxFreq(double wFreq, size_t chan = 0) = 0; - - /** Returns the starting write Timestamp*/ - virtual TIMESTAMP initialWriteTimestamp(void)=0; - - /** Returns the starting read Timestamp*/ - virtual TIMESTAMP initialReadTimestamp(void)=0; - - /** returns the full-scale transmit amplitude **/ - virtual double fullScaleInputValue()=0; - - /** returns the full-scale receive amplitude **/ - virtual double fullScaleOutputValue()=0; - - /** sets the receive chan gain, returns the gain setting **/ - virtual double setRxGain(double dB, size_t chan = 0) = 0; - - /** gets the current receive gain **/ - virtual double getRxGain(size_t chan = 0) = 0; - - /** return maximum Rx Gain **/ - virtual double maxRxGain(void) = 0; - - /** return minimum Rx Gain **/ - virtual double minRxGain(void) = 0; - - /** sets the transmit chan gain, returns the gain setting **/ - virtual double setTxGain(double dB, size_t chan = 0) = 0; - - /** return maximum Tx Gain **/ - virtual double maxTxGain(void) = 0; - - /** return minimum Tx Gain **/ - virtual double minTxGain(void) = 0; - - /** sets the RX path to use, returns true if successful and false otherwise */ - virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) = 0; - - /** return the used RX path */ - virtual std::string getRxAntenna(size_t chan = 0) = 0; - - /** sets the RX path to use, returns true if successful and false otherwise */ - virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) = 0; - - /** return the used RX path */ - virtual std::string getTxAntenna(size_t chan = 0) = 0; - - /** Return internal status values */ - virtual double getTxFreq(size_t chan = 0) = 0; - virtual double getRxFreq(size_t chan = 0) = 0; - virtual double getSampleRate()=0; - virtual double numberRead()=0; - virtual double numberWritten()=0; - -}; - -#endif diff --git a/configure.ac b/configure.ac index 125f41e..ee8077c 100644 --- a/configure.ac +++ b/configure.ac @@ -203,6 +203,9 @@ AC_CONFIG_FILES([\ Transceiver52M/arch/common/Makefile \ Transceiver52M/arch/arm/Makefile \ Transceiver52M/arch/x86/Makefile \ + Transceiver52M/device/Makefile \ + Transceiver52M/device/uhd/Makefile \ + Transceiver52M/device/usrp1/Makefile \ tests/Makefile \ tests/CommonLibs/Makefile \ tests/Transceiver52M/Makefile \ -- cgit v1.2.3