diff options
-rw-r--r-- | gr-uhd/grc/gen_uhd_usrp_blocks.py | 65 | ||||
-rw-r--r-- | gr-uhd/include/gr_uhd_usrp_sink.h | 45 | ||||
-rw-r--r-- | gr-uhd/include/gr_uhd_usrp_source.h | 43 | ||||
-rw-r--r-- | gr-uhd/lib/gr_uhd_usrp_sink.cc | 77 | ||||
-rw-r--r-- | gr-uhd/lib/gr_uhd_usrp_source.cc | 75 | ||||
-rw-r--r-- | gr-uhd/swig/__init__.py | 14 |
6 files changed, 289 insertions, 30 deletions
diff --git a/gr-uhd/grc/gen_uhd_usrp_blocks.py b/gr-uhd/grc/gen_uhd_usrp_blocks.py index 7116dad3b..19e64d2fd 100644 --- a/gr-uhd/grc/gen_uhd_usrp_blocks.py +++ b/gr-uhd/grc/gen_uhd_usrp_blocks.py @@ -27,8 +27,12 @@ MAIN_TMPL = """\ <import>from gnuradio import uhd</import> <make>uhd.usrp_$(sourk)( device_addr=\$dev_addr, - io_type=uhd.io_type.\$type.type, - num_channels=\$nchan, + stream_args=uhd.stream_args( + cpu_format='\$type.type', + otw_format='\$otw.format', + args='\$otw.args', + channels=range(\$nchan), + ), ) \#if \$clock_rate() self.\$(id).set_clock_rate(\$clock_rate, uhd.ALL_MBOARDS) @@ -84,17 +88,66 @@ self.\$(id).set_bandwidth(\$bw$(n), $n) <key>type</key> <type>enum</type> <option> - <name>Complex</name> + <name>complex float32</name> <key>complex</key> - <opt>type:COMPLEX_FLOAT32</opt> + <opt>type:fc32</opt> <opt>vlen:1</opt> </option> <option> - <name>Short</name> + <name>complex uint16</name> <key>short</key> - <opt>type:COMPLEX_INT16</opt> + <opt>type:sc16</opt> <opt>vlen:2</opt> </option> + <option> + <name>real float32</name> + <key>float</key> + <opt>type:f32</opt> + <opt>vlen:1</opt> + </option> + <option> + <name>real uint16</name> + <key>short</key> + <opt>type:s16</opt> + <opt>vlen:1</opt> + </option> + </param> + <param> + <name>Wire Format</name> + <key>otw_format</key> + <type>enum</type> + <option> + <name>complex uint16</name> + <key>sc16</key> + <opt>format:sc16</opt> + <opt>args:</opt> + </option> + <option> + <name>complex uint8</name> + <key>sc8</key> + <opt>format:sc8</opt> + <opt>args:</opt> + </option> + <option> + <name>real uint16</name> + <opt>format:s16</opt> + <opt>args:</opt> + </option> + <option> + <name>real uint8</name> + <opt>format:s8</opt> + <opt>args:</opt> + </option> + <option> + <name>magnitude uint16</name> + <opt>format:s16</opt> + <opt>args:magnitude</opt> + </option> + <option> + <name>magnitude uint8</name> + <opt>format:s8</opt> + <opt>args:magnitude</opt> + </option> </param> <param> <name>Device Addr</name> diff --git a/gr-uhd/include/gr_uhd_usrp_sink.h b/gr-uhd/include/gr_uhd_usrp_sink.h index f11d00063..e3d21226a 100644 --- a/gr-uhd/include/gr_uhd_usrp_sink.h +++ b/gr-uhd/include/gr_uhd_usrp_sink.h @@ -26,6 +26,20 @@ #include <gr_sync_block.h> #include <uhd/usrp/multi_usrp.hpp> +#ifndef INCLUDED_UHD_STREAM_HPP +namespace uhd{ + struct GR_UHD_API stream_args_t{ + std::string cpu_format; + std::string otw_format; + std::string args; + std::vector<size_t> channels; + }; +} +# define INCLUDED_UHD_STREAM_HPP +#else +# define GR_UHD_USE_STREAM_API +#endif + class uhd_usrp_sink; /*! @@ -62,6 +76,37 @@ GR_UHD_API boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink( size_t num_channels ); +/*! + * \brief Make a new USRP sink block. + * + * The USRP sink block reads a stream and transmits the samples. + * The sink block also provides API calls for transmitter settings. + * + * TX Stream tagging: + * + * The following tag keys will be consumed by the work function: + * - pmt::pmt_string_to_symbol("tx_sob") + * - pmt::pmt_string_to_symbol("tx_eob") + * - pmt::pmt_string_to_symbol("tx_time") + * + * The sob and eob (start and end of burst) tag values are pmt booleans. + * When present, burst tags should be set to true (pmt::PMT_T). + * + * The timstamp tag value is a pmt tuple of the following: + * (uint64 seconds, and double fractional seconds). + * + * See the UHD manual for more detailed documentation: + * http://code.ettus.com/redmine/ettus/projects/uhd/wiki + * + * \param device_addr the address to identify the hardware + * \param stream_args the IO format and channel specification + * \return a new USRP sink block object + */ +GR_UHD_API boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink( + const uhd::device_addr_t &device_addr, + const uhd::stream_args_t &stream_args +); + class GR_UHD_API uhd_usrp_sink : virtual public gr_sync_block{ public: diff --git a/gr-uhd/include/gr_uhd_usrp_source.h b/gr-uhd/include/gr_uhd_usrp_source.h index fecc6e94d..b194ba4a3 100644 --- a/gr-uhd/include/gr_uhd_usrp_source.h +++ b/gr-uhd/include/gr_uhd_usrp_source.h @@ -26,6 +26,20 @@ #include <gr_sync_block.h> #include <uhd/usrp/multi_usrp.hpp> +#ifndef INCLUDED_UHD_STREAM_HPP +namespace uhd{ + struct GR_UHD_API stream_args_t{ + std::string cpu_format; + std::string otw_format; + std::string args; + std::vector<size_t> channels; + }; +} +# define INCLUDED_UHD_STREAM_HPP +#else +# define GR_UHD_USE_STREAM_API +#endif + class uhd_usrp_source; /*! @@ -55,7 +69,34 @@ class uhd_usrp_source; GR_UHD_API boost::shared_ptr<uhd_usrp_source> uhd_make_usrp_source( const uhd::device_addr_t &device_addr, const uhd::io_type_t &io_type, - size_t num_channels + size_t num_channels = 1 +); + +/*! + * \brief Make a new USRP source block. + * + * The USRP source block receives samples and writes to a stream. + * The source block also provides API calls for receiver settings. + * + * RX Stream tagging: + * + * The following tag keys will be produced by the work function: + * - pmt::pmt_string_to_symbol("rx_time") + * + * The timstamp tag value is a pmt tuple of the following: + * (uint64 seconds, and double fractional seconds). + * A timestamp tag is produced at start() and after overflows. + * + * See the UHD manual for more detailed documentation: + * http://code.ettus.com/redmine/ettus/projects/uhd/wiki + * + * \param device_addr the address to identify the hardware + * \param stream_args the IO format and channel specification + * \return a new USRP source block object + */ +GR_UHD_API boost::shared_ptr<uhd_usrp_source> uhd_make_usrp_source( + const uhd::device_addr_t &device_addr, + const uhd::stream_args_t &stream_args ); class GR_UHD_API uhd_usrp_source : virtual public gr_sync_block{ diff --git a/gr-uhd/lib/gr_uhd_usrp_sink.cc b/gr-uhd/lib/gr_uhd_usrp_sink.cc index 1a6595293..56b16dbc2 100644 --- a/gr-uhd/lib/gr_uhd_usrp_sink.cc +++ b/gr-uhd/lib/gr_uhd_usrp_sink.cc @@ -22,11 +22,25 @@ #include <gr_uhd_usrp_sink.h> #include <gr_io_signature.h> #include <stdexcept> +#include <boost/make_shared.hpp> static const pmt::pmt_t SOB_KEY = pmt::pmt_string_to_symbol("tx_sob"); static const pmt::pmt_t EOB_KEY = pmt::pmt_string_to_symbol("tx_eob"); static const pmt::pmt_t TIME_KEY = pmt::pmt_string_to_symbol("tx_time"); +#include <uhd/convert.hpp> +inline gr_io_signature_sptr args_to_io_sig(const uhd::stream_args_t &args){ + const size_t nchan = std::max<size_t>(args.channels.size(), 1); + #ifdef GR_UHD_USE_STREAM_API + const size_t size = uhd::convert::get_bytes_per_item(args.cpu_format); + #else + size_t size = 0; + if (args.cpu_format == "fc32") size = 8; + if (args.cpu_format == "sc16") size = 4; + #endif + return gr_make_io_signature(nchan, nchan, size); +} + /*********************************************************************** * UHD Multi USRP Sink Impl **********************************************************************/ @@ -34,17 +48,18 @@ class uhd_usrp_sink_impl : public uhd_usrp_sink{ public: uhd_usrp_sink_impl( const uhd::device_addr_t &device_addr, - const uhd::io_type_t &io_type, - size_t num_channels + const uhd::stream_args_t &stream_args ): gr_sync_block( "gr uhd usrp sink", - gr_make_io_signature(num_channels, num_channels, io_type.size), + args_to_io_sig(stream_args), gr_make_io_signature(0, 0, 0) ), - _type(io_type), - _nchan(num_channels) + _stream_args(stream_args), + _nchan(stream_args.channels.size()) { + if (stream_args.cpu_format == "fc32") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_FLOAT32); + if (stream_args.cpu_format == "sc16") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_INT16); _dev = uhd::usrp::multi_usrp::make(device_addr); } @@ -194,11 +209,17 @@ public: get_tags_in_range(_tags, 0, samp0_count, samp0_count + ninput_items); if (not _tags.empty()) this->tag_work(ninput_items); + #ifdef GR_UHD_USE_STREAM_API //send all ninput_items with metadata + const size_t num_sent = _tx_stream->send( + input_items, ninput_items, _metadata, 1.0 + ); + #else const size_t num_sent = _dev->get_device()->send( input_items, ninput_items, _metadata, - _type, uhd::device::SEND_MODE_FULL_BUFF, 1.0 + *_type, uhd::device::SEND_MODE_FULL_BUFF, 1.0 ); + #endif //increment the timespec by the number of samples sent _metadata.time_spec += uhd::time_spec_t(0, num_sent, _sample_rate); @@ -266,15 +287,25 @@ public: //Send an empty start-of-burst packet to begin streaming. //Set at a time in the near future to avoid late packets. bool start(void){ + #ifdef GR_UHD_USE_STREAM_API + _tx_stream = _dev->get_tx_stream(_stream_args); + #endif + _metadata.start_of_burst = true; _metadata.end_of_burst = false; _metadata.has_time_spec = _nchan > 1; _metadata.time_spec = get_time_now() + uhd::time_spec_t(0.01); + #ifdef GR_UHD_USE_STREAM_API + _tx_stream->send( + gr_vector_const_void_star(_nchan), 0, _metadata, 1.0 + ); + #else _dev->get_device()->send( gr_vector_const_void_star(_nchan), 0, _metadata, - _type, uhd::device::SEND_MODE_ONE_PACKET, 1.0 + *_type, uhd::device::SEND_MODE_ONE_PACKET, 1.0 ); + #endif return true; } @@ -285,16 +316,24 @@ public: _metadata.end_of_burst = true; _metadata.has_time_spec = false; + #ifdef GR_UHD_USE_STREAM_API + _tx_stream->send(gr_vector_const_void_star(_nchan), 0, _metadata, 1.0); + #else _dev->get_device()->send( gr_vector_const_void_star(_nchan), 0, _metadata, - _type, uhd::device::SEND_MODE_ONE_PACKET, 1.0 + *_type, uhd::device::SEND_MODE_ONE_PACKET, 1.0 ); + #endif return true; } private: uhd::usrp::multi_usrp::sptr _dev; - const uhd::io_type_t _type; + const uhd::stream_args_t _stream_args; + boost::shared_ptr<uhd::io_type_t> _type; + #ifdef GR_UHD_USE_STREAM_API + uhd::tx_streamer::sptr _tx_stream; + #endif size_t _nchan; uhd::tx_metadata_t _metadata; double _sample_rate; @@ -311,7 +350,25 @@ boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink( const uhd::io_type_t &io_type, size_t num_channels ){ + //fill in the streamer args + uhd::stream_args_t stream_args; + switch(io_type.tid){ + case uhd::io_type_t::COMPLEX_FLOAT32: stream_args.cpu_format = "fc32"; break; + case uhd::io_type_t::COMPLEX_INT16: stream_args.cpu_format = "sc16"; break; + default: throw std::runtime_error("only complex float and shorts known to work"); + } + stream_args.otw_format = "sc16"; //only sc16 known to work + for (size_t chan = 0; chan < num_channels; chan++) + stream_args.channels.push_back(chan); //linear mapping + + return uhd_make_usrp_sink(device_addr, stream_args); +} + +boost::shared_ptr<uhd_usrp_sink> uhd_make_usrp_sink( + const uhd::device_addr_t &device_addr, + const uhd::stream_args_t &stream_args +){ return boost::shared_ptr<uhd_usrp_sink>( - new uhd_usrp_sink_impl(device_addr, io_type, num_channels) + new uhd_usrp_sink_impl(device_addr, stream_args) ); } diff --git a/gr-uhd/lib/gr_uhd_usrp_source.cc b/gr-uhd/lib/gr_uhd_usrp_source.cc index a3369ade0..12dd1bee7 100644 --- a/gr-uhd/lib/gr_uhd_usrp_source.cc +++ b/gr-uhd/lib/gr_uhd_usrp_source.cc @@ -24,9 +24,23 @@ #include <stdexcept> #include <iostream> #include <boost/format.hpp> +#include <boost/make_shared.hpp> static const pmt::pmt_t TIME_KEY = pmt::pmt_string_to_symbol("rx_time"); +#include <uhd/convert.hpp> +inline gr_io_signature_sptr args_to_io_sig(const uhd::stream_args_t &args){ + const size_t nchan = std::max<size_t>(args.channels.size(), 1); + #ifdef GR_UHD_USE_STREAM_API + const size_t size = uhd::convert::get_bytes_per_item(args.cpu_format); + #else + size_t size = 0; + if (args.cpu_format == "fc32") size = 8; + if (args.cpu_format == "sc16") size = 4; + #endif + return gr_make_io_signature(nchan, nchan, size); +} + /*********************************************************************** * UHD Multi USRP Source Impl **********************************************************************/ @@ -34,19 +48,20 @@ class uhd_usrp_source_impl : public uhd_usrp_source{ public: uhd_usrp_source_impl( const uhd::device_addr_t &device_addr, - const uhd::io_type_t &io_type, - size_t num_channels + const uhd::stream_args_t &stream_args ): gr_sync_block( "gr uhd usrp source", gr_make_io_signature(0, 0, 0), - gr_make_io_signature(num_channels, num_channels, io_type.size) + args_to_io_sig(stream_args) ), - _type(io_type), - _nchan(num_channels), + _stream_args(stream_args), + _nchan(stream_args.channels.size()), _stream_now(_nchan == 1), _tag_now(false) { + if (stream_args.cpu_format == "fc32") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_FLOAT32); + if (stream_args.cpu_format == "sc16") _type = boost::make_shared<uhd::io_type_t>(uhd::io_type_t::COMPLEX_INT16); std::stringstream str; str << name() << unique_id(); _id = pmt::pmt_string_to_symbol(str.str()); @@ -187,22 +202,33 @@ public: gr_vector_const_void_star &input_items, gr_vector_void_star &output_items ){ + #ifdef GR_UHD_USE_STREAM_API //In order to allow for low-latency: //We receive all available packets without timeout. //This call can timeout under regular operation... - size_t num_samps = _dev->get_device()->recv( - output_items, noutput_items, _metadata, - _type, uhd::device::RECV_MODE_FULL_BUFF, 0.0 + size_t num_samps = _rx_stream->recv( + output_items, noutput_items, _metadata, 0.0 ); //If receive resulted in a timeout condition: //We now receive a single packet with a large timeout. if (_metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT){ + num_samps = _rx_stream->recv( + output_items, noutput_items, _metadata, 1.0 + ); + } + #else + size_t num_samps = _dev->get_device()->recv( + output_items, noutput_items, _metadata, + *_type, uhd::device::RECV_MODE_FULL_BUFF, 0.0 + ); + if (_metadata.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT){ num_samps = _dev->get_device()->recv( output_items, noutput_items, _metadata, - _type, uhd::device::RECV_MODE_ONE_PACKET, 1.0 + *_type, uhd::device::RECV_MODE_ONE_PACKET, 1.0 ); } + #endif //handle possible errors conditions switch(_metadata.error_code){ @@ -242,6 +268,10 @@ public: } bool start(void){ + #ifdef GR_UHD_USE_STREAM_API + _rx_stream = _dev->get_rx_stream(_stream_args); + _samps_per_packet = _rx_stream->get_max_num_samps(); + #endif //setup a stream command that starts streaming slightly in the future static const double reasonable_delay = 0.1; //order of magnitude over RTT uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); @@ -278,7 +308,12 @@ public: private: uhd::usrp::multi_usrp::sptr _dev; - const uhd::io_type_t _type; + const uhd::stream_args_t _stream_args; + boost::shared_ptr<uhd::io_type_t> _type; + #ifdef GR_UHD_USE_STREAM_API + uhd::rx_streamer::sptr _rx_stream; + size_t _samps_per_packet; + #endif size_t _nchan; bool _stream_now, _tag_now; uhd::rx_metadata_t _metadata; @@ -294,7 +329,25 @@ boost::shared_ptr<uhd_usrp_source> uhd_make_usrp_source( const uhd::io_type_t &io_type, size_t num_channels ){ + //fill in the streamer args + uhd::stream_args_t stream_args; + switch(io_type.tid){ + case uhd::io_type_t::COMPLEX_FLOAT32: stream_args.cpu_format = "fc32"; break; + case uhd::io_type_t::COMPLEX_INT16: stream_args.cpu_format = "sc16"; break; + default: throw std::runtime_error("only complex float and shorts known to work"); + } + stream_args.otw_format = "sc16"; //only sc16 known to work + for (size_t chan = 0; chan < num_channels; chan++) + stream_args.channels.push_back(chan); //linear mapping + + return uhd_make_usrp_source(device_addr, stream_args); +} + +boost::shared_ptr<uhd_usrp_source> uhd_make_usrp_source( + const uhd::device_addr_t &device_addr, + const uhd::stream_args_t &stream_args +){ return boost::shared_ptr<uhd_usrp_source>( - new uhd_usrp_source_impl(device_addr, io_type, num_channels) + new uhd_usrp_source_impl(device_addr, stream_args) ); } diff --git a/gr-uhd/swig/__init__.py b/gr-uhd/swig/__init__.py index 7745b4b79..5c7642a81 100644 --- a/gr-uhd/swig/__init__.py +++ b/gr-uhd/swig/__init__.py @@ -57,6 +57,13 @@ def _prepare_uhd_swig(): def __setitem__(self, key, val): self.set(key, val) setattr(uhd_swig, 'device_addr_t', device_addr_t) + #make the streamer args take **kwargs on init + class streamer_args(uhd_swig.stream_args_t): + def __init__(self, *args, **kwargs): + super(stream_args_t, self).__init__(*args) + for key, val in kwargs.iteritems(): setattr(self, key, val) + setattr(uhd_swig, 'stream_args_t', stream_args_t) + #handle general things on all uhd_swig attributes #Install the __str__ and __repr__ handlers if applicable #Create aliases for uhd swig attributes to avoid the "_t" @@ -87,9 +94,12 @@ def _prepare_uhd_swig(): for index, key, cast in ( (0, 'device_addr', device_addr), (1, 'io_type', io_type), + (1, 'stream_args', stream_args), ): - if len(args) > index: args[index] = cast(args[index]) - if kwargs.has_key(key): kwargs[key] = cast(kwargs[key]) + try: + if len(args) > index: args[index] = cast(args[index]) + if kwargs.has_key(key): kwargs[key] = cast(kwargs[key]) + except: pass return old_constructor(*args, **kwargs) return constructor_interceptor setattr(uhd_swig, attr, constructor_factory(getattr(uhd_swig, attr))) |