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
This commit is contained in:
Dimitri Stolnikov 2013-03-03 18:06:48 +01:00
parent ede9c80455
commit e415d843c7
7 changed files with 214 additions and 24 deletions

View File

@ -109,6 +109,7 @@ set(GRC_BLOCKS_DIR ${GR_PKG_DATA_DIR}/grc/blocks)
######################################################################## ########################################################################
find_package(Gruel) find_package(Gruel)
find_package(GnuradioCore) find_package(GnuradioCore)
find_package(GnuradioIQBalance)
find_package(UHD) find_package(UHD)
find_package(GnuradioUHD) find_package(GnuradioUHD)
find_package(GnuradioFCD) find_package(GnuradioFCD)

View File

@ -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)

View File

@ -32,6 +32,7 @@ self.\$(id).set_sample_rate(\$sample_rate)
\#if \$nchan() > $n \#if \$nchan() > $n
self.\$(id).set_center_freq(\$freq$(n), $n) self.\$(id).set_center_freq(\$freq$(n), $n)
self.\$(id).set_freq_corr(\$corr$(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_mode(\$gain_mode$(n), $n)
self.\$(id).set_gain(\$gain$(n), $n) self.\$(id).set_gain(\$gain$(n), $n)
self.\$(id).set_if_gain(\$if_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) #for $n in range($max_nchan)
<callback>set_center_freq(\$freq$(n), $n)</callback> <callback>set_center_freq(\$freq$(n), $n)</callback>
<callback>set_freq_corr(\$corr$(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_mode(\$gain_mode$(n), $n)</callback>
<callback>set_gain(\$gain$(n), $n)</callback> <callback>set_gain(\$gain$(n), $n)</callback>
<callback>set_if_gain(\$if_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.: Freq. Corr.:
The frequency correction factor in parts per million (ppm). Set to 0 if unknown. 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: Gain Mode:
Chooses between the manual (default) and automatic gain mode where appropriate. Chooses between the manual (default) and automatic gain mode where appropriate.
Currently, only rtlsdr devices support automatic gain mode. Currently, only rtlsdr devices support automatic gain mode.
@ -187,6 +197,25 @@ PARAMS_TMPL = """
<type>real</type> <type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide> <hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param> </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> <param>
<name>Ch$(n): Gain Mode</name> <name>Ch$(n): Gain Mode</name>
<key>gain_mode$(n)</key> <key>gain_mode$(n)</key>
@ -198,7 +227,7 @@ PARAMS_TMPL = """
<key>0</key> <key>0</key>
</option> </option>
<option> <option>
<name>Auto</name> <name>Automatic</name>
<key>1</key> <key>1</key>
</option> </option>
</param> </param>

View File

@ -229,6 +229,30 @@ public:
* \return antenna the actual antenna's name * \return antenna the actual antenna's name
*/ */
virtual std::string get_antenna( size_t chan = 0 ) = 0; 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 */ #endif /* INCLUDED_OSMOSDR_SOURCE_C_H */

View File

@ -47,6 +47,13 @@ GR_OSMOSDR_APPEND_LIBS(
${GNURADIO_CORE_LIBRARIES} ${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 # Setup OsmoSDR component
######################################################################## ########################################################################

View File

@ -173,70 +173,81 @@ osmosdr_source_c_impl::osmosdr_source_c_impl (const std::string &args)
// BOOST_FOREACH( dict_t::value_type &entry, dict ) // BOOST_FOREACH( dict_t::value_type &entry, dict )
// std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl; // std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl;
osmosdr_src_iface *iface = NULL;
gr_basic_block_sptr block;
#ifdef ENABLE_OSMOSDR #ifdef ENABLE_OSMOSDR
if ( dict.count("osmosdr") ) { if ( dict.count("osmosdr") ) {
osmosdr_src_c_sptr src = osmosdr_make_src_c( arg ); osmosdr_src_c_sptr src = osmosdr_make_src_c( arg );
block = src; iface = src.get();
for (size_t i = 0; i < src->get_num_channels(); i++)
connect(src, i, self(), channel++);
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_FCD #ifdef ENABLE_FCD
if ( dict.count("fcd") ) { if ( dict.count("fcd") ) {
fcd_source_sptr src = make_fcd_source( arg ); fcd_source_sptr src = make_fcd_source( arg );
connect(src, 0, self(), channel++); block = src; iface = src.get();
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_FILE #ifdef ENABLE_FILE
if ( dict.count("file") ) { if ( dict.count("file") ) {
file_source_c_sptr src = make_file_source_c( arg ); file_source_c_sptr src = make_file_source_c( arg );
connect(src, 0, self(), channel++); block = src; iface = src.get();
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_RTL #ifdef ENABLE_RTL
if ( dict.count("rtl") ) { if ( dict.count("rtl") ) {
rtl_source_c_sptr src = make_rtl_source_c( arg ); rtl_source_c_sptr src = make_rtl_source_c( arg );
connect(src, 0, self(), channel++); block = src; iface = src.get();
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_RTL_TCP #ifdef ENABLE_RTL_TCP
if ( dict.count("rtl_tcp") ) { if ( dict.count("rtl_tcp") ) {
rtl_tcp_source_c_sptr src = make_rtl_tcp_source_c( arg ); rtl_tcp_source_c_sptr src = make_rtl_tcp_source_c( arg );
connect(src, 0, self(), channel++); block = src; iface = src.get();
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_UHD #ifdef ENABLE_UHD
if ( dict.count("uhd") ) { if ( dict.count("uhd") ) {
uhd_source_c_sptr src = make_uhd_source_c( arg ); uhd_source_c_sptr src = make_uhd_source_c( arg );
block = src; iface = src.get();
for (size_t i = 0; i < src->get_num_channels(); i++)
connect(src, i, self(), channel++);
_devs.push_back( src.get() );
} }
#endif #endif
#ifdef ENABLE_MIRI #ifdef ENABLE_MIRI
if ( dict.count("miri") ) { if ( dict.count("miri") ) {
miri_source_c_sptr src = make_miri_source_c( arg ); miri_source_c_sptr src = make_miri_source_c( arg );
block = src; iface = src.get();
for (size_t i = 0; i < src->get_num_channels(); i++)
connect(src, i, self(), channel++);
_devs.push_back( src.get() );
} }
#endif #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()) if (!_devs.size())
@ -305,6 +316,24 @@ double osmosdr_source_c_impl::set_sample_rate(double rate)
BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) BOOST_FOREACH( osmosdr_src_iface *dev, _devs )
sample_rate = dev->set_sample_rate(rate); 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; _sample_rate = sample_rate;
} }
@ -542,3 +571,61 @@ std::string osmosdr_source_c_impl::get_antenna( size_t chan )
return ""; 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
}

View File

@ -22,6 +22,11 @@
#include <osmosdr_source_c.h> #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 <osmosdr_src_iface.h>
#include <map> #include <map>
@ -57,6 +62,9 @@ public:
std::string set_antenna( const std::string & antenna, size_t chan = 0 ); std::string set_antenna( const std::string & antenna, size_t chan = 0 );
std::string get_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: private:
osmosdr_source_c_impl (const std::string & args); // private constructor 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, double > _if_gain;
std::map< size_t, std::string > _antenna; std::map< size_t, std::string > _antenna;
std::vector< osmosdr_src_iface * > _devs; 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 */ #endif /* INCLUDED_OSMOSDR_SOURCE_C_IMPL_H */