From 448206b2d2a932d583c502a0ba0f55fd9ab1800b Mon Sep 17 00:00:00 2001 From: Dimitri Stolnikov Date: Sat, 13 Apr 2013 23:12:22 +0200 Subject: [PATCH] hackrf: implement full control for RF/IF/BB gain stages the following named gain stages are available: RF: MGA-81563, switchable 0 or 14dB IF: MAX2837 LNA, 0 to 40dB in 8dB steps BB: MAX2837 VGA, 0 to 62dB in 2dB steps --- grc/gen_osmosdr_blocks.py | 372 +++++++++++++++-------------- include/osmosdr/osmosdr_source_c.h | 12 +- lib/hackrf/hackrf_source_c.cc | 164 ++++++++----- lib/hackrf/hackrf_source_c.h | 6 +- lib/osmosdr_source_c_impl.cc | 16 +- lib/osmosdr_source_c_impl.h | 1 + lib/osmosdr_src_iface.h | 12 +- 7 files changed, 344 insertions(+), 239 deletions(-) diff --git a/grc/gen_osmosdr_blocks.py b/grc/gen_osmosdr_blocks.py index 0cd5ffe..76231aa 100644 --- a/grc/gen_osmosdr_blocks.py +++ b/grc/gen_osmosdr_blocks.py @@ -21,12 +21,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA MAIN_TMPL = """\ - $(title) $sourk.title() - $(prefix)_$(sourk)_c - Sources - 1 - import osmosdr - osmosdr.$(sourk)_c( args="nchan=" + str(\$nchan) + " " + \$args ) + $(title) $sourk.title() + $(prefix)_$(sourk)_c + Sources + 1 + import osmosdr + osmosdr.$(sourk)_c( args="nchan=" + str(\$nchan) + " " + \$args ) self.\$(id).set_sample_rate(\$sample_rate) #for $n in range($max_nchan) \#if \$nchan() > $n @@ -36,72 +36,74 @@ 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) +self.\$(id).set_bb_gain(\$bb_gain$(n), $n) \#if \$ant$(n)() self.\$(id).set_antenna(\$ant$(n), $n) \#end if \#end if #end for - - set_sample_rate(\$sample_rate) - #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) - set_antenna(\$ant$(n), $n) - #end for - - $(dir.title())put Type - type - enum - - - - Device Arguments - args - - string - - \#if \$args() - none - \#else - part - \#end if - - - - Num Channels - nchan - 1 - int - #for $n in range(1, $max_nchan+1) - - #end for - - - Sample Rate (sps) - sample_rate - samp_rate - real - - $params - $max_nchan >= \$nchan - \$nchan > 0 - <$sourk> - $dir - \$type.type - \$nchan - - + + set_sample_rate(\$sample_rate) + #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) + set_bb_gain(\$bb_gain$(n), $n) + set_antenna(\$ant$(n), $n) + #end for + + $(dir.title())put Type + type + enum + + + + Device Arguments + args + + string + + \#if \$args() + none + \#else + part + \#end if + + + + Num Channels + nchan + 1 + int + #for $n in range(1, $max_nchan+1) + + #end for + + + Sample Rate (sps) + sample_rate + samp_rate + real + + $params + $max_nchan >= \$nchan + \$nchan > 0 + <$sourk> + $dir + \$type.type + \$nchan + + The OsmoSDR $sourk.title() block: While primarily being developed for the OsmoSDR hardware, this block as well supports: @@ -155,23 +157,28 @@ 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. + Off: Disable correction algorithm (pass through). + Manual: Keep last estimated correction when switched from Automatic to Manual. + Automatic: Periodicallly 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. +To allow manual control of RF/IF/BB gain stages, manual gain mode must be configured. +Currently, only RTL-SDR devices support automatic gain mode. RF Gain: -Overall RF gain of the receiving device. For the new gain value to be applied, the manual gain mode must be enabled first. +Overall RF gain of the receiving device. IF Gain: -Overall IF gain of the receiving device. For the new gain value to be applied, the manual gain mode must be enabled first. +Overall intermediate frequency gain of the receiving device. This setting has only effect for RTL-SDR and OsmoSDR devices with E4000 tuners. Observations lead to a reasonable gain range from 15 to 30dB. +BB Gain: +Overall baseband gain of the receiving device. +This setting has only effect for HackRF Jawbreaker. Observations lead to a reasonable gain range from 15 to 30dB. + Antenna: For devices with only one antenna, this may be left blank. Otherwise, the user should specify one of the possible antenna choices. @@ -180,125 +187,132 @@ See the OsmoSDR project page for more detailed documentation: http://sdr.osmocom.org/trac/ http://sdr.osmocom.org/trac/wiki/rtl-sdr http://sdr.osmocom.org/trac/wiki/GrOsmoSDR - + """ PARAMS_TMPL = """ - - Ch$(n): Frequency (Hz) - freq$(n) - 100e6 - real - \#if \$nchan() > $n then 'none' else 'all'# - - - Ch$(n): Freq. Corr. (ppm) - corr$(n) - 0 - 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) - 0 - int - \#if \$nchan() > $n then 'none' else 'all'# - - - - - Ch$(n): RF Gain (dB) - gain$(n) - 10 - real - \#if \$nchan() > $n then 'none' else 'all'# - - - Ch$(n): IF Gain (dB) - if_gain$(n) - 24 - real - \#if \$nchan() > $n then 'none' else 'all'# - - - Ch$(n): Antenna - ant$(n) - - string - - \#if not \$nchan() > $n - all - \#elif \$ant$(n)() - none - \#else - part - \#end if - - + + Ch$(n): Frequency (Hz) + freq$(n) + 100e6 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): Freq. Corr. (ppm) + corr$(n) + 0 + 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) + 0 + int + \#if \$nchan() > $n then 'none' else 'all'# + + + + + Ch$(n): RF Gain (dB) + gain$(n) + 10 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): IF Gain (dB) + if_gain$(n) + 20 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): BB Gain (dB) + bb_gain$(n) + 20 + real + \#if \$nchan() > $n then 'none' else 'all'# + + + Ch$(n): Antenna + ant$(n) + + string + + \#if not \$nchan() > $n + all + \#elif \$ant$(n)() + none + \#else + part + \#end if + + """ def parse_tmpl(_tmpl, **kwargs): - from Cheetah import Template - return str(Template.Template(_tmpl, kwargs)) + from Cheetah import Template + return str(Template.Template(_tmpl, kwargs)) max_num_channels = 5 import os.path if __name__ == '__main__': - import sys - for file in sys.argv[1:]: - head, tail = os.path.split(file) + import sys + for file in sys.argv[1:]: + head, tail = os.path.split(file) - if tail.startswith('rtlsdr'): - title = 'RTLSDR' - prefix = 'rtlsdr' - elif tail.startswith('osmosdr'): - title = 'OsmoSDR' - prefix = 'osmosdr' - else: raise Exception, 'file %s has wrong syntax!'%tail + if tail.startswith('rtlsdr'): + title = 'RTLSDR' + prefix = 'rtlsdr' + elif tail.startswith('osmosdr'): + title = 'OsmoSDR' + prefix = 'osmosdr' + else: raise Exception, 'file %s has wrong syntax!'%tail - if tail.endswith ('source_c.xml'): - sourk = 'source' - dir = 'out' - elif tail.endswith ('sink_c.xml'): - sourk = 'sink' - dir = 'in' - else: raise Exception, 'is %s a source or sink?'%file + if tail.endswith ('source_c.xml'): + sourk = 'source' + dir = 'out' + elif tail.endswith ('sink_c.xml'): + sourk = 'sink' + dir = 'in' + else: raise Exception, 'is %s a source or sink?'%file - params = ''.join([parse_tmpl(PARAMS_TMPL, n=n) for n in range(max_num_channels)]) - open(file, 'w').write(parse_tmpl(MAIN_TMPL, - max_nchan=max_num_channels, - params=params, - title=title, - prefix=prefix, - sourk=sourk, - dir=dir, - )) + params = ''.join([parse_tmpl(PARAMS_TMPL, n=n) for n in range(max_num_channels)]) + open(file, 'w').write(parse_tmpl(MAIN_TMPL, + max_nchan=max_num_channels, + params=params, + title=title, + prefix=prefix, + sourk=sourk, + dir=dir, + )) diff --git a/include/osmosdr/osmosdr_source_c.h b/include/osmosdr/osmosdr_source_c.h index 453f592..dd4505c 100644 --- a/include/osmosdr/osmosdr_source_c.h +++ b/include/osmosdr/osmosdr_source_c.h @@ -207,6 +207,16 @@ public: */ virtual double set_if_gain( double gain, size_t chan = 0 ) = 0; + /*! + * Set the BB gain for the underlying radio hardware. + * This function will automatically distribute the desired gain value over + * available BB gain stages in an appropriate way and return the actual value. + * \param gain the gain in dB + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_bb_gain( double gain, size_t chan = 0 ) = 0; + /*! * Get the available antennas of the underlying radio hardware. * \param chan the channel index 0 to N-1 @@ -226,7 +236,7 @@ public: /*! * Get the actual underlying radio hardware antenna setting. * \param chan the channel index 0 to N-1 - * \return antenna the actual antenna's name + * \return the actual antenna's name */ virtual std::string get_antenna( size_t chan = 0 ) = 0; diff --git a/lib/hackrf/hackrf_source_c.cc b/lib/hackrf/hackrf_source_c.cc index 0ded9f3..5543ea0 100644 --- a/lib/hackrf/hackrf_source_c.cc +++ b/lib/hackrf/hackrf_source_c.cc @@ -83,7 +83,9 @@ hackrf_source_c::hackrf_source_c (const std::string &args) _center_freq(0), _freq_corr(0), _auto_gain(false), - _if_gain(0), + _amp_gain(0), + _lna_gain(0), + _vga_gain(0), _skipped(0) { int ret; @@ -157,21 +159,17 @@ hackrf_source_c::hackrf_source_c (const std::string &args) << std::endl; } - ret = hackrf_sample_rate_set( _dev, 10000000 ); - if (ret != HACKRF_SUCCESS) - throw std::runtime_error("Failed to set default samplerate."); + set_sample_rate( 5000000 ); - ret = hackrf_set_amp_enable( _dev, 0 ); - if (ret != HACKRF_SUCCESS) - throw std::runtime_error("Failed to disable the RX amplifier."); + set_gain( 0 ); /* disable AMP gain stage */ hackrf_max2837_read( _dev, 8, &val ); val |= 0x3; /* enable LNA & VGA control over SPI */ hackrf_max2837_write( _dev, 8, val ); - set_if_gain( 24 ); /* preset to a reasonable default (non-GRC use case) */ + set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */ - set_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */ + set_bb_gain( 20 ); /* preset to a reasonable default (non-GRC use case) */ _buf = (unsigned short **) malloc(_buf_num * sizeof(unsigned short *)); @@ -365,7 +363,7 @@ double hackrf_source_c::set_sample_rate(double rate) _sample_rate = rate; set_bandwidth( rate ); } else { - throw std::runtime_error( std::string( hackrf_error_name( (hackrf_error) ret ) ) ); + throw std::runtime_error( std::string( __FUNCTION__ ) ); } } @@ -398,7 +396,7 @@ double hackrf_source_c::set_center_freq( double freq, size_t chan ) if ( HACKRF_SUCCESS == ret ) { _center_freq = freq; } else { - throw std::runtime_error( std::string( hackrf_error_name( (hackrf_error) ret ) ) ); + throw std::runtime_error( std::string( __FUNCTION__ ) ); } } @@ -428,28 +426,33 @@ std::vector hackrf_source_c::get_gain_names( size_t chan ) { std::vector< std::string > names; - names += "LNA"; + names += "RF"; names += "IF"; + names += "BB"; return names; } osmosdr::gain_range_t hackrf_source_c::get_gain_range( size_t chan ) { - osmosdr::gain_range_t range; - - range += osmosdr::range_t( 0, 40, 8 ); - - return range; + return get_gain_range( "RF", chan ); } osmosdr::gain_range_t hackrf_source_c::get_gain_range( const std::string & name, size_t chan ) { + if ( "RF" == name ) { + return osmosdr::gain_range_t( 0, 14, 14 ); + } + if ( "IF" == name ) { + return osmosdr::gain_range_t( 0, 40, 8 ); + } + + if ( "BB" == name ) { return osmosdr::gain_range_t( 0, 62, 2 ); } - return get_gain_range( chan ); + return osmosdr::gain_range_t(); } bool hackrf_source_c::set_gain_mode( bool automatic, size_t chan ) @@ -466,7 +469,73 @@ bool hackrf_source_c::get_gain_mode( size_t chan ) double hackrf_source_c::set_gain( double gain, size_t chan ) { - osmosdr::gain_range_t rf_gains = get_gain_range( chan ); + osmosdr::gain_range_t rf_gains = get_gain_range( "RF", chan ); + + if (_dev) { + double clip_gain = rf_gains.clip( gain, true ); + + std::map reg_vals; + reg_vals[ 0 ] = 0; + reg_vals[ 14 ] = 1; + + if ( reg_vals.count( clip_gain ) ) { + int value = reg_vals[ clip_gain ]; +#if 0 + std::cerr << "amp gain: " << gain + << " clip_gain: " << clip_gain + << " value: " << value + << std::endl; +#endif + if ( hackrf_set_amp_enable( _dev, value ) == HACKRF_SUCCESS ) + _amp_gain = clip_gain; + } + } + + return _amp_gain; +} + +double hackrf_source_c::set_gain( double gain, const std::string & name, size_t chan) +{ + if ( "RF" == name ) { + return set_gain( gain, chan ); + } + + if ( "IF" == name ) { + return set_if_gain( gain, chan ); + } + + if ( "BB" == name ) { + return set_bb_gain( gain, chan ); + } + + return set_gain( gain, chan ); +} + +double hackrf_source_c::get_gain( size_t chan ) +{ + return _amp_gain; +} + +double hackrf_source_c::get_gain( const std::string & name, size_t chan ) +{ + if ( "RF" == name ) { + return get_gain( chan ); + } + + if ( "IF" == name ) { + return _lna_gain; + } + + if ( "BB" == name ) { + return _vga_gain; + } + + return get_gain( chan ); +} + +double hackrf_source_c::set_if_gain(double gain, size_t chan) +{ + osmosdr::gain_range_t rf_gains = get_gain_range( "IF", chan ); if (_dev) { double clip_gain = rf_gains.clip( gain, true ); @@ -482,48 +551,29 @@ double hackrf_source_c::set_gain( double gain, size_t chan ) if ( reg_vals.count( rel_gain ) ) { int value = reg_vals[ rel_gain ]; - -// std::cerr << "lna gain: " << rel_gain << " value: " << value << std::endl; - +#if 0 + std::cerr << "lna gain: " << gain + << " clip_gain: " << clip_gain + << " rel_gain: " << rel_gain + << " value: " << value + << std::endl; +#endif uint16_t val; hackrf_max2837_read( _dev, 1, &val ); val = (val & ~(7 << 2)) | ((value & 7) << 2); if ( hackrf_max2837_write( _dev, 1, val ) == HACKRF_SUCCESS ) - _gain = clip_gain; + _lna_gain = clip_gain; } } - return get_gain( chan ); + return _lna_gain; } -double hackrf_source_c::set_gain( double gain, const std::string & name, size_t chan) +double hackrf_source_c::set_bb_gain( double gain, size_t chan ) { - if ( "IF" == name ) { - return set_if_gain( gain, chan ); - } - - return set_gain( gain, chan ); -} - -double hackrf_source_c::get_gain( size_t chan ) -{ - return _gain; -} - -double hackrf_source_c::get_gain( const std::string & name, size_t chan ) -{ - if ( "IF" == name ) { - return _if_gain; - } - - return get_gain( chan ); -} - -double hackrf_source_c::set_if_gain(double gain, size_t chan) -{ - osmosdr::gain_range_t if_gains = get_gain_range( "IF", chan ); + osmosdr::gain_range_t if_gains = get_gain_range( "BB", chan ); if (_dev) { double clip_gain = if_gains.clip( gain, true ); @@ -536,20 +586,24 @@ double hackrf_source_c::set_if_gain(double gain, size_t chan) if ( reg_vals.count( rel_gain ) ) { int value = reg_vals[ rel_gain ]; - -// std::cerr << "vga gain: " << rel_gain << " value: " << value << std::endl; - +#if 0 + std::cerr << "vga gain: " << gain + << " clip_gain: " << clip_gain + << " rel_gain: " << rel_gain + << " value: " << value + << std::endl; +#endif uint16_t val; hackrf_max2837_read( _dev, 5, &val ); val = (val & ~0x1f) | (value & 0x1f); if ( hackrf_max2837_write( _dev, 5, val ) == HACKRF_SUCCESS ) - _if_gain = clip_gain; + _vga_gain = clip_gain; } } - return gain; + return _vga_gain; } std::vector< std::string > hackrf_source_c::get_antennas( size_t chan ) @@ -586,7 +640,7 @@ void hackrf_source_c::set_bandwidth( double bandwidth, size_t chan ) if ( HACKRF_SUCCESS == ret ) { _bandwidth = bw; } else { - throw std::runtime_error( std::string( hackrf_error_name( (hackrf_error) ret ) ) ); + throw std::runtime_error( std::string( __FUNCTION__ ) ); } } } diff --git a/lib/hackrf/hackrf_source_c.h b/lib/hackrf/hackrf_source_c.h index 8bdb0d3..ea14944 100644 --- a/lib/hackrf/hackrf_source_c.h +++ b/lib/hackrf/hackrf_source_c.h @@ -107,6 +107,7 @@ private: double get_gain( const std::string & name, size_t chan = 0 ); double set_if_gain( double gain, size_t chan = 0 ); + double set_bb_gain( double gain, size_t chan = 0 ); std::vector< std::string > get_antennas( size_t chan = 0 ); std::string set_antenna( const std::string & antenna, size_t chan = 0 ); @@ -145,8 +146,9 @@ private: double _center_freq; double _freq_corr; bool _auto_gain; - double _gain; - double _if_gain; + double _amp_gain; + double _lna_gain; + double _vga_gain; unsigned int _skipped; double _bandwidth; }; diff --git a/lib/osmosdr_source_c_impl.cc b/lib/osmosdr_source_c_impl.cc index b5e34e4..fcac3cb 100644 --- a/lib/osmosdr_source_c_impl.cc +++ b/lib/osmosdr_source_c_impl.cc @@ -541,7 +541,7 @@ double osmosdr_source_c_impl::get_gain( const std::string & name, size_t chan ) return 0; } -double osmosdr_source_c_impl::set_if_gain(double gain, size_t chan) +double osmosdr_source_c_impl::set_if_gain( double gain, size_t chan ) { size_t channel = 0; BOOST_FOREACH( osmosdr_src_iface *dev, _devs ) @@ -555,6 +555,20 @@ double osmosdr_source_c_impl::set_if_gain(double gain, size_t chan) return 0; } +double osmosdr_source_c_impl::set_bb_gain( double gain, size_t chan ) +{ + 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 ( _if_gain[ chan ] != gain ) { + _if_gain[ chan ] = gain; + return dev->set_bb_gain( gain, dev_chan ); + } + + return 0; +} + std::vector< std::string > osmosdr_source_c_impl::get_antennas( size_t chan ) { size_t channel = 0; diff --git a/lib/osmosdr_source_c_impl.h b/lib/osmosdr_source_c_impl.h index 292ca90..92c8d38 100644 --- a/lib/osmosdr_source_c_impl.h +++ b/lib/osmosdr_source_c_impl.h @@ -57,6 +57,7 @@ public: double get_gain( const std::string & name, size_t chan = 0 ); double set_if_gain( double gain, size_t chan = 0 ); + double set_bb_gain( double gain, size_t chan = 0 ); std::vector< std::string > get_antennas( size_t chan = 0 ); std::string set_antenna( const std::string & antenna, size_t chan = 0 ); diff --git a/lib/osmosdr_src_iface.h b/lib/osmosdr_src_iface.h index 8169b3a..6c149f9 100644 --- a/lib/osmosdr_src_iface.h +++ b/lib/osmosdr_src_iface.h @@ -185,6 +185,16 @@ public: */ virtual double set_if_gain( double gain, size_t chan = 0 ) { return 0; } + /*! + * Set the BB gain for the underlying radio hardware. + * This function will automatically distribute the desired gain value over + * available BB gain stages in an appropriate way and return the actual value. + * \param gain the gain in dB + * \param chan the channel index 0 to N-1 + * \return the actual gain in dB + */ + virtual double set_bb_gain( double gain, size_t chan = 0 ) { return 0; } + /*! * Get the available antennas of the underlying radio hardware. * \param chan the channel index 0 to N-1 @@ -203,7 +213,7 @@ public: /*! * Get the actual underlying radio hardware antenna setting. * \param chan the channel index 0 to N-1 - * \return antenna the actual antenna's name + * \return the actual antenna's name */ virtual std::string get_antenna( size_t chan = 0 ) = 0; };