diff options
author | Dimitri Stolnikov <horiz0n@gmx.net> | 2013-03-03 18:06:48 +0100 |
---|---|---|
committer | Dimitri Stolnikov <horiz0n@gmx.net> | 2013-03-11 21:06:13 +0100 |
commit | e415d843c7588d3cd018fe43f139c5429a6a6c18 (patch) | |
tree | feecc7b7d61821902922ef404521ec22a4d4c1e2 | |
parent | ede9c80455d64d0286c68ce9faaaa7da69aeefd1 (diff) |
add support for software IQ imbalance correction
this functionality depend on the gr-iqbal blocks developed by Sylvain
Munaut and is a compile time dependency:
http://cgit.osmocom.org/cgit/gr-iqbal
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | cmake/Modules/FindGnuradioIQBalance.cmake | 29 | ||||
-rw-r--r-- | grc/gen_osmosdr_blocks.py | 31 | ||||
-rw-r--r-- | include/osmosdr/osmosdr_source_c.h | 24 | ||||
-rw-r--r-- | lib/CMakeLists.txt | 7 | ||||
-rw-r--r-- | lib/osmosdr_source_c_impl.cc | 131 | ||||
-rw-r--r-- | lib/osmosdr_source_c_impl.h | 13 |
7 files changed, 213 insertions, 23 deletions
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) <callback>set_center_freq(\$freq$(n), $n)</callback> <callback>set_freq_corr(\$corr$(n), $n)</callback> + <callback>set_iq_balance_mode(\$iq_balance_mode$(n), $n)</callback> <callback>set_gain_mode(\$gain_mode$(n), $n)</callback> <callback>set_gain(\$gain$(n), $n)</callback> <callback>set_if_gain(\$if_gain$(n), $n)</callback> @@ -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. @@ -188,6 +198,25 @@ PARAMS_TMPL = """ <hide>\#if \$nchan() > $n then 'none' else 'all'#</hide> </param> <param> + <name>Ch$(n): IQ Balance Mode</name> + <key>iq_balance_mode$(n)</key> + <value>0</value> + <type>int</type> + <hide>\#if \$nchan() > $n then 'none' else 'all'#</hide> + <option> + <name>Off</name> + <key>0</key> + </option> + <option> + <name>Manual</name> + <key>1</key> + </option> + <option> + <name>Automatic</name> + <key>2</key> + </option> + </param> + <param> <name>Ch$(n): Gain Mode</name> <key>gain_mode$(n)</key> <value>0</value> @@ -198,7 +227,7 @@ PARAMS_TMPL = """ <key>0</key> </option> <option> - <name>Auto</name> + <name>Automatic</name> <key>1</key> </option> </param> 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<double> &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 ); + block = src; iface = src.get(); + } +#endif - for (size_t i = 0; i < src->get_num_channels(); i++) - connect(src, i, self(), channel++); + if ( iface != NULL && long(block.get()) != 0 ) { + _devs.push_back( iface ); - _devs.push_back( src.get() ); - } + 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<double> &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 <osmosdr_source_c.h> +#ifdef HAVE_IQBALANCE +#include <iqbalance_optimize_c.h> +#include <iqbalance_fix_cc.h> +#endif + #include <osmosdr_src_iface.h> #include <map> @@ -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<double> &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<float, float> > _vals; +#endif }; #endif /* INCLUDED_OSMOSDR_SOURCE_C_IMPL_H */ |