diff --git a/CMakeLists.txt b/CMakeLists.txt index 2571f8e..60e0bf4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,7 @@ set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks) ######################################################################## find_package(Gruel) find_package(GnuradioCore) +find_package(GnuradioIQBalance) find_package(UHD) find_package(GnuradioUHD) find_package(GnuradioFCD) diff --git a/cmake/Modules/FindGnuradioIQBalance.cmake b/cmake/Modules/FindGnuradioIQBalance.cmake new file mode 100644 index 0000000..642ee3f --- /dev/null +++ b/cmake/Modules/FindGnuradioIQBalance.cmake @@ -0,0 +1,29 @@ +INCLUDE(FindPkgConfig) +PKG_CHECK_MODULES(PC_GNURADIO_IQBALANCE gnuradio-iqbalance) + +FIND_PATH( + GNURADIO_IQBALANCE_INCLUDE_DIRS + NAMES iqbalance_api.h + HINTS $ENV{GNURADIO_IQBALANCE_DIR}/include/iqbalance + ${PC_GNURADIO_IQBALANCE_INCLUDEDIR} + ${CMAKE_INSTALL_PREFIX}/include/iqbalance + PATHS /usr/local/include/iqbalance + /usr/include/iqbalance +) + +FIND_LIBRARY( + GNURADIO_IQBALANCE_LIBRARIES + NAMES gnuradio-iqbalance + HINTS $ENV{GNURADIO_IQBALANCE_DIR}/lib + ${PC_GNURADIO_IQBALANCE_LIBDIR} + ${CMAKE_INSTALL_PREFIX}/lib64 + ${CMAKE_INSTALL_PREFIX}/lib + PATHS /usr/local/lib + /usr/local/lib64 + /usr/lib + /usr/lib64 +) + +INCLUDE(FindPackageHandleStandardArgs) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(GNURADIO_IQBALANCE DEFAULT_MSG GNURADIO_IQBALANCE_LIBRARIES GNURADIO_IQBALANCE_INCLUDE_DIRS) +MARK_AS_ADVANCED(GNURADIO_IQBALANCE_LIBRARIES GNURADIO_IQBALANCE_INCLUDE_DIRS) diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index c060d20..e317482 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -32,6 +32,7 @@ self.\$(id).set_sample_rate(\$sample_rate) \#if \$nchan() > $n self.\$(id).set_center_freq(\$freq$(n), $n) self.\$(id).set_freq_corr(\$corr$(n), $n) +self.\$(id).set_iq_balance_mode(\$iq_balance_mode$(n), $n) self.\$(id).set_gain_mode(\$gain_mode$(n), $n) self.\$(id).set_gain(\$gain$(n), $n) self.\$(id).set_if_gain(\$if_gain$(n), $n) @@ -45,6 +46,7 @@ self.\$(id).set_antenna(\$ant$(n), $n) #for $n in range($max_nchan) set_center_freq(\$freq$(n), $n) set_freq_corr(\$corr$(n), $n) + set_iq_balance_mode(\$iq_balance_mode$(n), $n) set_gain_mode(\$gain_mode$(n), $n) set_gain(\$gain$(n), $n) set_if_gain(\$if_gain$(n), $n) @@ -149,6 +151,14 @@ The center frequency is the frequency the RF chain is tuned to. Freq. Corr.: The frequency correction factor in parts per million (ppm). Set to 0 if unknown. +IQ Balance Mode: +Controls the behavior of software IQ imbalance corrrection. + Off: Disable correction algorithm (pass through) + Manual: Keep last estimated correction when switched from Automatic to Manual + Automatic: Find the best solution to compensate for image signals. + +This functionality depends on http://cgit.osmocom.org/cgit/gr-iqbal/ + Gain Mode: Chooses between the manual (default) and automatic gain mode where appropriate. Currently, only rtlsdr devices support automatic gain mode. @@ -187,6 +197,25 @@ PARAMS_TMPL = """ real \#if \$nchan() > $n then 'none' else 'all'# + + Ch$(n): IQ Balance Mode + iq_balance_mode$(n) + 0 + int + \#if \$nchan() > $n then 'none' else 'all'# + + + + Ch$(n): Gain Mode gain_mode$(n) @@ -198,7 +227,7 @@ PARAMS_TMPL = """ 0 diff --git a/include/osmosdr/osmosdr_source_c.h b/include/osmosdr/osmosdr_source_c.h index 55a9a0f..a2b598c 100644 --- a/include/osmosdr/osmosdr_source_c.h +++ b/include/osmosdr/osmosdr_source_c.h @@ -229,6 +229,30 @@ public: * \return antenna the actual antenna's name */ virtual std::string get_antenna( size_t chan = 0 ) = 0; + + enum IQBalanceMode { + IQBalanceOff = 0, + IQBalanceManual, + IQBalanceAutomatic + }; + + /*! + * Set the RX frontend IQ balance mode. + * + * \param mode iq balance correction mode: 0 = Off, 1 = Manual, 2 = Automatic + * \param chan the channel index 0 to N-1 + */ + virtual void set_iq_balance_mode( int mode, size_t chan = 0 ) = 0; + + /*! + * Set the RX frontend IQ balance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction value + * \param chan the channel index 0 to N-1 + */ + virtual void set_iq_balance( const std::complex &correction, + size_t chan = 0 ) = 0; }; #endif /* INCLUDED_OSMOSDR_SOURCE_C_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 494d498..4f9d7d3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -47,6 +47,13 @@ GR_OSMOSDR_APPEND_LIBS( ${GNURADIO_CORE_LIBRARIES} ) +if(GNURADIO_IQBALANCE_FOUND) + message(STATUS "Will build with gnuradio iqbalance support.") + add_definitions(-DHAVE_IQBALANCE=1) + include_directories(APPEND ${GNURADIO_IQBALANCE_INCLUDE_DIRS}) + GR_OSMOSDR_APPEND_LIBS(${GNURADIO_IQBALANCE_LIBRARIES}) +endif() + ######################################################################## # Setup OsmoSDR component ######################################################################## diff --git a/lib/osmosdr_source_c_impl.cc b/lib/osmosdr_source_c_impl.cc index ba17a83..708831c 100644 --- a/lib/osmosdr_source_c_impl.cc +++ b/lib/osmosdr_source_c_impl.cc @@ -173,70 +173,81 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args) // BOOST_FOREACH( dict_t::value_type &entry, dict ) // std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl; + osmosdr_src_iface *iface = NULL; + gr_basic_block_sptr block; + #ifdef ENABLE_OSMOSDR if ( dict.count("osmosdr") ) { osmosdr_src_c_sptr src = osmosdr_make_src_c( arg ); - - for (size_t i = 0; i < src->get_num_channels(); i++) - connect(src, i, self(), channel++); - - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_FCD if ( dict.count("fcd") ) { fcd_source_sptr src = make_fcd_source( arg ); - connect(src, 0, self(), channel++); - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_FILE if ( dict.count("file") ) { file_source_c_sptr src = make_file_source_c( arg ); - connect(src, 0, self(), channel++); - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_RTL if ( dict.count("rtl") ) { rtl_source_c_sptr src = make_rtl_source_c( arg ); - connect(src, 0, self(), channel++); - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_RTL_TCP if ( dict.count("rtl_tcp") ) { rtl_tcp_source_c_sptr src = make_rtl_tcp_source_c( arg ); - connect(src, 0, self(), channel++); - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_UHD if ( dict.count("uhd") ) { uhd_source_c_sptr src = make_uhd_source_c( arg ); - - for (size_t i = 0; i < src->get_num_channels(); i++) - connect(src, i, self(), channel++); - - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif #ifdef ENABLE_MIRI if ( dict.count("miri") ) { miri_source_c_sptr src = make_miri_source_c( arg ); - - for (size_t i = 0; i < src->get_num_channels(); i++) - connect(src, i, self(), channel++); - - _devs.push_back( src.get() ); + block = src; iface = src.get(); } #endif + + if ( iface != NULL && long(block.get()) != 0 ) { + _devs.push_back( iface ); + + for (size_t i = 0; i < iface->get_num_channels(); i++) { +#ifdef HAVE_IQBALANCE + iqbalance_optimize_c_sptr iq_opt = iqbalance_make_optimize_c( 0 ); + iqbalance_fix_cc_sptr iq_fix = iqbalance_make_fix_cc(); + + connect(block, i, iq_fix, 0); + connect(iq_fix, 0, self(), channel++); + + connect(block, i, iq_opt, 0); + msg_connect(iq_opt, "iqbal_corr", iq_fix, "iqbal_corr"); + + _iq_opt.push_back( iq_opt.get() ); + _iq_fix.push_back( iq_fix.get() ); +#else + connect(block, i, self(), channel++); +#endif + } + } else if ( (iface != NULL) || (long(block.get()) != 0) ) + throw std::runtime_error("Eitner iface or block are NULL."); + } if (!_devs.size()) @@ -305,6 +316,24 @@ double osmosdr_source_c_impl::set_sample_rate(double rate) BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) sample_rate = dev->set_sample_rate(rate); +#ifdef HAVE_IQBALANCE + size_t channel = 0; + BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) { + for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { + if ( channel < _iq_opt.size() ) { + iqbalance_optimize_c *opt = _iq_opt[channel]; + + if ( opt->period() > 0 ) { /* optimize is enabled */ + opt->set_period( dev->get_sample_rate() / 5 ); + opt->reset(); + } + } + + channel++; + } + } +#endif + _sample_rate = sample_rate; } @@ -542,3 +571,61 @@ std::string osmosdr_source_c_impl::get_antenna( size_t chan ) return ""; } + +void osmosdr_source_c_impl::set_iq_balance_mode( int mode, size_t chan ) +{ +#ifdef HAVE_IQBALANCE + size_t channel = 0; + BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) { + for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { + if ( chan == channel++ ) { + if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { + iqbalance_optimize_c *opt = _iq_opt[chan]; + iqbalance_fix_cc *fix = _iq_fix[chan]; + + if ( IQBalanceOff == mode ) { + opt->set_period( 0 ); + /* store current values in order to be able to restore them later */ + _vals[ chan ] = std::pair< float, float >( fix->mag(), fix->phase() ); + fix->set_mag( 0.0f ); + fix->set_phase( 0.0f ); + } else if ( IQBalanceManual == mode ) { + if ( opt->period() == 0 ) { /* transition from Off to Manual */ + /* restore previous values */ + std::pair< float, float > val = _vals[ chan ]; + fix->set_mag( val.first ); + fix->set_phase( val.second ); + } + opt->set_period( 0 ); + } else if ( IQBalanceAutomatic == mode ) { + opt->set_period( dev->get_sample_rate() / 5 ); + opt->reset(); + } + } + } + } + } +#endif +} + +void osmosdr_source_c_impl::set_iq_balance( const std::complex &correction, size_t chan ) +{ +#ifdef HAVE_IQBALANCE + size_t channel = 0; + BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) { + for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++) { + if ( chan == channel++ ) { + if ( chan < _iq_opt.size() && chan < _iq_fix.size() ) { + iqbalance_optimize_c *opt = _iq_opt[chan]; + iqbalance_fix_cc *fix = _iq_fix[chan]; + + if ( opt->period() == 0 ) { /* automatic optimization desabled */ + fix->set_mag( correction.real() ); + fix->set_phase( correction.imag() ); + } + } + } + } + } +#endif +} diff --git a/lib/osmosdr_source_c_impl.h b/lib/osmosdr_source_c_impl.h index a34bf3b..47d6695 100644 --- a/lib/osmosdr_source_c_impl.h +++ b/lib/osmosdr_source_c_impl.h @@ -22,6 +22,11 @@ #include +#ifdef HAVE_IQBALANCE +#include +#include +#endif + #include #include @@ -57,6 +62,9 @@ public: std::string set_antenna( const std::string & antenna, size_t chan = 0 ); std::string get_antenna( size_t chan = 0 ); + void set_iq_balance_mode( int mode, size_t chan = 0 ); + void set_iq_balance( const std::complex &correction, size_t chan = 0 ); + private: osmosdr_source_c_impl (const std::string & args); // private constructor @@ -72,6 +80,11 @@ private: std::map< size_t, double > _if_gain; std::map< size_t, std::string > _antenna; std::vector< osmosdr_src_iface * > _devs; +#ifdef HAVE_IQBALANCE + std::vector< iqbalance_fix_cc * > _iq_fix; + std::vector< iqbalance_optimize_c * > _iq_opt; + std::map< size_t, std::pair > _vals; +#endif }; #endif /* INCLUDED_OSMOSDR_SOURCE_C_IMPL_H */