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(GnuradioCore)
find_package(GnuradioIQBalance)
find_package(UHD)
find_package(GnuradioUHD)
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
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.
@ -187,6 +197,25 @@ PARAMS_TMPL = """
<type>real</type>
<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>
@ -198,7 +227,7 @@ PARAMS_TMPL = """
<key>0</key>
</option>
<option>
<name>Auto</name>
<name>Automatic</name>
<key>1</key>
</option>
</param>

View File

@ -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 */

View File

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

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 )
// 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<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>
#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 */