forked from sdr/gr-osmosdr
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:
parent
ede9c80455
commit
e415d843c7
|
@ -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)
|
||||||
|
|
|
@ -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)
|
|
@ -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>
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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
|
||||||
########################################################################
|
########################################################################
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
Loading…
Reference in New Issue