From 3ce7c3398102e49f5fe7411a8e0fb5700236a3c7 Mon Sep 17 00:00:00 2001 From: Dimitri Stolnikov Date: Sat, 13 Jul 2013 14:13:41 +0200 Subject: [PATCH] fcd: add support for FUNcube Dongle Pro+ The gnuradio block https://github.com/dl1ksv/gr-fcdproplus must be installed before building gr-osmosdr. Available named gains: Dongle Classic: LNA: -5 to 30 dB, in 2.5 dB steps MIX: 4 or 12 dB Dongle Pro+: LNA: 0 or 1, meaning off/on only. no information about real values. MIX: 0 or 1, meaning off/on only. no information about real values. BB: 0 to 59 dB, in 1 dB steps This patch also introduces optional "device" and "type" arguments which allow to override the values automatically picked by gr-osmosdr: osmocom_fft -a "fcd,device=hw:2,type=2" The "device" argument overrides the audio device used by the underlying driver to access the dongle's IQ sample stream. The "type" argument selects the dongle type, 1 for Classic, 2 for Pro+. Thanks to Alexey Bazhin for the initial patch and Volker Schroer for testing. --- CMakeLists.txt | 1 + README | 3 +- cmake/Modules/FindGnuradioFCDPP.cmake | 34 ++++ grc/gen_osmosdr_blocks.py | 5 +- lib/CMakeLists.txt | 11 +- lib/fcd/CMakeLists.txt | 21 ++- lib/fcd/fcd_source_c.cc | 250 ++++++++++++++++++++++---- lib/fcd/fcd_source_c.h | 26 ++- 8 files changed, 307 insertions(+), 44 deletions(-) create mode 100644 cmake/Modules/FindGnuradioFCDPP.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 38abe5c..49dd315 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,6 +149,7 @@ find_package(GnuradioIQBalance) find_package(UHD) find_package(GnuradioUHD) find_package(GnuradioFCD) +find_package(GnuradioFCDPP) find_package(LibOsmoSDR) find_package(LibRTLSDR) find_package(LibMiriSDR) diff --git a/README b/README index d10c245..c384cc4 100644 --- a/README +++ b/README @@ -1,7 +1,8 @@ While primarily being developed for the OsmoSDR hardware, this block as well supports: - * FunCube Dongle through libgnuradio-fcd + * FUNcube Dongle through libgnuradio-fcd + * FUNcube Dongle Pro+ through gr-fcdproplus * sysmocom OsmoSDR Devices through libosmosdr * Great Scott Gadgets HackRF through libhackrf * Ettus USRP Devices through Ettus UHD library diff --git a/cmake/Modules/FindGnuradioFCDPP.cmake b/cmake/Modules/FindGnuradioFCDPP.cmake new file mode 100644 index 0000000..c6f03f1 --- /dev/null +++ b/cmake/Modules/FindGnuradioFCDPP.cmake @@ -0,0 +1,34 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_GNURADIO_FCDPP gnuradio-fcdproplus) + +FIND_PATH( + GNURADIO_FCDPP_INCLUDE_DIRS + NAMES fcdproplus/api.h + HINTS $ENV{GNURADIO_FCDPP_DIR}/include + ${PC_GNURADIO_FCDPP_INCLUDEDIR} + PATHS /usr/local/include + /usr/include +) + +FIND_LIBRARY( + GNURADIO_FCDPP_LIBRARIES + NAMES gnuradio-fcdproplus + HINTS $ENV{GNURADIO_FCDPP_DIR}/lib + ${PC_GNURADIO_FCDPP_LIBDIR} + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +if(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) + set(GNURADIO_FCDPP_FOUND TRUE CACHE INTERNAL "gnuradio-fcdproplus found") + message(STATUS "Found gnuradio-fcdproplus: ${GNURADIO_FCDPP_INCLUDE_DIRS}, ${GNURADIO_FCDPP_LIBRARIES}") +else(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) + set(GNURADIO_FCDPP_FOUND FALSE CACHE INTERNAL "gnuradio-fcdproplus found") + message(STATUS "gnuradio-fcdproplus not found.") +endif(GNURADIO_FCDPP_INCLUDE_DIRS AND GNURADIO_FCDPP_LIBRARIES) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_FCDPP DEFAULT_MSG GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS) +MARK_AS_ADVANCED(GNURADIO_FCDPP_LIBRARIES GNURADIO_FCDPP_INCLUDE_DIRS) diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index bad04b2..a579aa6 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -115,7 +115,8 @@ The osmocom $sourk block: While primarily being developed for the OsmoSDR hardware, this block as well supports: #if $sourk == 'source': - * FunCube Dongle through libgnuradio-fcd + * FUNcube Dongle through libgnuradio-fcd + * FUNcube Dongle Pro+ through gr-fcdproplus * sysmocom OsmoSDR Devices through libosmosdr #end if * Great Scott Gadgets HackRF through libhackrf @@ -142,7 +143,7 @@ Optional arguments are placed into [] brackets, remove the brackets before using Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space. #if $sourk == 'source': - fcd=0 + fcd=0[,device=hw:2][,type=2] hackrf=0[,buffers=32] miri=0[,buffers=32] ... rtl=serial_number ... diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 25a4368..ffbf7a8 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -70,10 +70,17 @@ endif(ENABLE_OSMOSDR) ######################################################################## # Setup FCD component ######################################################################## -GR_REGISTER_COMPONENT("FunCube Dongle" ENABLE_FCD GNURADIO_FCD_FOUND) +GR_REGISTER_COMPONENT("FUNcube Dongle" ENABLE_FCD GNURADIO_FCD_FOUND) +GR_REGISTER_COMPONENT("FUNcube Dongle Pro+" ENABLE_FCDPP GNURADIO_FCDPP_FOUND) if(ENABLE_FCD) -GR_INCLUDE_SUBDIRECTORY(fcd) +add_definitions(-DHAVE_FCD=1) endif(ENABLE_FCD) +if(ENABLE_FCDPP) +add_definitions(-DHAVE_FCDPP=1) +endif(ENABLE_FCDPP) +if(ENABLE_FCD OR ENABLE_FCDPP) +GR_INCLUDE_SUBDIRECTORY(fcd) +endif(ENABLE_FCD OR ENABLE_FCDPP) ######################################################################## # Setup File component diff --git a/lib/fcd/CMakeLists.txt b/lib/fcd/CMakeLists.txt index 94581f4..e71b153 100644 --- a/lib/fcd/CMakeLists.txt +++ b/lib/fcd/CMakeLists.txt @@ -21,10 +21,15 @@ # This file included, use CMake directory variables ######################################################################## -include_directories( - ${CMAKE_CURRENT_SOURCE_DIR} - ${GNURADIO_FCD_INCLUDE_DIRS} -) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}) + +if(ENABLE_FCD) +include_directories(${GNURADIO_FCD_INCLUDE_DIRS}) +endif(ENABLE_FCD) + +if(ENABLE_FCDPP) +include_directories(${GNURADIO_FCDPP_INCLUDE_DIRS}) +endif(ENABLE_FCDPP) set(fcd_srcs ${CMAKE_CURRENT_SOURCE_DIR}/fcd_source_c.cc @@ -34,5 +39,11 @@ set(fcd_srcs # Append gnuradio-osmosdr library sources ######################################################################## list(APPEND gr_osmosdr_srcs ${fcd_srcs}) -list(APPEND gr_osmosdr_libs ${GNURADIO_FCD_LIBRARIES}) +if(ENABLE_FCD) +list(APPEND gr_osmosdr_libs ${GNURADIO_FCD_LIBRARIES}) +endif(ENABLE_FCD) + +if(ENABLE_FCDPP) +list(APPEND gr_osmosdr_libs ${GNURADIO_FCDPP_LIBRARIES}) +endif(ENABLE_FCDPP) diff --git a/lib/fcd/fcd_source_c.cc b/lib/fcd/fcd_source_c.cc index c5a89b6..885d514 100644 --- a/lib/fcd/fcd_source_c.cc +++ b/lib/fcd/fcd_source_c.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Dimitri Stolnikov + * Copyright 2013 Dimitri Stolnikov * * 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 @@ -42,10 +42,17 @@ fcd_source_c_sptr make_fcd_source_c(const std::string &args) 2 [V10 ]: USB-Audio - FUNcube Dongle V1.0 Hanlincrest Ltd. FUNcube Dongle V1.0 at usb-0000:00:1d.0-2, full speed */ +/* + 2 [V20 ]: USB-Audio - FUNcube Dongle V2.0 + Hanlincrest Ltd. FUNcube Dongle V2.0 at usb-0000:00:1d.0-2, full speed + */ -static std::vector< std::string > _get_devices() +typedef std::pair< fcd_source_c::dongle_type, std::string > device_t; +typedef std::vector< device_t > devices_t; + +static devices_t _get_devices() /* FIXME: non-portable way to discover dongles */ { - std::vector< std::string > devices; + devices_t devices; std::string line; std::ifstream cards( "/proc/asound/cards" ); @@ -55,15 +62,23 @@ static std::vector< std::string > _get_devices() { getline (cards, line); - if ( line.find( "USB-Audio - FUNcube Dongle" ) != std::string::npos ) + fcd_source_c::dongle_type type = fcd_source_c::FUNCUBE_UNKNOWN; + + if ( line.find( "USB-Audio - FUNcube Dongle V1.0" ) != std::string::npos ) + type = fcd_source_c::FUNCUBE_V1; + + if ( line.find( "USB-Audio - FUNcube Dongle V2.0" ) != std::string::npos ) + type = fcd_source_c::FUNCUBE_V2; + + if ( type != fcd_source_c::FUNCUBE_UNKNOWN ) { int id; std::istringstream( line ) >> id; std::ostringstream hw_id; - hw_id << "hw:" << id; // build alsa identifier + hw_id << "hw:" << id; /* build alsa identifier */ - devices += hw_id.str(); + devices += device_t( type, hw_id.str() ); } } @@ -76,7 +91,8 @@ static std::vector< std::string > _get_devices() fcd_source_c::fcd_source_c(const std::string &args) : gr::hier_block2("fcd_source_c", gr::io_signature::make(0, 0, 0), - gr::io_signature::make(1, 1, sizeof (gr_complex))) + gr::io_signature::make(1, 1, sizeof (gr_complex))), + _type( FUNCUBE_UNKNOWN ) { std::string dev_name; unsigned int dev_index = 0; @@ -84,18 +100,70 @@ fcd_source_c::fcd_source_c(const std::string &args) : dict_t dict = params_to_dict(args); if (dict.count("fcd")) - dev_index = boost::lexical_cast< unsigned int >( dict["fcd"] ); + { + std::string value = dict["fcd"]; + if ( value.length() ) + { + try { + dev_index = boost::lexical_cast< unsigned int >( value ); + } catch ( std::exception &ex ) { + throw std::runtime_error( + "Failed to use '" + value + "' as index: " + ex.what()); + } + } + } - std::vector< std::string > devices = _get_devices(); + if (dict.count("device")) + { + dev_name = dict["device"]; + _type = FUNCUBE_V1; + } + + if (dict.count("type")) + { + _type = (dongle_type) boost::lexical_cast< int >( dict["type"] ); + + if ( FUNCUBE_V1 != _type && FUNCUBE_V2 != _type ) + throw std::runtime_error("FUNcube Dongle type must be 1 or 2."); + } + + devices_t devices = _get_devices(); if ( devices.size() ) - dev_name = devices[dev_index]; - else - throw std::runtime_error("No FunCube Dongle found."); + { + if ( FUNCUBE_UNKNOWN == _type ) + _type = devices[dev_index].first; - _src = gr::fcd::source_c::make( dev_name ); + if ( dev_name.length() == 0 ) + dev_name = devices[dev_index].second; + } + else if ( dev_name.length() == 0 ) + throw std::runtime_error("No FUNcube Dongle found."); - connect( _src, 0, self(), 0 ); + std::cerr << "Using " << name() << " (" << dev_name << ")" << std::endl; + +#ifdef HAVE_FCD + if ( FUNCUBE_V1 == _type ) + { + _src_v1 = gr::fcd::source_c::make( dev_name ); + connect( _src_v1, 0, self(), 0 ); + + set_gain( 20, "LNA" ); + set_gain( 12, "MIX" ); + } +#endif + +#ifdef HAVE_FCDPP + if ( FUNCUBE_V2 == _type ) + { + _src_v2 = gr::fcdproplus::fcdproplus::make( dev_name ); + connect( _src_v2, 0, self(), 0 ); + + set_gain( 1, "LNA" ); + set_gain( 1, "MIX" ); + set_gain( 15, "BB" ); + } +#endif } fcd_source_c::~fcd_source_c() @@ -107,9 +175,15 @@ std::vector< std::string > fcd_source_c::get_devices() int id = 0; std::vector< std::string > devices; - BOOST_FOREACH( std::string dev, _get_devices() ) { + BOOST_FOREACH( device_t dev, _get_devices() ) + { std::string args = "fcd=" + boost::lexical_cast< std::string >( id++ ); - args += ",label='FunCube Dongle'"; + + if ( dev.first == fcd_source_c::FUNCUBE_V1 ) + args += ",label='FUNcube Dongle V1.0'"; + else if ( dev.first == fcd_source_c::FUNCUBE_V2 ) + args += ",label='FUNcube Dongle V2.0'"; + devices.push_back( args ); } @@ -118,7 +192,12 @@ std::vector< std::string > fcd_source_c::get_devices() std::string fcd_source_c::name() { - return "FUNcube Dongle"; + if ( FUNCUBE_V1 == _type ) + return "FUNcube Dongle V1.0"; + else if ( FUNCUBE_V2 == _type ) + return "FUNcube Dongle V2.0"; + + return ""; } size_t fcd_source_c::get_num_channels( void ) @@ -142,19 +221,35 @@ double fcd_source_c::set_sample_rate( double rate ) double fcd_source_c::get_sample_rate( void ) { - return 96e3; + if ( FUNCUBE_V1 == _type ) + return 96e3; + else if ( FUNCUBE_V2 == _type ) + return 192e3; + + return 0; } osmosdr::freq_range_t fcd_source_c::get_freq_range( size_t chan ) { - osmosdr::freq_range_t range( 52e6, 2.2e9 ); + if ( FUNCUBE_V1 == _type ) + return osmosdr::freq_range_t( 52e6, 2.2e9 ); + else if ( FUNCUBE_V2 == _type ) + return osmosdr::freq_range_t( 150e3, 2.05e9 ); - return range; + return osmosdr::freq_range_t(); } double fcd_source_c::set_center_freq( double freq, size_t chan ) { - _src->set_freq(float(freq)); +#ifdef HAVE_FCD + if ( FUNCUBE_V1 == _type ) + _src_v1->set_freq( float(freq) ); +#endif + +#ifdef HAVE_FCDPP + if ( FUNCUBE_V2 == _type ) + _src_v2->set_freq( float(freq) ); +#endif _freq = freq; @@ -168,7 +263,15 @@ double fcd_source_c::get_center_freq( size_t chan ) double fcd_source_c::set_freq_corr( double ppm, size_t chan ) { - _src->set_freq_corr( ppm ); +#ifdef HAVE_FCD + if ( FUNCUBE_V1 == _type ) + _src_v1->set_freq_corr( ppm ); +#endif + +#ifdef HAVE_FCDPP + if ( FUNCUBE_V2 == _type ) + _src_v2->set_freq_corr( ppm ); +#endif _correct = ppm; @@ -185,44 +288,131 @@ std::vector fcd_source_c::get_gain_names( size_t chan ) std::vector< std::string > names; names += "LNA"; + names += "MIX"; + + if ( FUNCUBE_V2 == _type ) + names += "BB"; return names; } osmosdr::gain_range_t fcd_source_c::get_gain_range( size_t chan ) { - osmosdr::gain_range_t range(-5, 30, 2.5); + std::string name = ""; - return range; + if ( FUNCUBE_V1 == _type ) + name = "LNA"; /* use LNA gain for V1 dongle */ + else if ( FUNCUBE_V2 == _type ) + name = "BB"; /* use BB gain for V2 dongle */ + + return get_gain_range( name, chan ); } osmosdr::gain_range_t fcd_source_c::get_gain_range( const std::string & name, size_t chan ) { - return get_gain_range( chan ); + if ( FUNCUBE_V1 == _type ) + { + if ( "LNA" == name ) + return osmosdr::gain_range_t(-5, 30, 2.5); + else if ( "MIX" == name ) + return osmosdr::gain_range_t(4, 12, 8); + } + else if ( FUNCUBE_V2 == _type ) + { + if ( "LNA" == name ) + return osmosdr::gain_range_t(0, 1, 1); + else if ( "MIX" == name ) + return osmosdr::gain_range_t(0, 1, 1); + else if ( "BB" == name ) + return osmosdr::gain_range_t(0, 59, 1); + } + + return osmosdr::gain_range_t(); } double fcd_source_c::set_gain( double gain, size_t chan ) { - _src->set_lna_gain(gain); + if ( FUNCUBE_V1 == _type ) + _lna_gain = set_gain( gain, "LNA" ); - _gain = gain; + if ( FUNCUBE_V2 == _type ) + _bb_gain = set_gain( gain, "BB" ); return get_gain(chan); } double fcd_source_c::set_gain( double gain, const std::string & name, size_t chan ) { - return set_gain(chan); +#ifdef HAVE_FCD + if ( FUNCUBE_V1 == _type ) + { + if ( "LNA" == name ) + { + _lna_gain = gain; + _src_v1->set_lna_gain(_lna_gain); + } + else if ( "MIX" == name ) + { + _mix_gain = gain > 4 ? 12 : 4; + _src_v1->set_mixer_gain(_mix_gain); + } + } +#endif + +#ifdef HAVE_FCDPP + if ( FUNCUBE_V2 == _type ) + { + if ( "LNA" == name ) + { + _lna_gain = gain > 0 ? 1 : 0; + _src_v2->set_lna(_lna_gain); + } + else if ( "MIX" == name ) + { + _mix_gain = gain > 0 ? 1 : 0; + _src_v2->set_mixer_gain(_mix_gain); + } + else if ( "BB" == name ) + { + _bb_gain = gain; + _src_v2->set_if_gain(_bb_gain); + } + } +#endif + + return get_gain( name, chan ); } double fcd_source_c::get_gain( size_t chan ) { - return _gain; + if ( FUNCUBE_V1 == _type ) + return get_gain( "LNA", chan ); + else if ( FUNCUBE_V2 == _type ) + return get_gain( "BB", chan ); + + return 0; } double fcd_source_c::get_gain( const std::string & name, size_t chan ) { - return get_gain(chan); + if ( FUNCUBE_V1 == _type ) + { + if ( "LNA" == name ) + return _lna_gain; + else if ( "MIX" == name ) + return _mix_gain; + } + else if ( FUNCUBE_V2 == _type ) + { + if ( "LNA" == name ) + return _lna_gain; + else if ( "MIX" == name ) + return _mix_gain; + else if ( "BB" == name ) + return _bb_gain; + } + + return 0; } std::vector< std::string > fcd_source_c::get_antennas( size_t chan ) diff --git a/lib/fcd/fcd_source_c.h b/lib/fcd/fcd_source_c.h index c134a4c..70239f8 100644 --- a/lib/fcd/fcd_source_c.h +++ b/lib/fcd/fcd_source_c.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2012 Dimitri Stolnikov + * Copyright 2013 Dimitri Stolnikov * * 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 @@ -22,7 +22,13 @@ #include +#ifdef HAVE_FCD #include +#endif + +#ifdef HAVE_FCDPP +#include +#endif #include "source_iface.h" @@ -44,6 +50,12 @@ private: public: ~fcd_source_c(); + enum dongle_type { + FUNCUBE_UNKNOWN, + FUNCUBE_V1, + FUNCUBE_V2 + }; + static std::vector< std::string > get_devices(); std::string name(); @@ -73,9 +85,15 @@ public: std::string get_antenna( size_t chan = 0 ); private: - gr::fcd::source_c::sptr _src; - double _gain, _freq; - int32_t _correct; + dongle_type _type; +#ifdef HAVE_FCD + gr::fcd::source_c::sptr _src_v1; +#endif +#ifdef HAVE_FCDPP + gr::fcdproplus::fcdproplus::sptr _src_v2; +#endif + double _lna_gain, _mix_gain, _bb_gain, _freq; + int _correct; }; #endif // FCD_SOURCE_C_H