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
This commit is contained in:
Dimitri Stolnikov 2013-04-13 23:12:22 +02:00
parent 7b66bb860a
commit 448206b2d2
7 changed files with 344 additions and 239 deletions

View File

@ -21,12 +21,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
MAIN_TMPL = """\
<?xml version="1.0"?>
<block>
<name>$(title) $sourk.title()</name>
<key>$(prefix)_$(sourk)_c</key>
<category>Sources</category>
<throttle>1</throttle>
<import>import osmosdr</import>
<make>osmosdr.$(sourk)_c( args="nchan=" + str(\$nchan) + " " + \$args )
<name>$(title) $sourk.title()</name>
<key>$(prefix)_$(sourk)_c</key>
<category>Sources</category>
<throttle>1</throttle>
<import>import osmosdr</import>
<make>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
</make>
<callback>set_sample_rate(\$sample_rate)</callback>
#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>
<callback>set_antenna(\$ant$(n), $n)</callback>
#end for
<param>
<name>$(dir.title())put Type</name>
<key>type</key>
<type>enum</type>
<option>
<name>Complex float32</name>
<key>fc32</key>
<opt>type:fc32</opt>
</option>
</param>
<param>
<name>Device Arguments</name>
<key>args</key>
<value></value>
<type>string</type>
<hide>
\#if \$args()
none
\#else
part
\#end if
</hide>
</param>
<param>
<name>Num Channels</name>
<key>nchan</key>
<value>1</value>
<type>int</type>
#for $n in range(1, $max_nchan+1)
<option>
<name>$(n)</name>
<key>$n</key>
</option>
#end for
</param>
<param>
<name>Sample Rate (sps)</name>
<key>sample_rate</key>
<value>samp_rate</value>
<type>real</type>
</param>
$params
<check>$max_nchan >= \$nchan</check>
<check>\$nchan > 0</check>
<$sourk>
<name>$dir</name>
<type>\$type.type</type>
<nports>\$nchan</nports>
</$sourk>
<doc>
</make>
<callback>set_sample_rate(\$sample_rate)</callback>
#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>
<callback>set_bb_gain(\$bb_gain$(n), $n)</callback>
<callback>set_antenna(\$ant$(n), $n)</callback>
#end for
<param>
<name>$(dir.title())put Type</name>
<key>type</key>
<type>enum</type>
<option>
<name>Complex float32</name>
<key>fc32</key>
<opt>type:fc32</opt>
</option>
</param>
<param>
<name>Device Arguments</name>
<key>args</key>
<value></value>
<type>string</type>
<hide>
\#if \$args()
none
\#else
part
\#end if
</hide>
</param>
<param>
<name>Num Channels</name>
<key>nchan</key>
<value>1</value>
<type>int</type>
#for $n in range(1, $max_nchan+1)
<option>
<name>$(n)</name>
<key>$n</key>
</option>
#end for
</param>
<param>
<name>Sample Rate (sps)</name>
<key>sample_rate</key>
<value>samp_rate</value>
<type>real</type>
</param>
$params
<check>$max_nchan >= \$nchan</check>
<check>\$nchan > 0</check>
<$sourk>
<name>$dir</name>
<type>\$type.type</type>
<nports>\$nchan</nports>
</$sourk>
<doc>
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
</doc>
</doc>
</block>
"""
PARAMS_TMPL = """
<param>
<name>Ch$(n): Frequency (Hz)</name>
<key>freq$(n)</key>
<value>100e6</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): Freq. Corr. (ppm)</name>
<key>corr$(n)</key>
<value>0</value>
<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>
<value>0</value>
<type>int</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
<option>
<name>Manual</name>
<key>0</key>
</option>
<option>
<name>Automatic</name>
<key>1</key>
</option>
</param>
<param>
<name>Ch$(n): RF Gain (dB)</name>
<key>gain$(n)</key>
<value>10</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): IF Gain (dB)</name>
<key>if_gain$(n)</key>
<value>24</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): Antenna</name>
<key>ant$(n)</key>
<value></value>
<type>string</type>
<hide>
\#if not \$nchan() > $n
all
\#elif \$ant$(n)()
none
\#else
part
\#end if
</hide>
</param>
<param>
<name>Ch$(n): Frequency (Hz)</name>
<key>freq$(n)</key>
<value>100e6</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): Freq. Corr. (ppm)</name>
<key>corr$(n)</key>
<value>0</value>
<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>
<value>0</value>
<type>int</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
<option>
<name>Manual</name>
<key>0</key>
</option>
<option>
<name>Automatic</name>
<key>1</key>
</option>
</param>
<param>
<name>Ch$(n): RF Gain (dB)</name>
<key>gain$(n)</key>
<value>10</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): IF Gain (dB)</name>
<key>if_gain$(n)</key>
<value>20</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): BB Gain (dB)</name>
<key>bb_gain$(n)</key>
<value>20</value>
<type>real</type>
<hide>\#if \$nchan() > $n then 'none' else 'all'#</hide>
</param>
<param>
<name>Ch$(n): Antenna</name>
<key>ant$(n)</key>
<value></value>
<type>string</type>
<hide>
\#if not \$nchan() > $n
all
\#elif \$ant$(n)()
none
\#else
part
\#end if
</hide>
</param>
"""
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,
))

View File

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

View File

@ -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<std::string> 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<double, int> 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__ ) );
}
}
}

View File

@ -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;
};

View File

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

View File

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

View File

@ -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;
};