From dc82ffd1f8b7fee5a8de2d73dabd24eb6d83d5a6 Mon Sep 17 00:00:00 2001 From: Csaba Sipos Date: Sun, 11 Oct 2020 14:23:00 +0200 Subject: [PATCH] add xtrx support Signed-off-by: Eric Wild --- CMakeLists.txt | 1 + README | 3 +- cmake/Modules/FindLibXTRX.cmake | 27 ++ grc/gen_osmosdr_blocks.py | 4 +- lib/CMakeLists.txt | 8 + lib/config.h.in | 1 + lib/sink_impl.cc | 15 + lib/source_impl.cc | 18 + lib/xtrx/CMakeLists.txt | 35 ++ lib/xtrx/xtrx_obj.cc | 138 ++++++++ lib/xtrx/xtrx_obj.h | 68 ++++ lib/xtrx/xtrx_sink_c.cc | 505 +++++++++++++++++++++++++++ lib/xtrx/xtrx_sink_c.h | 129 +++++++ lib/xtrx/xtrx_source_c.cc | 583 ++++++++++++++++++++++++++++++++ lib/xtrx/xtrx_source_c.h | 127 +++++++ 15 files changed, 1660 insertions(+), 2 deletions(-) create mode 100644 cmake/Modules/FindLibXTRX.cmake create mode 100644 lib/xtrx/CMakeLists.txt create mode 100644 lib/xtrx/xtrx_obj.cc create mode 100644 lib/xtrx/xtrx_obj.h create mode 100644 lib/xtrx/xtrx_sink_c.cc create mode 100644 lib/xtrx/xtrx_sink_c.h create mode 100644 lib/xtrx/xtrx_source_c.cc create mode 100644 lib/xtrx/xtrx_source_c.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c23cb49..74c54f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,6 +181,7 @@ find_package(LibbladeRF) find_package(GnuradioFCDPP) find_package(SoapySDR NO_MODULE) find_package(LibFreeSRP) +find_package(LibXTRX) find_package(Doxygen) # Python diff --git a/README b/README index 842c154..6550b3d 100644 --- a/README +++ b/README @@ -13,7 +13,8 @@ as well supports: * Great Scott Gadgets HackRF through libhackrf * Nuand LLC bladeRF through libbladeRF library * Ettus USRP Devices through Ettus UHD library - * Fairwaves UmTRX through Fairwaves' fork of UHD + * Fairwaves UmTRX through Fairwaves' module for UHD + * Fairwaves XTRX through libxtrx * Red Pitaya SDR transceiver (http://bazaar.redpitaya.com) * FreeSRP through libfreesrp diff --git a/cmake/Modules/FindLibXTRX.cmake b/cmake/Modules/FindLibXTRX.cmake new file mode 100644 index 0000000..e7681d1 --- /dev/null +++ b/cmake/Modules/FindLibXTRX.cmake @@ -0,0 +1,27 @@ +if(NOT LIBXTRX_FOUND) + pkg_check_modules (LIBXTRX_PKG libxtrx) + find_path(LIBXTRX_INCLUDE_DIRS NAMES xtrx_api.h + PATHS + ${LIBXTRX_PKG_INCLUDE_DIRS} + /usr/include + /usr/local/include + ) + + find_library(LIBXTRX_LIBRARIES NAMES xtrx + PATHS + ${LIBXTRX_PKG_LIBRARY_DIRS} + /usr/lib + /usr/local/lib + ) + +if(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + set(LIBXTRX_FOUND TRUE CACHE INTERNAL "libxtrx found") + message(STATUS "Found libxtrx: ${LIBXTRX_INCLUDE_DIRS}, ${LIBXTRX_LIBRARIES}") +else(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + set(LIBXTRX_FOUND FALSE CACHE INTERNAL "libxtrx found") + message(STATUS "libxtrx not found.") +endif(LIBXTRX_INCLUDE_DIRS AND LIBXTRX_LIBRARIES) + +mark_as_advanced(LIBXTRX_LIBRARIES LIBXTRX_INCLUDE_DIRS) + +endif(NOT LIBXTRX_FOUND) diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index 45bac1f..4cca82f 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -171,7 +171,8 @@ documentation: |- * Great Scott Gadgets HackRF through libhackrf * Nuand LLC bladeRF through libbladeRF library * Ettus USRP Devices through Ettus UHD library - * Fairwaves UmTRX through Fairwaves' fork of UHD + * Fairwaves XTRX through libxtrx + * Fairwaves UmTRX through Fairwaves' module for UHD * Red Pitaya SDR transceiver (http://bazaar.redpitaya.com) * FreeSRP through libfreesrp library @@ -210,6 +211,7 @@ documentation: |- hackrf=0[,buffers=32][,bias=0|1][,bias_tx=0|1] bladerf=0[,tamer=internal|external|external_1pps][,smb=25e6] uhd[,serial=...][,lo_offset=0][,mcr=52e6][,nchan=2][,subdev='\\\\'B:0 A:0\\\\''] ... + xtrx Num Channels: Selects the total number of channels in this multi-device configuration. Required when specifying multiple device arguments. diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index bf974d8..1bb8655 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -249,6 +249,14 @@ if(ENABLE_FREESRP) add_subdirectory(freesrp) endif(ENABLE_FREESRP) +######################################################################## +# Setup XTRX component +######################################################################## +GR_REGISTER_COMPONENT("XTRX SDR" ENABLE_XTRX LIBXTRX_FOUND) +if(ENABLE_XTRX) + add_subdirectory(xtrx) +endif(ENABLE_XTRX) + ######################################################################## # Setup configuration file ######################################################################## diff --git a/lib/config.h.in b/lib/config.h.in index cce87e6..d96cd80 100644 --- a/lib/config.h.in +++ b/lib/config.h.in @@ -18,6 +18,7 @@ #cmakedefine ENABLE_SOAPY #cmakedefine ENABLE_REDPITAYA #cmakedefine ENABLE_FREESRP +#cmakedefine ENABLE_XTRX //provide NAN define for MSVC older than VC12 #if defined(_MSC_VER) && (_MSC_VER < 1800) diff --git a/lib/sink_impl.cc b/lib/sink_impl.cc index a09d7df..2493883 100644 --- a/lib/sink_impl.cc +++ b/lib/sink_impl.cc @@ -48,6 +48,9 @@ #ifdef ENABLE_FREESRP #include #endif +#ifdef ENABLE_XTRX +#include "xtrx_sink_c.h" +#endif #ifdef ENABLE_FILE #include "file_sink_c.h" #endif @@ -99,6 +102,9 @@ sink_impl::sink_impl( const std::string &args ) #ifdef ENABLE_FREESRP dev_types.push_back("freesrp"); #endif +#ifdef ENABLE_XTRX + dev_types.push_back("xtrx"); +#endif #ifdef ENABLE_FILE dev_types.push_back("file"); #endif @@ -147,6 +153,9 @@ sink_impl::sink_impl( const std::string &args ) for (std::string dev : freesrp_sink_c::get_devices()) dev_list.push_back( dev ); #endif +#ifdef ENABLE_XTRX + BOOST_FOREACH( std::string dev, xtrx_sink_c::get_devices() ) +#endif #ifdef ENABLE_FILE for (std::string dev : file_sink_c::get_devices()) dev_list.push_back( dev ); @@ -209,6 +218,12 @@ sink_impl::sink_impl( const std::string &args ) block = sink; iface = sink.get(); } #endif +#ifdef ENABLE_XTRX + if ( dict.count("xtrx") ) { + xtrx_sink_c_sptr sink = make_xtrx_sink_c( arg ); + block = sink; iface = sink.get(); + } +#endif #ifdef ENABLE_FILE if ( dict.count("file") ) { file_sink_c_sptr sink = make_file_sink_c( arg ); diff --git a/lib/source_impl.cc b/lib/source_impl.cc index 8f8efef..84f5024 100644 --- a/lib/source_impl.cc +++ b/lib/source_impl.cc @@ -88,6 +88,10 @@ #include #endif +#ifdef ENABLE_XTRX +#include +#endif + #include "arg_helpers.h" #include "source_impl.h" @@ -158,6 +162,9 @@ source_impl::source_impl( const std::string &args ) #endif #ifdef ENABLE_FREESRP dev_types.push_back("freesrp"); +#endif +#ifdef ENABLE_XTRX + dev_types.push_back("xtrx"); #endif std::cerr << "gr-osmosdr " << GR_OSMOSDR_VERSION << " (" << GR_OSMOSDR_LIBVER << ") " @@ -234,6 +241,10 @@ source_impl::source_impl( const std::string &args ) for (std::string dev : freesrp_source_c::get_devices()) dev_list.push_back( dev ); #endif +#ifdef ENABLE_XTRX + BOOST_FOREACH( std::string dev, xtrx_source_c::get_devices() ) + dev_list.push_back( dev ); +#endif // std::cerr << std::endl; // for (std::string dev : dev_list) @@ -358,6 +369,13 @@ source_impl::source_impl( const std::string &args ) } #endif +#ifdef ENABLE_XTRX + if ( dict.count("xtrx") ) { + xtrx_source_c_sptr src = make_xtrx_source_c( arg ); + block = src; iface = src.get(); + } +#endif + if ( iface != NULL && long(block.get()) != 0 ) { _devs.push_back( iface ); diff --git a/lib/xtrx/CMakeLists.txt b/lib/xtrx/CMakeLists.txt new file mode 100644 index 0000000..9297bf0 --- /dev/null +++ b/lib/xtrx/CMakeLists.txt @@ -0,0 +1,35 @@ +# Copyright 2012 Free Software Foundation, Inc. +# +# This file is part of GNU Radio +# +# GNU Radio is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# GNU Radio 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 General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Radio; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +target_include_directories(gnuradio-osmosdr PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXTRX_INCLUDE_DIRS} +) + +list(APPEND gr_osmosdr_srcs + ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_obj.cc + ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_source_c.cc + ${CMAKE_CURRENT_SOURCE_DIR}/xtrx_sink_c.cc +) + +set(gr_osmosdr_srcs ${gr_osmosdr_srcs} PARENT_SCOPE) diff --git a/lib/xtrx/xtrx_obj.cc b/lib/xtrx/xtrx_obj.cc new file mode 100644 index 0000000..a971df4 --- /dev/null +++ b/lib/xtrx/xtrx_obj.cc @@ -0,0 +1,138 @@ +/* -*- c++ -*- */ +/* + * Copyright 2017 Sergey Kostanbaev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include "xtrx_obj.h" +#include +#include +#include +#include +#include + +static std::map s_objects; + +xtrx_obj_sptr xtrx_obj::get(const char* xtrx_dev, + unsigned loglevel, + bool lmsreset) +{ + std::map::iterator i; + std::string name(xtrx_dev); + + i = s_objects.find(name); + if (i == s_objects.end()) { + // No such object + s_objects[name].reset(new xtrx_obj(name, loglevel, lmsreset)); + } + + return s_objects[name]; +} + +void xtrx_obj::clear_all() +{ + s_objects.clear(); +} + +std::vector xtrx_obj::get_devices() +{ + std::vector devices; + // TODO + devices.push_back("/dev/xtrx0"); + return devices; +} + + +xtrx_obj::xtrx_obj(const std::string &path, unsigned loglevel, bool lmsreset) + : _run(false) + , _vio(0) + , _sink_rate(0) + , _sink_master(0) + , _source_rate(0) + , _source_master(0) + , _flags(0) +{ + unsigned xtrxflag = (loglevel & XTRX_O_LOGLVL_MASK) | ((lmsreset) ? XTRX_O_RESET : 0); + std::cerr << "xtrx_obj::xtrx_obj = " << xtrxflag << std::endl; + + int res = xtrx_open_list(path.c_str(), NULL, &_obj); + if (res < 0) { + std::stringstream message; + message << "Couldn't open " ": Error: " << -res; + + throw std::runtime_error( message.str() ); + } + + _devices = res; +} + +double xtrx_obj::set_smaplerate(double rate, double master, bool sink, unsigned flags) +{ + boost::mutex::scoped_lock lock(mtx); + + if (sink) { + _sink_rate = rate; + _sink_master = master; + } else { + _source_rate = rate; + _source_master = master; + } + _flags |= flags | XTRX_SAMPLERATE_FORCE_UPDATE; + + if (_sink_master != 0 && _source_master != 0 && _sink_master != _source_master) { + std::stringstream message; + message << "Can't operate on diferrent master settings for XTRX sink and source" + " sink_master " << _sink_master << " source_master" << _source_master; + + throw std::runtime_error( message.str() ); + } + + double rxrate = 0, txrate = 0; + double actmaster = (_source_master > 0) ? _source_master : _sink_master; + int res = xtrx_set_samplerate(_obj, + actmaster, + _source_rate, + _sink_rate, + _flags, + NULL, + &rxrate, + &txrate); + if (res) { + std::cerr << "Unable to set samplerate, error=" << res << std::endl; + if (sink) + return _sink_rate; + return _source_rate; + } + + if (_vio) { + xtrx_val_set(_obj, XTRX_TRX, XTRX_CH_AB, XTRX_LMS7_VIO, _vio); + } + + if (sink) + return txrate; + return rxrate; +} + +xtrx_obj::~xtrx_obj() +{ + if (_obj) { + if (_run) { + //boost::mutex::scoped_lock lock(mtx); + xtrx_stop(_obj, XTRX_TRX); + } + xtrx_close(_obj); + } +} diff --git a/lib/xtrx/xtrx_obj.h b/lib/xtrx/xtrx_obj.h new file mode 100644 index 0000000..e26947d --- /dev/null +++ b/lib/xtrx/xtrx_obj.h @@ -0,0 +1,68 @@ +/* -*- c++ -*- */ +/* + * Copyright 2017 Sergey Kostanbaev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef XTRX_OBJ_H +#define XTRX_OBJ_H + +#include +#include +#include +#include +#include + +class xtrx_obj; + +typedef boost::shared_ptr xtrx_obj_sptr; + +class xtrx_obj +{ +public: + xtrx_obj(const std::string& path, unsigned loglevel, bool lmsreset); + ~xtrx_obj(); + + static std::vector get_devices(); + + static xtrx_obj_sptr get(const char* xtrx_dev, + unsigned loglevel, + bool lmsreset); + static void clear_all(); + + xtrx_dev* dev() { return _obj; } + unsigned dev_count() { return _devices; } + + double set_smaplerate(double rate, double master, bool sink, unsigned flags); + + void set_vio(unsigned vio) { _vio = vio; } + + boost::mutex mtx; +protected: + xtrx_dev* _obj; + bool _run; + unsigned _vio; + + double _sink_rate; + double _sink_master; + double _source_rate; + double _source_master; + + unsigned _flags; + unsigned _devices; +}; + +#endif // XTRX_OBJ_H diff --git a/lib/xtrx/xtrx_sink_c.cc b/lib/xtrx/xtrx_sink_c.cc new file mode 100644 index 0000000..46ce1b0 --- /dev/null +++ b/lib/xtrx/xtrx_sink_c.cc @@ -0,0 +1,505 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016,2017 Sergey Kostanbaev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xtrx_sink_c.h" + +#include "arg_helpers.h" + +static const int max_burstsz = 4096; +using namespace boost::assign; + +xtrx_sink_c_sptr make_xtrx_sink_c(const std::string &args) +{ + return gnuradio::get_initial_sptr(new xtrx_sink_c(args)); +} + +static size_t parse_nchan(const std::string &args) +{ + size_t nchan = 1; + + dict_t dict = params_to_dict(args); + + if (dict.count("nchan")) + nchan = boost::lexical_cast< size_t >( dict["nchan"] ); + + if (nchan < 1) + nchan = 1; + + return nchan; +} + +xtrx_sink_c::xtrx_sink_c(const std::string &args) : + gr::sync_block("xtrx_sink_c", + gr::io_signature::make(parse_nchan(args), + parse_nchan(args), + sizeof(gr_complex)), + gr::io_signature::make(0, 0, 0)), + _sample_flags(0), + _rate(0), + _master(0), + _freq(0), + _corr(0), + _bandwidth(0), + _dsp(0), + _auto_gain(false), + _otw(XTRX_WF_16), + _mimo_mode(false), + _gain_tx(0), + _channels(parse_nchan(args)), + _ts(8192), + _swap_ab(false), + _swap_iq(false), + _tdd(false), + _allow_dis(false), + _dev("") +{ + + dict_t dict = params_to_dict(args); + + if (dict.count("master")) { + _master = boost::lexical_cast< double >( dict["master"]); + } + + std::cerr << args.c_str() << std::endl; + + int loglevel = 4; + if (dict.count("loglevel")) { + loglevel = boost::lexical_cast< int >( dict["loglevel"] ); + } + + bool lmsreset = 0; + if (dict.count("lmsreset")) { + lmsreset = boost::lexical_cast< bool >( dict["lmsreset"] ); + } + + if (dict.count("txdelay")) { + _ts += 8192 * boost::lexical_cast< int >( dict["txdelay"] ); + } + + if (dict.count("allowdis")) { + _allow_dis = boost::lexical_cast< bool >( dict["allowdis"] ); + } + + if (dict.count("swap_ab")) { + _swap_ab = true; + std::cerr << "xtrx_sink_c: swap AB channels"; + } + + if (dict.count("swap_iq")) { + _swap_iq = true; + std::cerr << "xtrx_sink_c: swap IQ"; + } + + if (dict.count("sfl")) { + _sample_flags = boost::lexical_cast< unsigned >( dict["sfl"] ); + } + + if (dict.count("tdd")) { + _tdd = true; + std::cerr << "xtrx_sink_c: TDD mode"; + } + + if (dict.count("dsp")) { + _dsp = boost::lexical_cast< double >( dict["dsp"] ); + std::cerr << "xtrx_sink_c: DSP:" << _dsp; + } + + if (dict.count("dev")) { + _dev = dict["dev"]; + std::cerr << "xtrx_sink_c: XTRX device: %s" << _dev.c_str(); + } + + _xtrx = xtrx_obj::get(_dev.c_str(), loglevel, lmsreset); + if (_xtrx->dev_count() * 2 == _channels) { + _mimo_mode = true; + } else if (_xtrx->dev_count() != _channels) { + throw std::runtime_error("Number of requested channels != number of devices"); + } + if (dict.count("refclk")) { + xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["refclk"] ), XTRX_CLKSRC_INT); + } + if (dict.count("extclk")) { + xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["extclk"] ), XTRX_CLKSRC_EXT); + } + + std::cerr << "xtrx_sink_c::xtrx_sink_c()" << std::endl; + set_alignment(32); + set_output_multiple(max_burstsz); +} + +xtrx_sink_c::~xtrx_sink_c() +{ + std::cerr << "xtrx_sink_c::~xtrx_sink_c()" << std::endl; +} + +std::string xtrx_sink_c::name() +{ + return "GrLibXTRX"; +} + +size_t xtrx_sink_c::get_num_channels( void ) +{ + return input_signature()->max_streams(); +} + +osmosdr::meta_range_t xtrx_sink_c::get_sample_rates( void ) +{ + osmosdr::meta_range_t range; + range += osmosdr::range_t( 1000000, 160000000, 1 ); + return range; +} + +double xtrx_sink_c::set_sample_rate( double rate ) +{ + std::cerr << "Set sample rate " << rate << std::endl; + _rate = _xtrx->set_smaplerate(rate, _master, true, _sample_flags); + return get_sample_rate(); +} + +double xtrx_sink_c::get_sample_rate( void ) +{ + return _rate; +} + +osmosdr::freq_range_t xtrx_sink_c::get_freq_range( size_t chan ) +{ + osmosdr::freq_range_t range; + range += osmosdr::range_t( double(0.03e9), double(3.8e9), 1); // as far as we know + return range; +} + +double xtrx_sink_c::set_center_freq( double freq, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + _freq = freq; + double corr_freq = (freq)*(1.0 + (_corr) * 0.000001); + + std::cerr << "TX Set freq " << freq << std::endl; + xtrx_channel_t xchan = (xtrx_channel_t)(XTRX_CH_A << chan); + + int res = xtrx_tune_ex(_xtrx->dev(), (_tdd) ? XTRX_TUNE_TX_AND_RX_TDD : XTRX_TUNE_TX_FDD, xchan, corr_freq - _dsp, &_freq); + if (res) { + std::cerr << "Unable to deliver frequency " << corr_freq << std::endl; + } + + res = xtrx_tune_ex(_xtrx->dev(), XTRX_TUNE_BB_TX, xchan, _dsp, NULL); + return get_center_freq(chan); +} + +double xtrx_sink_c::get_center_freq( size_t chan ) +{ + return _freq + _dsp; +} + +double xtrx_sink_c::set_freq_corr( double ppm, size_t chan ) +{ + _corr = ppm; + + set_center_freq(_freq, chan); + + return get_freq_corr( chan ); +} + +double xtrx_sink_c::get_freq_corr( size_t chan ) +{ + return _corr; +} + + +static const std::vector s_lna_list = boost::assign::list_of("TX"); + +std::vector xtrx_sink_c::get_gain_names( size_t chan ) +{ + return s_lna_list; +} + +osmosdr::gain_range_t xtrx_sink_c::get_gain_range( size_t chan ) +{ + return get_gain_range("TX", chan); +} + +osmosdr::gain_range_t xtrx_sink_c::get_gain_range( const std::string & name, size_t chan ) +{ + osmosdr::gain_range_t range; + range += osmosdr::range_t( -31, 0, 1 ); + return range; +} + +bool xtrx_sink_c::set_gain_mode( bool automatic, size_t chan ) +{ + _auto_gain = automatic; + return get_gain_mode(chan); +} + +bool xtrx_sink_c::get_gain_mode( size_t chan ) +{ + return _auto_gain; +} + +double xtrx_sink_c::set_gain( double gain, size_t chan ) +{ + return set_gain(gain, "TX", chan); +} + +double xtrx_sink_c::set_gain( double igain, const std::string & name, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + osmosdr::gain_range_t gains = xtrx_sink_c::get_gain_range( name, chan ); + double gain = gains.clip(igain); + double actual_gain; + + std::cerr << "Set TX gain: " << igain << std::endl; + + int res = xtrx_set_gain(_xtrx->dev(), (xtrx_channel_t)(XTRX_CH_A << chan), + XTRX_TX_PAD_GAIN, gain, &actual_gain); + if (res) { + std::cerr << "Unable to set gain `" << name.c_str() << "`; err=" << res << std::endl; + } + + _gain_tx = actual_gain; + return actual_gain; +} + +double xtrx_sink_c::get_gain( size_t chan ) +{ + return get_gain("TX"); +} + +double xtrx_sink_c::get_gain( const std::string & name, size_t chan ) +{ + return _gain_tx; +} + +double xtrx_sink_c::set_bandwidth( double bandwidth, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + std::cerr << "Set bandwidth " << bandwidth << " chan " << chan << std::endl; + + if (bandwidth <= 0.0) { + bandwidth = get_sample_rate() * 0.75; + if (bandwidth < 0.5e6) { + bandwidth = 0.5e6; + } + } + + int res = xtrx_tune_tx_bandwidth(_xtrx->dev(), + (xtrx_channel_t)(XTRX_CH_A << chan), + bandwidth, &_bandwidth); + if (res) { + std::cerr << "Can't set bandwidth: " << res << std::endl; + } + return get_bandwidth(chan); +} + +double xtrx_sink_c::get_bandwidth( size_t chan ) +{ + return _bandwidth; +} + + +static const std::map s_ant_map = boost::assign::map_list_of + ("AUTO", XTRX_TX_AUTO) + ("B1", XTRX_TX_H) + ("B2", XTRX_TX_W) + ("TXH", XTRX_TX_H) + ("TXW", XTRX_TX_W) + ; +static const std::map s_ant_map_r = boost::assign::map_list_of + (XTRX_TX_H, "TXH") + (XTRX_TX_W, "TXW") + (XTRX_TX_AUTO, "AUTO") + ; + +static xtrx_antenna_t get_ant_type(const std::string& name) +{ + std::map::const_iterator it; + + it = s_ant_map.find(name); + if (it != s_ant_map.end()) { + return it->second; + } + + return XTRX_TX_AUTO; +} + +static const std::vector s_ant_list = boost::assign::list_of + ("AUTO")("TXH")("TXW") + ; + + +std::vector< std::string > xtrx_sink_c::get_antennas( size_t chan ) +{ + return s_ant_list; +} + +std::string xtrx_sink_c::set_antenna( const std::string & antenna, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + _ant = get_ant_type(antenna); + + std::cerr << "Set antenna " << antenna << std::endl; + + int res = xtrx_set_antenna_ex(_xtrx->dev(), + (xtrx_channel_t)(XTRX_CH_A << chan), + _ant); + if (res) { + std::cerr << "Can't set antenna: " << antenna << std::endl; + } + return get_antenna( chan ); +} + +std::string xtrx_sink_c::get_antenna( size_t chan ) +{ + return s_ant_map_r.find(_ant)->second; +} + +void xtrx_sink_c::tag_process(int ninput_items) +{ + std::sort(_tags.begin(), _tags.end(), gr::tag_t::offset_compare); + + const uint64_t samp0_count = this->nitems_read(0); + uint64_t max_count = samp0_count + ninput_items; + + bool found_time_tag = false; + BOOST_FOREACH(const gr::tag_t &my_tag, _tags) { + const uint64_t my_tag_count = my_tag.offset; + const pmt::pmt_t &key = my_tag.key; + const pmt::pmt_t &value = my_tag.value; + + if (my_tag_count >= max_count) { + break; + } else if(pmt::equal(key, TIME_KEY)) { + //if (my_tag_count != samp0_count) { + // max_count = my_tag_count; + // break; + //} + found_time_tag = true; + //_metadata.has_time_spec = true; + //_metadata.time_spec = ::uhd::time_spec_t + // (pmt::to_uint64(pmt::tuple_ref(value, 0)), + // pmt::to_double(pmt::tuple_ref(value, 1))); + uint64_t seconds = pmt::to_uint64(pmt::tuple_ref(value, 0)); + double fractional = pmt::to_double(pmt::tuple_ref(value, 1)); + + std::cerr << "TX_TIME: " << seconds << ":" << fractional << std::endl; + } + } // end foreach + + if (found_time_tag) { + //_metadata.has_time_spec = true; + } +} + +int xtrx_sink_c::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + int ninput_items = noutput_items; + const uint64_t samp0_count = nitems_read(0); + get_tags_in_range(_tags, 0, samp0_count, samp0_count + ninput_items); + if (!_tags.empty()) + tag_process(ninput_items); + + xtrx_send_ex_info_t nfo; + nfo.samples = noutput_items; + nfo.buffer_count = input_items.size(); + nfo.buffers = &input_items[0]; + nfo.flags = XTRX_TX_DONT_BUFFER; + if (!_allow_dis) + nfo.flags |= XTRX_TX_NO_DISCARD; + nfo.ts = _ts; + nfo.timeout = 0; + + int res = xtrx_send_sync_ex(_xtrx->dev(), &nfo); + if (res) { + std::cerr << "Err: " << res << std::endl; + + std::stringstream message; + message << "xtrx_send_burst_sync error: " << -res; + throw std::runtime_error( message.str() ); + } + + _ts += noutput_items; + for (unsigned i = 0; i < input_items.size(); i++) { + consume(i, noutput_items); + } + return 0; +} + +bool xtrx_sink_c::start() +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + xtrx_run_params_t params; + xtrx_run_params_init(¶ms); + + params.dir = XTRX_TX; + if (!_mimo_mode) + params.tx.flags |= XTRX_RSP_SISO_MODE; + + if (_swap_ab) + params.tx.flags |= XTRX_RSP_SWAP_AB; + + if (_swap_iq) + params.tx.flags |= XTRX_RSP_SWAP_IQ; + + params.tx.hfmt = XTRX_IQ_FLOAT32; + params.tx.wfmt = _otw; + params.tx.chs = XTRX_CH_AB; + params.tx.paketsize = 0; + params.rx_stream_start = 256*1024; + + int res = xtrx_run_ex(_xtrx->dev(), ¶ms); + if (res) { + std::cerr << "Got error: " << res << std::endl; + } + + return res == 0; +} + +bool xtrx_sink_c::stop() +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + //TODO: + std::cerr << "xtrx_sink_c::stop()" << std::endl; + int res = xtrx_stop(_xtrx->dev(), XTRX_TX); + if (res) { + std::cerr << "Got error: " << res << std::endl; + } + + return res == 0; +} diff --git a/lib/xtrx/xtrx_sink_c.h b/lib/xtrx/xtrx_sink_c.h new file mode 100644 index 0000000..1263858 --- /dev/null +++ b/lib/xtrx/xtrx_sink_c.h @@ -0,0 +1,129 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016 Sergey Kostanabev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef XTRX_SINK_C_H +#define XTRX_SINK_C_H + +#include +#include + +#include "sink_iface.h" +#include "xtrx_obj.h" + + +static const pmt::pmt_t SOB_KEY = pmt::string_to_symbol("tx_sob"); +static const pmt::pmt_t EOB_KEY = pmt::string_to_symbol("tx_eob"); +static const pmt::pmt_t TIME_KEY = pmt::string_to_symbol("tx_time"); +static const pmt::pmt_t FREQ_KEY = pmt::string_to_symbol("tx_freq"); +static const pmt::pmt_t COMMAND_KEY = pmt::string_to_symbol("tx_command"); + +class xtrx_sink_c; + +typedef boost::shared_ptr< xtrx_sink_c > xtrx_sink_c_sptr; + +xtrx_sink_c_sptr make_xtrx_sink_c( const std::string & args = "" ); + +class xtrx_sink_c : + public gr::sync_block, + public sink_iface +{ +private: + friend xtrx_sink_c_sptr make_xtrx_sink_c(const std::string &args); + + xtrx_sink_c(const std::string &args); + +public: + ~xtrx_sink_c(); + + std::string name(); + + static std::vector< std::string > get_devices( bool fake = false ) { return xtrx_obj::get_devices(); } + + size_t get_num_channels( void ); + + osmosdr::meta_range_t get_sample_rates( void ); + double set_sample_rate( double rate ); + double get_sample_rate( void ); + + osmosdr::freq_range_t get_freq_range( size_t chan = 0 ); + double set_center_freq( double freq, size_t chan = 0 ); + double get_center_freq( size_t chan = 0 ); + double set_freq_corr( double ppm, size_t chan = 0 ); + double get_freq_corr( size_t chan = 0 ); + + std::vector get_gain_names( size_t chan = 0 ); + osmosdr::gain_range_t get_gain_range( size_t chan = 0 ); + osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 ); + bool set_gain_mode( bool automatic, size_t chan = 0 ); + bool get_gain_mode( size_t chan = 0 ); + double set_gain( double gain, size_t chan = 0 ); + double set_gain( double gain, const std::string & name, size_t chan = 0 ); + double get_gain( size_t chan = 0 ); + double get_gain( const std::string & name, size_t chan = 0 ); + + std::vector< std::string > get_antennas( size_t chan = 0 ); + std::string set_antenna( const std::string & antenna, size_t chan = 0 ); + std::string get_antenna( size_t chan = 0 ); + + double set_bandwidth( double bandwidth, size_t chan = 0 ); + double get_bandwidth( size_t chan = 0 ); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + + void tag_process(int ninput_items); + +private: + xtrx_obj_sptr _xtrx; + std::vector _tags; + + unsigned _sample_flags; + double _rate; + double _master; + double _freq; + double _corr; + double _bandwidth; + double _dsp; + bool _auto_gain; + + xtrx_wire_format_t _otw; + bool _mimo_mode; + + int _gain_tx; + + unsigned _channels; + xtrx_antenna_t _ant; + + uint64_t _ts; + + bool _swap_ab; + bool _swap_iq; + + bool _tdd; + bool _allow_dis; + + std::string _dev; +}; + +#endif // xtrx_sink_c_H diff --git a/lib/xtrx/xtrx_source_c.cc b/lib/xtrx/xtrx_source_c.cc new file mode 100644 index 0000000..4fdc877 --- /dev/null +++ b/lib/xtrx/xtrx_source_c.cc @@ -0,0 +1,583 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016,2017 Sergey Kostanbaev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "xtrx_source_c.h" + +#include "arg_helpers.h" + +using namespace boost::assign; + + +xtrx_source_c_sptr make_xtrx_source_c(const std::string &args) +{ + return gnuradio::get_initial_sptr(new xtrx_source_c(args)); +} + +static size_t parse_nchan(const std::string &args) +{ + size_t nchan = 1; + + dict_t dict = params_to_dict(args); + + if (dict.count("nchan")) + nchan = boost::lexical_cast< size_t >( dict["nchan"] ); + + if (nchan < 1) + nchan = 1; + + return nchan; +} + +xtrx_source_c::xtrx_source_c(const std::string &args) : + gr::sync_block("xtrx_source_c", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(parse_nchan(args), + parse_nchan(args), + sizeof(gr_complex))), + _sample_flags(0), + _rate(0), + _master(0), + _freq(0), + _corr(0), + _bandwidth(0), + _auto_gain(false), + _otw(XTRX_WF_16), + _mimo_mode(false), + _gain_lna(0), + _gain_tia(0), + _gain_pga(0), + _channels(parse_nchan(args)), + _swap_ab(false), + _swap_iq(false), + _loopback(false), + _tdd(false), + _fbctrl(false), + _timekey(false), + _dsp(0) +{ + _id = pmt::string_to_symbol(args); + + dict_t dict = params_to_dict(args); + + if (dict.count("otw_format")) { + const std::string& otw = dict["otw_format"]; + if (otw == "sc16" || otw == "16") { + _otw = XTRX_WF_16; + } else if (otw == "sc12" || otw == "12") { + _otw = XTRX_WF_12; + } else if (otw == "sc8" || otw == "8") { + _otw = XTRX_WF_8; + } else { + throw std::runtime_error("Parameter `otw_format` should be {sc16,sc12,sc8}"); + } + } + + if (dict.count("master")) { + _master = boost::lexical_cast< double >( dict["master"]); + } + + std::cerr << args.c_str() << std::endl; + + int loglevel = 4; + if (dict.count("loglevel")) { + loglevel = boost::lexical_cast< int >( dict["loglevel"] ); + } + + bool lmsreset = 0; + if (dict.count("lmsreset")) { + lmsreset = boost::lexical_cast< bool >( dict["lmsreset"] ); + } + + if (dict.count("fbctrl")) { + _fbctrl = boost::lexical_cast< bool >( dict["fbctrl"] ); + } + + if (dict.count("swap_ab")) { + _swap_ab = true; + std::cerr << "xtrx_source_c: swap AB channels"; + } + + if (dict.count("swap_iq")) { + _swap_iq = true; + std::cerr << "xtrx_source_c: swap IQ"; + } + + if (dict.count("sfl")) { + _sample_flags = boost::lexical_cast< unsigned >( dict["sfl"] ); + } + + if (dict.count("loopback")) { + _loopback = true; + std::cerr << "xtrx_source_c: loopback"; + } + + if (dict.count("tdd")) { + _tdd = true; + std::cerr << "xtrx_source_c: TDD mode"; + } + + if (dict.count("dsp")) { + _dsp = boost::lexical_cast< double >( dict["dsp"] ); + std::cerr << "xtrx_source_c: DSP:" << _dsp; + } + + if (dict.count("dev")) { + _dev = dict["dev"]; + std::cerr << "xtrx_source_c: XTRX device: %s" << _dev.c_str(); + } + + _xtrx = xtrx_obj::get(_dev.c_str(), loglevel, lmsreset); + if (_xtrx->dev_count() * 2 == _channels) { + _mimo_mode = true; + } else if (_xtrx->dev_count() != _channels) { + throw std::runtime_error("Number of requested channels != number of devices"); + } + + if (dict.count("refclk")) { + xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["refclk"] ), XTRX_CLKSRC_INT); + } + if (dict.count("extclk")) { + xtrx_set_ref_clk(_xtrx->dev(), boost::lexical_cast< unsigned >( dict["extclk"] ), XTRX_CLKSRC_EXT); + } + + if (dict.count("vio")) { + unsigned vio = boost::lexical_cast< unsigned >( dict["vio"] ); + _xtrx->set_vio(vio); + } + + if (dict.count("dac")) { + unsigned dac = boost::lexical_cast< unsigned >( dict["dac"] ); + xtrx_val_set(_xtrx->dev(), XTRX_TRX, XTRX_CH_ALL, XTRX_VCTCXO_DAC_VAL, dac); + } + + if (dict.count("pmode")) { + unsigned pmode = boost::lexical_cast< unsigned >( dict["pmode"] ); + xtrx_val_set(_xtrx->dev(), XTRX_TRX, XTRX_CH_ALL, XTRX_LMS7_PWR_MODE, pmode); + } + + if (dict.count("timekey")) { + _timekey = boost::lexical_cast< bool >( dict["timekey"] ); + } + + std::cerr << "xtrx_source_c::xtrx_source_c()" << std::endl; + set_alignment(32); + if (_otw == XTRX_WF_16) { + if (_mimo_mode) + set_output_multiple(4096); + else + set_output_multiple(8192); + } else if (_otw == XTRX_WF_8) { + if (_mimo_mode) + set_output_multiple(8192); + else + set_output_multiple(16384); + } +} + +xtrx_source_c::~xtrx_source_c() +{ + std::cerr << "xtrx_source_c::~xtrx_source_c()" << std::endl; +} + +std::string xtrx_source_c::name() +{ + return "GrLibXTRX"; +} + +size_t xtrx_source_c::get_num_channels( void ) +{ + return output_signature()->max_streams(); +} + +osmosdr::meta_range_t xtrx_source_c::get_sample_rates( void ) +{ + osmosdr::meta_range_t range; + range += osmosdr::range_t( 200000, 160000000, 1 ); + return range; +} + +double xtrx_source_c::set_sample_rate( double rate ) +{ + std::cerr << "Set sample rate " << rate << std::endl; + _rate = _xtrx->set_smaplerate(rate, _master, false, _sample_flags); + return get_sample_rate(); +} + +double xtrx_source_c::get_sample_rate( void ) +{ + return _rate; +} + +osmosdr::freq_range_t xtrx_source_c::get_freq_range( size_t chan ) +{ + osmosdr::freq_range_t range; + range += osmosdr::range_t( double(0.03e9), double(3.8e9), 1); // as far as we know + return range; +} + +double xtrx_source_c::set_center_freq( double freq, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + _freq = freq; + double corr_freq = (freq)*(1.0 + (_corr) * 0.000001); + + if (_tdd) + return get_center_freq(chan); + + xtrx_channel_t xchan = (xtrx_channel_t)(XTRX_CH_A << chan); + + std::cerr << "Set freq " << freq << std::endl; + + int res = xtrx_tune_ex(_xtrx->dev(), XTRX_TUNE_RX_FDD, xchan, corr_freq - _dsp, &_freq); + if (res) { + std::cerr << "Unable to deliver frequency " << corr_freq << std::endl; + } + + res = xtrx_tune_ex(_xtrx->dev(), XTRX_TUNE_BB_RX, xchan, _dsp, NULL); + + return get_center_freq(chan); +} + +double xtrx_source_c::get_center_freq( size_t chan ) +{ + return _freq; +} + +double xtrx_source_c::set_freq_corr( double ppm, size_t chan ) +{ + _corr = ppm; + + set_center_freq(_freq, chan); + + return get_freq_corr( chan ); +} + +double xtrx_source_c::get_freq_corr( size_t chan ) +{ + return _corr; +} + +static const std::map s_lna_map = boost::assign::map_list_of + ("LNA", XTRX_RX_LNA_GAIN) + ("TIA", XTRX_RX_TIA_GAIN) + ("PGA", XTRX_RX_PGA_GAIN) + ("LB", XTRX_RX_LB_GAIN) + ; + +static xtrx_gain_type_t get_gain_type(const std::string& name) +{ + std::map::const_iterator it; + + it = s_lna_map.find(name); + if (it != s_lna_map.end()) { + return it->second; + } + + return XTRX_RX_LNA_GAIN; +} + +static const std::vector s_lna_list = boost::assign::list_of + ("LNA")("TIA")("PGA")("LB") + ; + +std::vector xtrx_source_c::get_gain_names( size_t chan ) +{ + return s_lna_list; +} + +osmosdr::gain_range_t xtrx_source_c::get_gain_range( size_t chan ) +{ + return get_gain_range("LNA", chan); +} + +osmosdr::gain_range_t xtrx_source_c::get_gain_range( const std::string & name, size_t chan ) +{ + osmosdr::gain_range_t range; + + if (name == "LNA") { + range += osmosdr::range_t( 0, 24, 3 ); + range += osmosdr::range_t( 25, 30, 1 ); + } else if (name == "TIA") { + range += osmosdr::range_t( 0 ); + range += osmosdr::range_t( 9 ); + range += osmosdr::range_t( 12 ); + } else if (name == "PGA") { + range += osmosdr::range_t( -12.5, 12.5, 1 ); + } else if (name == "LB") { + range += osmosdr::range_t( -40, 0, 1 ); + } + + return range; +} + +bool xtrx_source_c::set_gain_mode( bool automatic, size_t chan ) +{ + _auto_gain = automatic; + return get_gain_mode(chan); +} + +bool xtrx_source_c::get_gain_mode( size_t chan ) +{ + return _auto_gain; +} + +double xtrx_source_c::set_gain( double gain, size_t chan ) +{ + return set_gain(gain, "LNA", chan); +} + +double xtrx_source_c::set_gain( double igain, const std::string & name, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + osmosdr::gain_range_t gains = xtrx_source_c::get_gain_range( name, chan ); + double gain = gains.clip(igain); + double actual_gain; + xtrx_gain_type_t gt = get_gain_type(name); + + std::cerr << "Set gain " << name << " (" << gt << "): " << igain << std::endl; + + int res = xtrx_set_gain(_xtrx->dev(), (xtrx_channel_t)(XTRX_CH_A << chan), + gt, gain, &actual_gain); + if (res) { + std::cerr << "Unable to set gain `" << name.c_str() << "`; err=" << res << std::endl; + } + + switch (gt) { + case XTRX_RX_LNA_GAIN: _gain_lna = actual_gain; break; + case XTRX_RX_TIA_GAIN: _gain_tia = actual_gain; break; + case XTRX_RX_PGA_GAIN: _gain_pga = actual_gain; break; + default: break; + } + + return actual_gain; +} + +double xtrx_source_c::get_gain( size_t chan ) +{ + return get_gain("LNA"); +} + +double xtrx_source_c::get_gain( const std::string & name, size_t chan ) +{ + xtrx_gain_type_t gt = get_gain_type(name); + switch (gt) { + case XTRX_RX_LNA_GAIN: return _gain_lna; + case XTRX_RX_TIA_GAIN: return _gain_tia; + case XTRX_RX_PGA_GAIN: return _gain_pga; + default: return 0; + } +} + +double xtrx_source_c::set_if_gain(double gain, size_t chan) +{ + return set_gain(gain, "PGA", chan); +} + +double xtrx_source_c::set_bandwidth( double bandwidth, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + std::cerr << "Set bandwidth " << bandwidth << " chan " << chan << std::endl; + + if (bandwidth <= 0.0) { + bandwidth = get_sample_rate() * 0.75; + if (bandwidth < 0.5e6) { + bandwidth = 0.5e6; + } + } + + int res = xtrx_tune_rx_bandwidth(_xtrx->dev(), (xtrx_channel_t)(XTRX_CH_A << chan), + bandwidth, &_bandwidth); + if (res) { + std::cerr << "Can't set bandwidth: " << res << std::endl; + } + return get_bandwidth(chan); +} + +double xtrx_source_c::get_bandwidth( size_t chan ) +{ + return _bandwidth; +} + +osmosdr::freq_range_t xtrx_source_c::get_bandwidth_range( size_t chan ) +{ + return osmosdr::freq_range_t(500e3, 140e6, 0); +} + + +static const std::map s_ant_map = boost::assign::map_list_of + ("AUTO", XTRX_RX_AUTO) + ("RXL", XTRX_RX_L) + ("RXH", XTRX_RX_H) + ("RXW", XTRX_RX_W) + ("RXL_LB", XTRX_RX_L_LB) + ("RXW_LB", XTRX_RX_W_LB) + ; +static const std::map s_ant_map_r = boost::assign::map_list_of + (XTRX_RX_AUTO, "AUTO") + (XTRX_RX_L, "RXL") + (XTRX_RX_H, "RXH") + (XTRX_RX_W, "RXW") + (XTRX_RX_L_LB, "RXL_LB") + (XTRX_RX_W_LB, "RXW_LB") + ; + +static xtrx_antenna_t get_ant_type(const std::string& name) +{ + std::map::const_iterator it; + + it = s_ant_map.find(name); + if (it != s_ant_map.end()) { + return it->second; + } + + return XTRX_RX_AUTO; +} + +static const std::vector s_ant_list = boost::assign::list_of + ("AUTO")("RXL")("RXH")("RXW") + ; + + +std::vector< std::string > xtrx_source_c::get_antennas( size_t chan ) +{ + return s_ant_list; +} + +std::string xtrx_source_c::set_antenna( const std::string & antenna, size_t chan ) +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + _ant = get_ant_type(antenna); + + std::cerr << "Set antenna " << antenna << " type:" << _ant << std::endl; + + int res = xtrx_set_antenna_ex(_xtrx->dev(), (xtrx_channel_t)(XTRX_CH_A << chan), + _ant); + if (res) { + std::cerr << "Can't set antenna: " << antenna << std::endl; + } + return get_antenna( chan ); +} + +std::string xtrx_source_c::get_antenna( size_t chan ) +{ + return s_ant_map_r.find(_ant)->second; +} + +int xtrx_source_c::work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + xtrx_recv_ex_info_t ri; + ri.samples = noutput_items; + ri.buffer_count = output_items.size(); + ri.buffers = &output_items[0]; + ri.flags = RCVEX_DONT_INSER_ZEROS | RCVEX_DROP_OLD_ON_OVERFLOW; + ri.timeout = 1000; + + int res = xtrx_recv_sync_ex(_xtrx->dev(), &ri); + if (res) { + std::stringstream message; + message << "xtrx_recv_sync error: " << -res; + throw std::runtime_error( message.str() ); + } + + if (_timekey) { + uint64_t seconds = (ri.out_first_sample / _rate); + double fractional = (ri.out_first_sample - (uint64_t)(_rate * seconds)) / _rate; + + //std::cerr << "Time " << seconds << ":" << fractional << std::endl; + const pmt::pmt_t val = pmt::make_tuple + (pmt::from_uint64(seconds), + pmt::from_double(fractional)); + for(size_t i = 0; i < output_items.size(); i++) { + this->add_item_tag(i, nitems_written(0), TIME_KEY, + val, _id); + this->add_item_tag(i, nitems_written(0), RATE_KEY, + pmt::from_double(_rate), _id); + this->add_item_tag(i, nitems_written(0), FREQ_KEY, + pmt::from_double(this->get_center_freq(i)), _id); + } + } + return ri.out_samples; +} + +bool xtrx_source_c::start() +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + + xtrx_run_params_t params; + xtrx_run_params_init(¶ms); + + params.dir = XTRX_RX; + if (!_mimo_mode) + params.rx.flags |= XTRX_RSP_SISO_MODE; + + if (_swap_ab) + params.rx.flags |= XTRX_RSP_SWAP_AB; + + if (_swap_iq) + params.rx.flags |= XTRX_RSP_SWAP_IQ; + + params.rx.hfmt = XTRX_IQ_FLOAT32; + params.rx.wfmt = _otw; + params.rx.chs = XTRX_CH_AB; + params.rx.paketsize = 0; + params.rx_stream_start = 256*1024; + + params.nflags = (_loopback) ? XTRX_RUN_DIGLOOPBACK : 0; + + int res = xtrx_run_ex(_xtrx->dev(), ¶ms); + if (res) { + std::cerr << "Got error: " << res << std::endl; + } + + res = xtrx_tune_ex(_xtrx->dev(), XTRX_TUNE_BB_RX, XTRX_CH_ALL, _dsp, NULL); + + return res == 0; +} + +bool xtrx_source_c::stop() +{ + boost::mutex::scoped_lock lock(_xtrx->mtx); + //TODO: + std::cerr << "xtrx_source_c::stop()" << std::endl; + int res = xtrx_stop(_xtrx->dev(), XTRX_RX); + if (res) { + std::cerr << "Got error: " << res << std::endl; + } + + return res == 0; +} diff --git a/lib/xtrx/xtrx_source_c.h b/lib/xtrx/xtrx_source_c.h new file mode 100644 index 0000000..fda9d77 --- /dev/null +++ b/lib/xtrx/xtrx_source_c.h @@ -0,0 +1,127 @@ +/* -*- c++ -*- */ +/* + * Copyright 2016,2017 Sergey Kostanbaev + * + * GNU Radio is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ +#ifndef XTRX_SOURCE_C_H +#define XTRX_SOURCE_C_H + +#include +#include + +#include "source_iface.h" +#include "xtrx_obj.h" + +static const pmt::pmt_t TIME_KEY = pmt::string_to_symbol("rx_time"); +static const pmt::pmt_t RATE_KEY = pmt::string_to_symbol("rx_rate"); +static const pmt::pmt_t FREQ_KEY = pmt::string_to_symbol("rx_freq"); + +class xtrx_source_c; + +typedef boost::shared_ptr< xtrx_source_c > xtrx_source_c_sptr; + +xtrx_source_c_sptr make_xtrx_source_c( const std::string & args = "" ); + +class xtrx_source_c : + public gr::sync_block, + public source_iface +{ +private: + friend xtrx_source_c_sptr make_xtrx_source_c(const std::string &args); + + xtrx_source_c(const std::string &args); + +public: + ~xtrx_source_c(); + + std::string name(); + + static std::vector< std::string > get_devices( bool fake = false ) { return xtrx_obj::get_devices(); } + + size_t get_num_channels( void ); + + osmosdr::meta_range_t get_sample_rates( void ); + double set_sample_rate( double rate ); + double get_sample_rate( void ); + + osmosdr::freq_range_t get_freq_range( size_t chan = 0 ); + double set_center_freq( double freq, size_t chan = 0 ); + double get_center_freq( size_t chan = 0 ); + double set_freq_corr( double ppm, size_t chan = 0 ); + double get_freq_corr( size_t chan = 0 ); + + std::vector get_gain_names( size_t chan = 0 ); + osmosdr::gain_range_t get_gain_range( size_t chan = 0 ); + osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 ); + bool set_gain_mode( bool automatic, size_t chan = 0 ); + bool get_gain_mode( size_t chan = 0 ); + double set_gain( double gain, size_t chan = 0 ); + double set_gain( double gain, const std::string & name, size_t chan = 0 ); + double get_gain( size_t chan = 0 ); + double get_gain( const std::string & name, size_t chan = 0 ); + + double set_if_gain( double gain, size_t chan = 0 ); + + std::vector< std::string > get_antennas( size_t chan = 0 ); + std::string set_antenna( const std::string & antenna, size_t chan = 0 ); + std::string get_antenna( size_t chan = 0 ); + + double set_bandwidth( double bandwidth, size_t chan = 0 ); + double get_bandwidth( size_t chan = 0 ); + osmosdr::freq_range_t get_bandwidth_range( size_t chan = 0); + + int work (int noutput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); + + bool start(); + bool stop(); + +private: + xtrx_obj_sptr _xtrx; + pmt::pmt_t _id; + + unsigned _sample_flags; + double _rate; + double _master; + double _freq; + double _corr; + double _bandwidth; + bool _auto_gain; + + xtrx_wire_format_t _otw; + bool _mimo_mode; + + int _gain_lna; + int _gain_tia; + int _gain_pga; + + unsigned _channels; + xtrx_antenna_t _ant; + + bool _swap_ab; + bool _swap_iq; + bool _loopback; + bool _tdd; + bool _fbctrl; + bool _timekey; + + double _dsp; + std::string _dev; +}; + +#endif // XTRX_SOURCE_C_H