forked from sdr/gr-osmosdr
hackrf: add TX support (wip)
features: - gain control for AMP & VGA - frequency error correction - automatic baseband filter - up to 20M sampling rate limitations: - no DC offset correction implemented (yet) - high sampling rates may not work on slow machines the following TX named gain stages are available: RF: MGA-81563, switchable 0 or 14dB IF: MAX2837 VGA, 0 to 47dB in 1dB stepsgr3.6
parent
882b6da6ac
commit
a5bdb27240
|
@ -34,7 +34,7 @@ endmacro(GEN_BLOCK_XML)
|
|||
|
||||
GEN_BLOCK_XML(gen_osmosdr_blocks.py rtlsdr_source_c.xml)
|
||||
GEN_BLOCK_XML(gen_osmosdr_blocks.py osmosdr_source_c.xml)
|
||||
#GEN_BLOCK_XML(gen_osmosdr_blocks.py osmosdr_sink_c.xml)
|
||||
GEN_BLOCK_XML(gen_osmosdr_blocks.py osmosdr_sink_c.xml)
|
||||
|
||||
add_custom_target(osmosdr_grc_xml_blocks ALL DEPENDS ${xml_blocks})
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ MAIN_TMPL = """\
|
|||
<block>
|
||||
<name>$(title) $sourk.title()</name>
|
||||
<key>$(prefix)_$(sourk)_c</key>
|
||||
<category>Sources</category>
|
||||
<category>$($sourk.title())s</category>
|
||||
<throttle>1</throttle>
|
||||
<import>import osmosdr</import>
|
||||
<make>osmosdr.$(sourk)_c( args="nchan=" + str(\$nchan) + " " + \$args )
|
||||
|
@ -104,7 +104,7 @@ self.\$(id).set_bandwidth(\$bw$(n), $n)
|
|||
<nports>\$nchan</nports>
|
||||
</$sourk>
|
||||
<doc>
|
||||
The OsmoSDR $sourk.title() block:
|
||||
The OSMOCOM block:
|
||||
|
||||
While primarily being developed for the OsmoSDR hardware, this block as well supports:
|
||||
|
||||
|
@ -131,6 +131,7 @@ Examples:
|
|||
Optional arguments are placed into [] brackets, remove the brackets before using them! Specific variable values are separated with a |, choose one of them. Variable values containing spaces shall be enclosed in '' as demonstrated in examples section below.
|
||||
Lines ending with ... mean it's possible to bind devices together by specifying multiple device arguments separated with a space.
|
||||
|
||||
Source Mode:
|
||||
fcd=0
|
||||
hackrf=0
|
||||
miri=0[,buffers=32] ...
|
||||
|
@ -143,6 +144,9 @@ Lines ending with ... mean it's possible to bind devices together by specifying
|
|||
osmosdr=0[,mcr=64e6][,nchan=5][,buffers=32][,buflen=N*512] ...
|
||||
file='/path/to/your file',rate=1e6[,freq=100e6][,repeat=true][,throttle=true] ...
|
||||
|
||||
Sink Mode:
|
||||
hackrf=0[,buffers=32]
|
||||
|
||||
Num Channels:
|
||||
Selects the total number of channels in this multi-device configuration. Required when specifying multiple device arguments.
|
||||
|
||||
|
@ -310,10 +314,10 @@ if __name__ == '__main__':
|
|||
head, tail = os.path.split(file)
|
||||
|
||||
if tail.startswith('rtlsdr'):
|
||||
title = 'RTLSDR'
|
||||
title = 'RTL-SDR'
|
||||
prefix = 'rtlsdr'
|
||||
elif tail.startswith('osmosdr'):
|
||||
title = 'OsmoSDR'
|
||||
title = 'OSMOCOM'
|
||||
prefix = 'osmosdr'
|
||||
else: raise Exception, 'file %s has wrong syntax!'%tail
|
||||
|
||||
|
|
|
@ -26,6 +26,6 @@ install(FILES
|
|||
osmosdr_ranges.h
|
||||
osmosdr_device.h
|
||||
osmosdr_source_c.h
|
||||
# osmosdr_sink_c.h
|
||||
osmosdr_sink_c.h
|
||||
DESTINATION include/osmosdr
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#define INCLUDED_OSMOSDR_SINK_C_H
|
||||
|
||||
#include <osmosdr/osmosdr_api.h>
|
||||
#include <osmosdr/osmosdr_ranges.h>
|
||||
#include <gnuradio/gr_hier_block2.h>
|
||||
|
||||
class osmosdr_sink_c;
|
||||
|
@ -56,7 +57,234 @@ OSMOSDR_API osmosdr_sink_c_sptr osmosdr_make_sink_c ( const std::string & args =
|
|||
class OSMOSDR_API osmosdr_sink_c : virtual public gr_hier_block2
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Get the number of channels the underlying radio hardware offers.
|
||||
* \return the number of available channels
|
||||
*/
|
||||
virtual size_t get_num_channels( void ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the possible sample rates for the underlying radio hardware.
|
||||
* \return a range of rates in Sps
|
||||
*/
|
||||
virtual osmosdr::meta_range_t get_sample_rates( void ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the sample rate for the underlying radio hardware.
|
||||
* This also will select the appropriate IF bandpass, if applicable.
|
||||
* \param rate a new rate in Sps
|
||||
*/
|
||||
virtual double set_sample_rate( double rate ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the sample rate for the underlying radio hardware.
|
||||
* This is the actual sample rate and may differ from the rate set.
|
||||
* \return the actual rate in Sps
|
||||
*/
|
||||
virtual double get_sample_rate( void ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the tunable frequency range for the underlying radio hardware.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the frequency range in Hz
|
||||
*/
|
||||
virtual osmosdr::freq_range_t get_freq_range( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Tune the underlying radio hardware to the desired center frequency.
|
||||
* This also will select the appropriate RF bandpass.
|
||||
* \param freq the desired frequency in Hz
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual frequency in Hz
|
||||
*/
|
||||
virtual double set_center_freq( double freq, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the center frequency the underlying radio hardware is tuned to.
|
||||
* This is the actual frequency and may differ from the frequency set.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the frequency in Hz
|
||||
*/
|
||||
virtual double get_center_freq( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the frequency correction value in parts per million.
|
||||
* \param ppm the desired correction value in parts per million
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return correction value in parts per million
|
||||
*/
|
||||
virtual double set_freq_corr( double ppm, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the frequency correction value.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return correction value in parts per million
|
||||
*/
|
||||
virtual double get_freq_corr( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the gain stage names of the underlying radio hardware.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return a vector of strings containing the names of gain stages
|
||||
*/
|
||||
virtual std::vector<std::string> get_gain_names( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the settable overall gain range for the underlying radio hardware.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the gain range in dB
|
||||
*/
|
||||
virtual osmosdr::gain_range_t get_gain_range( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the settable gain range for a specific gain stage.
|
||||
* \param name the name of the gain stage
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the gain range in dB
|
||||
*/
|
||||
virtual osmosdr::gain_range_t get_gain_range( const std::string & name,
|
||||
size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the gain mode for the underlying radio hardware.
|
||||
* This might be supported only for certain hardware types.
|
||||
* \param automatic the gain mode (true means automatic gain mode)
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual gain mode
|
||||
*/
|
||||
virtual bool set_gain_mode( bool automatic, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the gain mode selected for the underlying radio hardware.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual gain mode (true means automatic gain mode)
|
||||
*/
|
||||
virtual bool get_gain_mode( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the gain for the underlying radio hardware.
|
||||
* This function will automatically distribute the desired gain value over
|
||||
* available 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_gain( double gain, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the named gain on the underlying radio hardware.
|
||||
* \param gain the gain in dB
|
||||
* \param name the name of the gain stage
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual gain in dB
|
||||
*/
|
||||
virtual double set_gain( double gain,
|
||||
const std::string & name,
|
||||
size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the actual gain setting of the underlying radio hardware.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual gain in dB
|
||||
*/
|
||||
virtual double get_gain( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the actual gain setting of a named stage.
|
||||
* \param name the name of the gain stage
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual gain in dB
|
||||
*/
|
||||
virtual double get_gain( const std::string & name, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Set the IF gain for the underlying radio hardware.
|
||||
* This function will automatically distribute the desired gain value over
|
||||
* available IF 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_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
|
||||
* \return a vector of strings containing the names of available antennas
|
||||
*/
|
||||
virtual std::vector< std::string > get_antennas( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Select the active antenna of the underlying radio hardware.
|
||||
* \param antenna name of the antenna to be selected
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual antenna's name
|
||||
*/
|
||||
virtual std::string set_antenna( const std::string & antenna,
|
||||
size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the actual underlying radio hardware antenna setting.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return 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;
|
||||
|
||||
/*!
|
||||
* Set the bandpass filter on the radio frontend.
|
||||
* \param bandwidth the filter bandwidth in Hz
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual filter bandwidth in Hz
|
||||
*/
|
||||
virtual double set_bandwidth( double bandwidth, size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the actual bandpass filter setting on the radio frontend.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return the actual filter bandwidth in Hz
|
||||
*/
|
||||
virtual double get_bandwidth( size_t chan = 0 ) = 0;
|
||||
|
||||
/*!
|
||||
* Get the possible bandpass filter settings on the radio frontend.
|
||||
* \param chan the channel index 0 to N-1
|
||||
* \return a range of bandwidths in Hz
|
||||
*/
|
||||
virtual osmosdr::meta_range_t get_bandwidth_range( size_t chan = 0 ) = 0;
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_OSMOSDR_SINK_C_H */
|
||||
|
|
|
@ -28,6 +28,7 @@ include_directories(
|
|||
|
||||
set(hackrf_srcs
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hackrf_source_c.cc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/hackrf_sink_c.cc
|
||||
)
|
||||
|
||||
########################################################################
|
||||
|
|
|
@ -27,15 +27,91 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "hackrf_sink_c.h"
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/assign.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/detail/endian.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <gnuradio/gr_io_signature.h>
|
||||
|
||||
/*
|
||||
* Create a new instance of hackrf_sink_c and return
|
||||
* a boost shared_ptr. This is effectively the public constructor.
|
||||
*/
|
||||
hackrf_sink_c_sptr
|
||||
make_hackrf_sink_c (const std::string &args)
|
||||
#include "hackrf_sink_c.h"
|
||||
|
||||
#include "osmosdr_arg_helpers.h"
|
||||
|
||||
using namespace boost::assign;
|
||||
|
||||
#define BUF_LEN (16 * 32 * 512) /* must be multiple of 512 */
|
||||
#define BUF_NUM 32
|
||||
|
||||
#define BYTES_PER_SAMPLE 2 /* HackRF device consumes 8 bit unsigned IQ data */
|
||||
|
||||
static inline bool cb_init(circular_buffer_t *cb, size_t capacity, size_t sz)
|
||||
{
|
||||
cb->buffer = malloc(capacity * sz);
|
||||
if(cb->buffer == NULL)
|
||||
return false; // handle error
|
||||
cb->buffer_end = (char *)cb->buffer + capacity * sz;
|
||||
cb->capacity = capacity;
|
||||
cb->count = 0;
|
||||
cb->sz = sz;
|
||||
cb->head = cb->buffer;
|
||||
cb->tail = cb->buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void cb_free(circular_buffer_t *cb)
|
||||
{
|
||||
if (cb->buffer) {
|
||||
free(cb->buffer);
|
||||
cb->buffer = NULL;
|
||||
}
|
||||
// clear out other fields too, just to be safe
|
||||
cb->buffer_end = 0;
|
||||
cb->capacity = 0;
|
||||
cb->count = 0;
|
||||
cb->sz = 0;
|
||||
cb->head = 0;
|
||||
cb->tail = 0;
|
||||
}
|
||||
|
||||
static inline bool cb_has_room(circular_buffer_t *cb)
|
||||
{
|
||||
if(cb->count == cb->capacity)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool cb_push_back(circular_buffer_t *cb, const void *item)
|
||||
{
|
||||
if(cb->count == cb->capacity)
|
||||
return false; // handle error
|
||||
memcpy(cb->head, item, cb->sz);
|
||||
cb->head = (char *)cb->head + cb->sz;
|
||||
if(cb->head == cb->buffer_end)
|
||||
cb->head = cb->buffer;
|
||||
cb->count++;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool cb_pop_front(circular_buffer_t *cb, void *item)
|
||||
{
|
||||
if(cb->count == 0)
|
||||
return false; // handle error
|
||||
memcpy(item, cb->tail, cb->sz);
|
||||
cb->tail = (char *)cb->tail + cb->sz;
|
||||
if(cb->tail == cb->buffer_end)
|
||||
cb->tail = cb->buffer;
|
||||
cb->count--;
|
||||
return true;
|
||||
}
|
||||
|
||||
int hackrf_sink_c::_usage = 0;
|
||||
boost::mutex hackrf_sink_c::_usage_mutex;
|
||||
|
||||
hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args)
|
||||
{
|
||||
return gnuradio::get_initial_sptr(new hackrf_sink_c (args));
|
||||
}
|
||||
|
@ -47,7 +123,7 @@ make_hackrf_sink_c (const std::string &args)
|
|||
* output signatures are used by the runtime system to
|
||||
* check that a valid number and type of inputs and outputs
|
||||
* are connected to this block. In this case, we accept
|
||||
* only 1 input and 0 output.
|
||||
* only 0 input and 1 output.
|
||||
*/
|
||||
static const int MIN_IN = 1; // mininum number of input streams
|
||||
static const int MAX_IN = 1; // maximum number of input streams
|
||||
|
@ -60,9 +136,83 @@ static const int MAX_OUT = 0; // maximum number of output streams
|
|||
hackrf_sink_c::hackrf_sink_c (const std::string &args)
|
||||
: gr_sync_block ("hackrf_sink_c",
|
||||
gr_make_io_signature (MIN_IN, MAX_IN, sizeof (gr_complex)),
|
||||
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
|
||||
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex))),
|
||||
_dev(NULL),
|
||||
_buf(NULL),
|
||||
_sample_rate(0),
|
||||
_center_freq(0),
|
||||
_freq_corr(0),
|
||||
_auto_gain(false),
|
||||
_amp_gain(0),
|
||||
_vga_gain(0)
|
||||
{
|
||||
int ret;
|
||||
uint16_t val;
|
||||
|
||||
dict_t dict = params_to_dict(args);
|
||||
|
||||
_buf_num = 0;
|
||||
|
||||
if (dict.count("buffers"))
|
||||
_buf_num = boost::lexical_cast< unsigned int >( dict["buffers"] );
|
||||
|
||||
if (0 == _buf_num)
|
||||
_buf_num = BUF_NUM;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||
|
||||
if ( _usage == 0 )
|
||||
hackrf_init(); /* call only once before the first open */
|
||||
|
||||
_usage++;
|
||||
}
|
||||
|
||||
_dev = NULL;
|
||||
ret = hackrf_open( &_dev );
|
||||
if (ret != HACKRF_SUCCESS)
|
||||
throw std::runtime_error("Failed to open HackRF device.");
|
||||
|
||||
uint8_t board_id;
|
||||
ret = hackrf_board_id_read( _dev, &board_id );
|
||||
if (ret != HACKRF_SUCCESS)
|
||||
throw std::runtime_error("Failed to get board id.");
|
||||
|
||||
char version[40];
|
||||
memset(version, 0, sizeof(version));
|
||||
ret = hackrf_version_string_read( _dev, version, sizeof(version));
|
||||
if (ret != HACKRF_SUCCESS)
|
||||
throw std::runtime_error("Failed to read version string.");
|
||||
#if 0
|
||||
read_partid_serialno_t serial_number;
|
||||
ret = hackrf_board_partid_serialno_read( _dev, &serial_number );
|
||||
if (ret != HACKRF_SUCCESS)
|
||||
throw std::runtime_error("Failed to read serial number.");
|
||||
#endif
|
||||
std::cerr << "Using " << hackrf_board_id_name(hackrf_board_id(board_id)) << " "
|
||||
<< "with firmware " << version << " "
|
||||
<< std::endl;
|
||||
|
||||
if ( BUF_NUM != _buf_num ) {
|
||||
std::cerr << "Using " << _buf_num << " buffers of size " << BUF_LEN << "."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
set_sample_rate( 5000000 );
|
||||
|
||||
set_gain( 0 ); /* disable AMP gain stage */
|
||||
|
||||
hackrf_max2837_read( _dev, 29, &val );
|
||||
val |= 0x3; /* enable TX VGA control over SPI */
|
||||
hackrf_max2837_write( _dev, 29, val );
|
||||
|
||||
set_if_gain( 16 ); /* preset to a reasonable default (non-GRC use case) */
|
||||
|
||||
_buf = (unsigned char *) malloc( BUF_LEN );
|
||||
|
||||
cb_init( &_cbuf, _buf_num, BUF_LEN );
|
||||
|
||||
// _thread = gruel::thread(_hackrf_wait, this);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -70,5 +220,503 @@ hackrf_sink_c::hackrf_sink_c (const std::string &args)
|
|||
*/
|
||||
hackrf_sink_c::~hackrf_sink_c ()
|
||||
{
|
||||
if (_dev) {
|
||||
// _thread.join();
|
||||
hackrf_close( _dev );
|
||||
_dev = NULL;
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock( _usage_mutex );
|
||||
|
||||
_usage--;
|
||||
|
||||
if ( _usage == 0 )
|
||||
hackrf_exit(); /* call only once after last close */
|
||||
}
|
||||
}
|
||||
|
||||
if (_buf) {
|
||||
free(_buf);
|
||||
_buf = NULL;
|
||||
}
|
||||
|
||||
cb_free( &_cbuf );
|
||||
}
|
||||
|
||||
int hackrf_sink_c::_hackrf_tx_callback(hackrf_transfer *transfer)
|
||||
{
|
||||
hackrf_sink_c *obj = (hackrf_sink_c *)transfer->tx_ctx;
|
||||
return obj->hackrf_tx_callback(transfer->buffer, transfer->valid_length);
|
||||
}
|
||||
|
||||
int hackrf_sink_c::hackrf_tx_callback(unsigned char *buffer, uint32_t length)
|
||||
{
|
||||
#if 0
|
||||
for (unsigned int i = 0; i < length; ++i) /* simulate noise */
|
||||
*buffer++ = rand() % 255;
|
||||
#else
|
||||
{
|
||||
boost::mutex::scoped_lock lock( _buf_mutex );
|
||||
|
||||
if ( ! cb_pop_front( &_cbuf, buffer ) ) {
|
||||
memset(buffer, 0, length);
|
||||
std::cerr << "U" << std::flush;
|
||||
} else {
|
||||
// std::cerr << ":" << std::flush;
|
||||
_buf_cond.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
return 0; // TODO: return -1 on error/stop
|
||||
}
|
||||
|
||||
void hackrf_sink_c::_hackrf_wait(hackrf_sink_c *obj)
|
||||
{
|
||||
obj->hackrf_wait();
|
||||
}
|
||||
|
||||
void hackrf_sink_c::hackrf_wait()
|
||||
{
|
||||
}
|
||||
|
||||
bool hackrf_sink_c::start()
|
||||
{
|
||||
if ( ! _dev )
|
||||
return false;
|
||||
|
||||
_buf_used = 0;
|
||||
|
||||
int ret = hackrf_start_tx( _dev, _hackrf_tx_callback, (void *)this );
|
||||
if (ret != HACKRF_SUCCESS) {
|
||||
std::cerr << "Failed to start TX streaming (" << ret << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
while ( ! hackrf_is_streaming( _dev ) );
|
||||
|
||||
return (bool) hackrf_is_streaming( _dev );
|
||||
}
|
||||
|
||||
bool hackrf_sink_c::stop()
|
||||
{
|
||||
if ( ! _dev )
|
||||
return false;
|
||||
|
||||
int ret = hackrf_stop_tx( _dev );
|
||||
if (ret != HACKRF_SUCCESS) {
|
||||
std::cerr << "Failed to stop TX streaming (" << ret << ")" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
while ( hackrf_is_streaming( _dev ) );
|
||||
|
||||
/* FIXME: hackrf_stop_tx should wait until the device is ready for a start */
|
||||
usleep(100000); /* required if we want to immediately start() again */
|
||||
|
||||
return ! (bool) hackrf_is_streaming( _dev );
|
||||
}
|
||||
|
||||
int hackrf_sink_c::work( int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items )
|
||||
{
|
||||
const gr_complex *in = (const gr_complex *) input_items[0];
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock( _buf_mutex );
|
||||
|
||||
while ( ! cb_has_room(&_cbuf) )
|
||||
_buf_cond.wait( lock );
|
||||
}
|
||||
|
||||
unsigned char *buf = _buf + _buf_used;
|
||||
|
||||
int items_consumed = 0;
|
||||
unsigned int prev_buf_used = _buf_used;
|
||||
|
||||
for (int i = 0; i < noutput_items; i++) {
|
||||
if ( _buf_used + BYTES_PER_SAMPLE > BUF_LEN ) {
|
||||
{
|
||||
boost::mutex::scoped_lock lock( _buf_mutex );
|
||||
|
||||
if ( ! cb_push_back( &_cbuf, _buf ) ) {
|
||||
_buf_used = prev_buf_used;
|
||||
items_consumed = 0;
|
||||
std::cerr << "O" << std::flush;
|
||||
break;
|
||||
} else {
|
||||
// std::cerr << "." << std::flush;
|
||||
}
|
||||
}
|
||||
|
||||
_buf_used = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
*buf++ = (in[i].real() + 1.0) * 127;
|
||||
*buf++ = (in[i].imag() + 1.0) * 127;
|
||||
|
||||
_buf_used += BYTES_PER_SAMPLE;
|
||||
items_consumed++;
|
||||
}
|
||||
|
||||
noutput_items = items_consumed;
|
||||
|
||||
// Tell runtime system how many input items we consumed on
|
||||
// each input stream.
|
||||
consume_each(noutput_items);
|
||||
|
||||
// Tell runtime system how many output items we produced.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> hackrf_sink_c::get_devices()
|
||||
{
|
||||
std::vector<std::string> devices;
|
||||
std::string label;
|
||||
|
||||
for (unsigned int i = 0; i < 1 /* TODO: missing libhackrf api */; i++) {
|
||||
std::string args = "hackrf=" + boost::lexical_cast< std::string >( i );
|
||||
|
||||
label.clear();
|
||||
|
||||
label = "HackRF Jawbreaker"; /* TODO: missing libhackrf api */
|
||||
|
||||
boost::algorithm::trim(label);
|
||||
|
||||
args += ",label='" + label + "'";
|
||||
devices.push_back( args );
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
size_t hackrf_sink_c::get_num_channels()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
osmosdr::meta_range_t hackrf_sink_c::get_sample_rates()
|
||||
{
|
||||
osmosdr::meta_range_t range;
|
||||
|
||||
range += osmosdr::range_t( 5e6 ); /* out of spec but appears to work */
|
||||
range += osmosdr::range_t( 10e6 );
|
||||
range += osmosdr::range_t( 12.5e6 );
|
||||
range += osmosdr::range_t( 16e6 );
|
||||
range += osmosdr::range_t( 20e6 ); /* confirmed to work on fast machines */
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_sample_rate(double rate)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (_dev) {
|
||||
ret = hackrf_sample_rate_set( _dev, uint32_t(rate) );
|
||||
if ( HACKRF_SUCCESS == ret ) {
|
||||
_sample_rate = rate;
|
||||
set_bandwidth( rate );
|
||||
} else {
|
||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
||||
}
|
||||
}
|
||||
|
||||
return get_sample_rate();
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_sample_rate()
|
||||
{
|
||||
return _sample_rate;
|
||||
}
|
||||
|
||||
osmosdr::freq_range_t hackrf_sink_c::get_freq_range( size_t chan )
|
||||
{
|
||||
osmosdr::freq_range_t range;
|
||||
|
||||
range += osmosdr::range_t( 30e6, 6e9 );
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_center_freq( double freq, size_t chan )
|
||||
{
|
||||
int ret;
|
||||
|
||||
#define APPLY_PPM_CORR(val, ppm) ((val) * (1.0 + (ppm) * 0.000001))
|
||||
|
||||
if (_dev) {
|
||||
double corr_freq = APPLY_PPM_CORR( freq, _freq_corr );
|
||||
ret = hackrf_set_freq( _dev, uint64_t(corr_freq) );
|
||||
if ( HACKRF_SUCCESS == ret ) {
|
||||
_center_freq = freq;
|
||||
} else {
|
||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
||||
}
|
||||
}
|
||||
|
||||
return get_center_freq( chan );
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_center_freq( size_t chan )
|
||||
{
|
||||
return _center_freq;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_freq_corr( double ppm, size_t chan )
|
||||
{
|
||||
_freq_corr = ppm;
|
||||
|
||||
set_center_freq( _center_freq );
|
||||
|
||||
return get_freq_corr( chan );
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_freq_corr( size_t chan )
|
||||
{
|
||||
return _freq_corr;
|
||||
}
|
||||
|
||||
std::vector<std::string> hackrf_sink_c::get_gain_names( size_t chan )
|
||||
{
|
||||
std::vector< std::string > names;
|
||||
|
||||
names += "RF";
|
||||
names += "IF";
|
||||
|
||||
return names;
|
||||
}
|
||||
|
||||
osmosdr::gain_range_t hackrf_sink_c::get_gain_range( size_t chan )
|
||||
{
|
||||
return get_gain_range( "RF", chan );
|
||||
}
|
||||
|
||||
osmosdr::gain_range_t hackrf_sink_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, 47, 1 );
|
||||
}
|
||||
|
||||
return osmosdr::gain_range_t();
|
||||
}
|
||||
|
||||
bool hackrf_sink_c::set_gain_mode( bool automatic, size_t chan )
|
||||
{
|
||||
_auto_gain = automatic;
|
||||
|
||||
return get_gain_mode(chan);
|
||||
}
|
||||
|
||||
bool hackrf_sink_c::get_gain_mode( size_t chan )
|
||||
{
|
||||
return _auto_gain;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_gain( double gain, size_t 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_sink_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 );
|
||||
}
|
||||
|
||||
return set_gain( gain, chan );
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_gain( size_t chan )
|
||||
{
|
||||
return _amp_gain;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_gain( const std::string & name, size_t chan )
|
||||
{
|
||||
if ( "RF" == name ) {
|
||||
return get_gain( chan );
|
||||
}
|
||||
|
||||
if ( "IF" == name ) {
|
||||
return _vga_gain;
|
||||
}
|
||||
|
||||
return get_gain( chan );
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_if_gain( double gain, size_t chan )
|
||||
{
|
||||
osmosdr::gain_range_t if_gains = get_gain_range( "IF", chan );
|
||||
|
||||
double clip_gain = if_gains.clip( gain, true );
|
||||
double rel_atten = fabs( if_gains.stop() - clip_gain );
|
||||
|
||||
std::vector< osmosdr::gain_range_t > if_attens;
|
||||
|
||||
if_attens += osmosdr::gain_range_t(0, 1, 1); /* chapter 1.5: TX Gain Control */
|
||||
if_attens += osmosdr::gain_range_t(0, 2, 2);
|
||||
if_attens += osmosdr::gain_range_t(0, 4, 4);
|
||||
if_attens += osmosdr::gain_range_t(0, 8, 8);
|
||||
if_attens += osmosdr::gain_range_t(0, 16, 16);
|
||||
if_attens += osmosdr::gain_range_t(0, 16, 16);
|
||||
|
||||
std::map< int, double > attens;
|
||||
|
||||
/* initialize with min attens */
|
||||
for (unsigned int i = 0; i < if_attens.size(); i++) {
|
||||
attens[ i + 1 ] = if_attens[ i ].start();
|
||||
}
|
||||
|
||||
double atten = rel_atten;
|
||||
|
||||
for (int i = if_attens.size() - 1; i >= 0; i--) {
|
||||
osmosdr::gain_range_t range = if_attens[ i ];
|
||||
|
||||
if ( atten - range.stop() >= 0 ) {
|
||||
atten -= range.stop();
|
||||
attens[ i + 1 ] = range.stop();
|
||||
}
|
||||
}
|
||||
#if 0
|
||||
std::cerr << rel_atten << " => "; double sum = 0;
|
||||
for (unsigned int i = 0; i < attens.size(); i++) {
|
||||
sum += attens[ i + 1 ];
|
||||
std::cerr << attens[ i + 1 ] << " ";
|
||||
}
|
||||
std::cerr << " = " << sum << std::endl;
|
||||
#endif
|
||||
if (_dev) {
|
||||
int value = 0;
|
||||
for (unsigned int stage = 1; stage <= attens.size(); stage++) {
|
||||
if ( attens[ stage ] != 0 )
|
||||
value |= 1 << (stage - 1);
|
||||
}
|
||||
#if 0
|
||||
std::cerr << "vga gain: " << gain
|
||||
<< " clip_gain: " << clip_gain
|
||||
<< " rel_atten: " << rel_atten
|
||||
<< " value: " << value
|
||||
<< std::endl;
|
||||
#endif
|
||||
uint16_t val;
|
||||
hackrf_max2837_read( _dev, 29, &val );
|
||||
|
||||
val = (val & 0xf) | ((value & 0x3f) << 4);
|
||||
|
||||
if ( hackrf_max2837_write( _dev, 29, val ) == HACKRF_SUCCESS )
|
||||
_vga_gain = clip_gain;
|
||||
}
|
||||
|
||||
return _vga_gain;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_bb_gain(double gain, size_t chan)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector< std::string > hackrf_sink_c::get_antennas( size_t chan )
|
||||
{
|
||||
std::vector< std::string > antennas;
|
||||
|
||||
antennas += get_antenna( chan );
|
||||
|
||||
return antennas;
|
||||
}
|
||||
|
||||
std::string hackrf_sink_c::set_antenna( const std::string & antenna, size_t chan )
|
||||
{
|
||||
return get_antenna( chan );
|
||||
}
|
||||
|
||||
std::string hackrf_sink_c::get_antenna( size_t chan )
|
||||
{
|
||||
return "ANT";
|
||||
}
|
||||
|
||||
double hackrf_sink_c::set_bandwidth( double bandwidth, size_t chan )
|
||||
{
|
||||
int ret;
|
||||
// osmosdr::meta_range_t bandwidths = get_bandwidth_range( chan );
|
||||
|
||||
if ( bandwidth == 0.0 ) /* bandwidth of 0 means automatic filter selection */
|
||||
bandwidth = _sample_rate;
|
||||
|
||||
if ( _dev ) {
|
||||
/* compute best default value depending on sample rate (auto filter) */
|
||||
uint32_t bw = hackrf_compute_baseband_filter_bw( uint32_t(bandwidth) );
|
||||
ret = hackrf_baseband_filter_bandwidth_set( _dev, bw );
|
||||
if ( HACKRF_SUCCESS == ret ) {
|
||||
_bandwidth = bw;
|
||||
} else {
|
||||
throw std::runtime_error( std::string( __FUNCTION__ ) + " has failed" );
|
||||
}
|
||||
}
|
||||
|
||||
return _bandwidth;
|
||||
}
|
||||
|
||||
double hackrf_sink_c::get_bandwidth( size_t chan )
|
||||
{
|
||||
return _bandwidth;
|
||||
}
|
||||
|
||||
osmosdr::meta_range_t hackrf_sink_c::get_bandwidth_range( size_t chan )
|
||||
{
|
||||
osmosdr::meta_range_t bandwidths;
|
||||
|
||||
// TODO: read out from libhackrf when an API is available
|
||||
|
||||
bandwidths += osmosdr::range_t( 1750000 );
|
||||
bandwidths += osmosdr::range_t( 2500000 );
|
||||
bandwidths += osmosdr::range_t( 3500000 );
|
||||
bandwidths += osmosdr::range_t( 5000000 );
|
||||
bandwidths += osmosdr::range_t( 5500000 );
|
||||
bandwidths += osmosdr::range_t( 6000000 );
|
||||
bandwidths += osmosdr::range_t( 7000000 );
|
||||
bandwidths += osmosdr::range_t( 8000000 );
|
||||
bandwidths += osmosdr::range_t( 9000000 );
|
||||
bandwidths += osmosdr::range_t( 10000000 );
|
||||
bandwidths += osmosdr::range_t( 12000000 );
|
||||
bandwidths += osmosdr::range_t( 14000000 );
|
||||
bandwidths += osmosdr::range_t( 15000000 );
|
||||
bandwidths += osmosdr::range_t( 20000000 );
|
||||
bandwidths += osmosdr::range_t( 24000000 );
|
||||
bandwidths += osmosdr::range_t( 28000000 );
|
||||
|
||||
return bandwidths;
|
||||
}
|
||||
|
|
|
@ -20,10 +20,29 @@
|
|||
#ifndef INCLUDED_HACKRF_SINK_C_H
|
||||
#define INCLUDED_HACKRF_SINK_C_H
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <gruel/thread.h>
|
||||
#include <gnuradio/gr_sync_block.h>
|
||||
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/condition_variable.hpp>
|
||||
|
||||
#include <libhackrf/hackrf.h>
|
||||
|
||||
#include "osmosdr_snk_iface.h"
|
||||
|
||||
class hackrf_sink_c;
|
||||
|
||||
typedef struct circular_buffer
|
||||
{
|
||||
void *buffer; // data buffer
|
||||
void *buffer_end; // end of data buffer
|
||||
size_t capacity; // maximum number of items in the buffer
|
||||
size_t count; // number of items in the buffer
|
||||
size_t sz; // size of each item in the buffer
|
||||
void *head; // pointer to head
|
||||
void *tail; // pointer to tail
|
||||
} circular_buffer_t;
|
||||
|
||||
/*
|
||||
* We use boost::shared_ptr's instead of raw pointers for all access
|
||||
* to gr_blocks (and many other data structures). The shared_ptr gets
|
||||
|
@ -46,7 +65,9 @@ typedef boost::shared_ptr<hackrf_sink_c> hackrf_sink_c_sptr;
|
|||
*/
|
||||
hackrf_sink_c_sptr make_hackrf_sink_c (const std::string & args = "");
|
||||
|
||||
class hackrf_sink_c
|
||||
class hackrf_sink_c :
|
||||
public gr_sync_block,
|
||||
public osmosdr_snk_iface
|
||||
{
|
||||
private:
|
||||
// The friend declaration allows hackrf_make_sink_c to
|
||||
|
@ -56,7 +77,78 @@ private:
|
|||
hackrf_sink_c (const std::string & args); // private constructor
|
||||
|
||||
public:
|
||||
~hackrf_source_c (); // public destructor
|
||||
~hackrf_sink_c (); // public destructor
|
||||
|
||||
bool start();
|
||||
bool stop();
|
||||
|
||||
int work( int noutput_items,
|
||||
gr_vector_const_void_star &input_items,
|
||||
gr_vector_void_star &output_items );
|
||||
|
||||
static std::vector< std::string > get_devices();
|
||||
|
||||
size_t get_num_channels( void );
|
||||
|
||||
osmosdr::meta_range_t get_sample_rates( void );
|
||||
double set_sample_rate( double rate );
|
||||
double get_sample_rate( void );
|
||||
|
||||
osmosdr::freq_range_t get_freq_range( size_t chan = 0 );
|
||||
double set_center_freq( double freq, size_t chan = 0 );
|
||||
double get_center_freq( size_t chan = 0 );
|
||||
double set_freq_corr( double ppm, size_t chan = 0 );
|
||||
double get_freq_corr( size_t chan = 0 );
|
||||
|
||||
std::vector<std::string> get_gain_names( size_t chan = 0 );
|
||||
osmosdr::gain_range_t get_gain_range( size_t chan = 0 );
|
||||
osmosdr::gain_range_t get_gain_range( const std::string & name, size_t chan = 0 );
|
||||
bool set_gain_mode( bool automatic, size_t chan = 0 );
|
||||
bool get_gain_mode( size_t chan = 0 );
|
||||
double set_gain( double gain, size_t chan = 0 );
|
||||
double set_gain( double gain, const std::string & name, size_t chan = 0 );
|
||||
double get_gain( size_t chan = 0 );
|
||||
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 );
|
||||
std::string get_antenna( size_t chan = 0 );
|
||||
|
||||
double set_bandwidth( double bandwidth, size_t chan = 0 );
|
||||
double get_bandwidth( size_t chan = 0 );
|
||||
osmosdr::meta_range_t get_bandwidth_range( size_t chan = 0 );
|
||||
|
||||
private:
|
||||
static int _hackrf_tx_callback(hackrf_transfer* transfer);
|
||||
int hackrf_tx_callback(unsigned char *buffer, uint32_t length);
|
||||
static void _hackrf_wait(hackrf_sink_c *obj);
|
||||
void hackrf_wait();
|
||||
|
||||
static int _usage;
|
||||
static boost::mutex _usage_mutex;
|
||||
|
||||
std::vector<gr_complex> _lut;
|
||||
|
||||
hackrf_device *_dev;
|
||||
// gruel::thread _thread;
|
||||
|
||||
circular_buffer_t _cbuf;
|
||||
unsigned char *_buf;
|
||||
unsigned int _buf_num;
|
||||
unsigned int _buf_used;
|
||||
boost::mutex _buf_mutex;
|
||||
boost::condition_variable _buf_cond;
|
||||
|
||||
double _sample_rate;
|
||||
double _center_freq;
|
||||
double _freq_corr;
|
||||
bool _auto_gain;
|
||||
double _amp_gain;
|
||||
double _vga_gain;
|
||||
double _bandwidth;
|
||||
};
|
||||
|
||||
#endif /* INCLUDED_HACKRF_SINK_C_H */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* -*- c++ -*- */
|
||||
/*
|
||||
* Copyright 2012 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||
* Copyright 2013 Dimitri Stolnikov <horiz0n@gmx.net>
|
||||
*
|
||||
* GNU Radio is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
|
@ -27,8 +27,22 @@
|
|||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <osmosdr_sink_c_impl.h>
|
||||
#include <gr_io_signature.h>
|
||||
#include <gnuradio/gr_io_signature.h>
|
||||
#include <gnuradio/gr_constants.h>
|
||||
#include <gnuradio/gr_throttle.h>
|
||||
#include <gnuradio/gr_null_sink.h>
|
||||
|
||||
#include "osmosdr_sink_c_impl.h"
|
||||
|
||||
#ifdef ENABLE_HACKRF
|
||||
#include "hackrf_sink_c.h"
|
||||
#endif
|
||||
|
||||
#include "osmosdr_arg_helpers.h"
|
||||
|
||||
/* This avoids throws in ctor of gr_hier_block2, as gnuradio is unable to deal
|
||||
with this behavior in a clean way. The GR maintainer Rondeau has been informed. */
|
||||
#define WORKAROUND_GR_HIER_BLOCK2_BUG
|
||||
|
||||
/*
|
||||
* Create a new instance of osmosdr_sink_c_impl and return
|
||||
|
@ -40,27 +54,449 @@ osmosdr_make_sink_c (const std::string &args)
|
|||
return gnuradio::get_initial_sptr(new osmosdr_sink_c_impl (args));
|
||||
}
|
||||
|
||||
/*
|
||||
* Specify constraints on number of input and output streams.
|
||||
* This info is used to construct the input and output signatures
|
||||
* (2nd & 3rd args to gr_block's constructor). The input and
|
||||
* output signatures are used by the runtime system to
|
||||
* check that a valid number and type of inputs and outputs
|
||||
* are connected to this block. In this case, we accept
|
||||
* only 1 input and 0 output.
|
||||
*/
|
||||
static const int MIN_IN = 1; // mininum number of input streams
|
||||
static const int MAX_IN = 1; // maximum number of input streams
|
||||
static const int MIN_OUT = 0; // minimum number of output streams
|
||||
static const int MAX_OUT = 0; // maximum number of output streams
|
||||
|
||||
/*
|
||||
* The private constructor
|
||||
*/
|
||||
osmosdr_sink_c_impl::osmosdr_sink_c_impl (const std::string &args)
|
||||
: gr_hier_block2 ("osmosdr_sink_c_impl",
|
||||
gr_make_io_signature (MIN_IN, MAX_IN, sizeof (gr_complex)),
|
||||
gr_make_io_signature (MIN_OUT, MAX_OUT, sizeof (gr_complex)))
|
||||
args_to_io_signature(args),
|
||||
gr_make_io_signature (0, 0, 0))
|
||||
{
|
||||
size_t channel = 0;
|
||||
bool device_specified = false;
|
||||
|
||||
std::vector< std::string > arg_list = args_to_vector(args);
|
||||
|
||||
std::vector< std::string > dev_types;
|
||||
|
||||
#ifdef ENABLE_HACKRF
|
||||
dev_types.push_back("hackrf");
|
||||
#endif
|
||||
|
||||
std::cerr << "gr-osmosdr "
|
||||
<< GR_OSMOSDR_VERSION " (" GR_OSMOSDR_LIBVER ") "
|
||||
<< "gnuradio " << gr_version() << std::endl;
|
||||
std::cerr << "built-in device types: ";
|
||||
BOOST_FOREACH(std::string dev_type, dev_types)
|
||||
std::cerr << dev_type << " ";
|
||||
std::cerr << std::endl << std::flush;
|
||||
|
||||
BOOST_FOREACH(std::string arg, arg_list) {
|
||||
dict_t dict = params_to_dict(arg);
|
||||
BOOST_FOREACH(std::string dev_type, dev_types) {
|
||||
if ( dict.count( dev_type ) ) {
|
||||
device_specified = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#ifdef WORKAROUND_GR_HIER_BLOCK2_BUG
|
||||
try {
|
||||
#endif
|
||||
std::vector< std::string > dev_list;
|
||||
#ifdef ENABLE_HACKRF
|
||||
BOOST_FOREACH( std::string dev, hackrf_sink_c::get_devices() )
|
||||
dev_list.push_back( dev );
|
||||
#endif
|
||||
// std::cerr << std::endl;
|
||||
// BOOST_FOREACH( std::string dev, dev_list )
|
||||
// std::cerr << "'" << dev << "'" << std::endl;
|
||||
|
||||
if (!device_specified) {
|
||||
if ( dev_list.size() )
|
||||
arg_list.push_back( dev_list.front() );
|
||||
else
|
||||
throw std::runtime_error("No supported devices found to pick from.");
|
||||
}
|
||||
|
||||
BOOST_FOREACH(std::string arg, arg_list) {
|
||||
|
||||
dict_t dict = params_to_dict(arg);
|
||||
|
||||
// std::cerr << std::endl;
|
||||
// BOOST_FOREACH( dict_t::value_type &entry, dict )
|
||||
// std::cerr << "'" << entry.first << "' = '" << entry.second << "'" << std::endl;
|
||||
|
||||
osmosdr_snk_iface *iface = NULL;
|
||||
gr_basic_block_sptr block;
|
||||
|
||||
#ifdef ENABLE_HACKRF
|
||||
if ( dict.count("hackrf") ) {
|
||||
hackrf_sink_c_sptr src = make_hackrf_sink_c( arg );
|
||||
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++) {
|
||||
connect(self(), channel++, block, i);
|
||||
}
|
||||
} else if ( (iface != NULL) || (long(block.get()) != 0) )
|
||||
throw std::runtime_error("Eitner iface or block are NULL.");
|
||||
|
||||
}
|
||||
|
||||
if (!_devs.size())
|
||||
throw std::runtime_error("No devices specified via device arguments.");
|
||||
#ifdef WORKAROUND_GR_HIER_BLOCK2_BUG
|
||||
} catch ( std::exception &ex ) {
|
||||
std::cerr << std::endl << "FATAL: " << ex.what() << std::endl << std::endl;
|
||||
|
||||
size_t missing_chans = output_signature()->max_streams() - channel;
|
||||
|
||||
std::cerr << "Trying to fill up " << missing_chans
|
||||
<< " missing channel(s) with null sinks.\n"
|
||||
<< "This is being done to prevent the application from crashing\n"
|
||||
<< "due to a gnuradio bug. The maintainers have been informed.\n"
|
||||
<< std::endl;
|
||||
|
||||
for (size_t i = 0; i < missing_chans; i++) {
|
||||
/* we try to prevent the whole application from crashing by faking
|
||||
* the missing hardware (channels) with a null sink */
|
||||
gr_null_sink_sptr null_sink = gr_make_null_sink( sizeof(gr_complex) );
|
||||
|
||||
gr_throttle::sptr throttle = gr_make_throttle(sizeof(gr_complex), 1e6);
|
||||
|
||||
connect(self(), channel++, throttle, 0);
|
||||
connect(throttle, 0, null_sink, 0);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t osmosdr_sink_c_impl::get_num_channels()
|
||||
{
|
||||
size_t channels = 0;
|
||||
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
channels += dev->get_num_channels();
|
||||
|
||||
return channels;
|
||||
}
|
||||
|
||||
#define NO_DEVICES_MSG "FATAL: No device(s) available to work with."
|
||||
|
||||
osmosdr::meta_range_t osmosdr_sink_c_impl::get_sample_rates()
|
||||
{
|
||||
osmosdr::meta_range_t rates(0, 0, 0);
|
||||
|
||||
if (!_devs.empty())
|
||||
rates = _devs[0]->get_sample_rates(); // assume same devices used in the group
|
||||
#if 0
|
||||
else
|
||||
throw std::runtime_error(NO_DEVICES_MSG);
|
||||
#endif
|
||||
return rates;
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::set_sample_rate(double rate)
|
||||
{
|
||||
double sample_rate = 0;
|
||||
|
||||
if (_sample_rate != rate) {
|
||||
#if 0
|
||||
if (_devs.empty())
|
||||
throw std::runtime_error(NO_DEVICES_MSG);
|
||||
#endif
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
sample_rate = dev->set_sample_rate(rate);
|
||||
|
||||
_sample_rate = sample_rate;
|
||||
}
|
||||
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::get_sample_rate()
|
||||
{
|
||||
double sample_rate = 0;
|
||||
|
||||
if (!_devs.empty())
|
||||
sample_rate = _devs[0]->get_sample_rate(); // assume same devices used in the group
|
||||
#if 0
|
||||
else
|
||||
throw std::runtime_error(NO_DEVICES_MSG);
|
||||
#endif
|
||||
return sample_rate;
|
||||
}
|
||||
|
||||
osmosdr::freq_range_t osmosdr_sink_c_impl::get_freq_range( size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
return dev->get_freq_range( dev_chan );
|
||||
|
||||
return osmosdr::freq_range_t();
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::set_center_freq( double freq, size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
if ( _center_freq[ chan ] != freq ) {
|
||||
_center_freq[ chan ] = freq;
|
||||
return dev->set_center_freq( freq, dev_chan );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::get_center_freq( size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
return dev->get_center_freq( dev_chan );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::set_freq_corr( double ppm, size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
if ( _freq_corr[ chan ] != ppm ) {
|
||||
_freq_corr[ chan ] = ppm;
|
||||
return dev->set_freq_corr( ppm, dev_chan );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
double osmosdr_sink_c_impl::get_freq_corr( size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
return dev->get_freq_corr( dev_chan );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> osmosdr_sink_c_impl::get_gain_names( size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
return dev->get_gain_names( dev_chan );
|
||||
|
||||
return std::vector< std::string >();
|
||||
}
|
||||
|
||||
osmosdr::gain_range_t osmosdr_sink_c_impl::get_gain_range( size_t chan )
|
||||
{
|
||||
size_t channel = 0;
|
||||
BOOST_FOREACH( osmosdr_snk_iface *dev, _devs )
|
||||
for (size_t dev_chan = 0; dev_chan < dev->get_num_channels(); dev_chan++)
|
||||
if ( chan == channel++ )
|
||||
return dev->get_gain_range( dev_chan );
|
||||
|
||||
return osmosdr::gain_range_t();
|
||||
}
|
||||