diff options
-rw-r--r-- | docs/doxygen/other/stream_tags.dox | 96 | ||||
-rw-r--r-- | gnuradio-runtime/lib/hier_block2_detail.cc | 17 | ||||
-rw-r--r-- | gr-blocks/examples/ctrlport/simple_copy.grc | 50 | ||||
-rw-r--r-- | gr-blocks/grc/blocks_moving_average_xx.xml | 11 | ||||
-rw-r--r-- | gr-blocks/include/gnuradio/blocks/moving_average_XX.h.t | 6 | ||||
-rw-r--r-- | gr-blocks/lib/moving_average_XX_impl.cc.t | 61 | ||||
-rw-r--r-- | gr-blocks/lib/moving_average_XX_impl.h.t | 11 | ||||
-rw-r--r-- | gr-blocks/python/blocks/qa_moving_average.py | 68 | ||||
-rw-r--r-- | gr-digital/swig/digital_swig1.i | 4 | ||||
-rw-r--r-- | gr-uhd/doc/uhd.dox | 1 | ||||
-rw-r--r-- | gr-uhd/include/gnuradio/uhd/usrp_source.h | 27 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_block_impl.cc | 16 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_block_impl.h | 12 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.cc | 12 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_sink_impl.h | 2 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.cc | 26 | ||||
-rw-r--r-- | gr-uhd/lib/usrp_source_impl.h | 6 |
17 files changed, 275 insertions, 151 deletions
diff --git a/docs/doxygen/other/stream_tags.dox b/docs/doxygen/other/stream_tags.dox index 8edc598e96..146218796e 100644 --- a/docs/doxygen/other/stream_tags.dox +++ b/docs/doxygen/other/stream_tags.dox @@ -15,7 +15,7 @@ GNU Radio was originally a streaming system with no other mechanism to pass data between blocks. Streams of data are a model that work well for samples, bits, etc., but can lack for control and meta data. -Part of this is solved using the message passing interface, which +Part of this is solved using the existing message passing interface, which allows blocks to subscribe to messages published by any other block in the flowgraph (see \ref page_msg_passing). The main drawback to the message passing system is that is works asynchronously, meaning that @@ -24,18 +24,18 @@ stream. Stream tags are an isosynchronous data stream that runs parallel to the main data stream. A stream \a tag is generated by a block's work -function and from there on flows downstream with a particular sample +function and from there on flows downstream alongside a particular sample, until it reaches a sink or is forced to stop propagating by another block. Stream tags are defined for a specific item in the data stream and are -formed as a key:value pair. The \a key identifies what the \a value is +formed as a key:value pair. The \a key identifies what the \a value represents while the value holds the data that the tag contains. Both \a key and \a value are PMTs (\ref page_pmt) where the \a key is a PMT symbol while -the \a value any type of PMT and can therefore handle any data we wish -to pass. A fourth part of the tag is the \a srcid, which is a PMT +the \a value is any type of PMT and can therefore handle any data we wish +to pass. An additional part of the tag is the \a srcid, which is a PMT symbol and is used to identify the block that created the tag (which -is usually the block's alias()). +is usually the block's alias). \section stream_tags_block_api_extensions API Extensions to the gr::block @@ -45,10 +45,10 @@ understand \a absolute item numbers. In the data stream model, each block's work function is given a buffer in the data stream that is referenced from 0 to N-1. This is a \a relative offset into the data stream. The absolute reference starts from the beginning of the -flowgraph and continues to count up with ever item. Each input stream +flowgraph and continues to count up with every item. Each input stream is associated with a concept of the 'number of items read' and each -output stream has a 'number of items written.' These are programmed -using the two API calls: +output stream has a 'number of items written'. These are retrieved during +runtime using the two API calls: \code unsigned long int nitems_read(unsigned int which_input); @@ -69,40 +69,25 @@ at <em>nitems_written(0)+i</em> for the 0th output port. \section stream_tags_api Stream Tags API -The stream tags API consists of four functions, two to add and two to -get the stream tags. These functions are only meant to be accessed -within a call to general_work/work. While they can be called elsewhere +The stream tags API is split into two parts: adding tags to a stream, +and getting tags from a stream. +Note that the functions described below are only meant to be accessed +within a call to general_work/work. While they can be called at other points in time by a block, the behavior outside of work is undefined without exact knowledge of the item counts in the buffers. -\li gr::block::add_item_tag: Adds an item tag to a particular output port using a -gr::tag_t data type. -\li gr::block::add_item_tag: Adds an item tag to a particular output -port where each value of the tag is explicitly given. - -\li gr::block::get_tags_in_range: Gets all tags from a particular -input port between a certain range of items (in absolute item time). - -\li gr::block::get_tags_in_range: Gets any tag that has a specified -key from a particular input port between a certain range of items (in -absolute item time). - -\li gr::block::get_tags_in_window: Gets all tags from a particular -input port between a certain range of items (in relative item time -within the work function). - -\li gr::block::get_tags_in_range: Gets any tag that has a specified -key from a particular input port between a certain range of items (in -relative item time within the work function). +\subsection stream_tags_add_item_tag Adding a Tag to a Stream +We add a tag to a particular output stream of the block using: -\subsection stream_tags_add_item_tag Adding a Tag to a Stream +\li gr::block::add_item_tag: Adds an item tag to a particular output port +using a gr::tag_t data type or by specifying the tag values. -The two function calls to add items tags are defined here. We add a -tag to a particular output stream of the block. We can output them to -multiple output streams if we want, but to do so means calling one of -these functions once for each port. +We can output them to multiple output streams if we want, but to do so +means calling this function once for each port. This function can be +provided with a gr::tag_t data type, or each value of the tag can be +explicitly given. Again, a tag is defined as: @@ -136,14 +121,25 @@ of the tag information in the function call: \subsection stream_tags_get_item_tags Getting tags from a Stream -To get tags from a particular input stream, we again have two -functions we can use. Both of these pass back vectors of -gr::tag_t. The second function allows us to specify a particular key -(as a PMT symbol) that filters out all but the key we are interested -in, which reduces the effort inside the work function for getting the -right tag's data. +To get tags from a particular input stream, we have two +functions we can use: + +\li gr::block::get_tags_in_range: Gets all tags from a particular +input port between a certain range of items (in absolute item time). + +\li gr::block::get_tags_in_window: Gets all tags from a particular +input port between a certain range of items (in relative item time +within the work function). -The first call just returns any tags between the given range of items: +The difference between these functions is working in absolute item +time versus relative item time. Both of these pass back vectors of +gr::tag_t, and they both allow +specifying a particular key (as a PMT symbol) to filter against +(or the fifth argument can be left out to search for all keys). +Filtering for a certain key reduces the effort inside the work function +for getting the right tag's data. + +For example, this call just returns any tags between the given range of items: \code void get_tags_in_range(std::vector<tag_t> &v, @@ -167,7 +163,7 @@ key \a key. \section stream_tags_propagation Tag Propagation Tags are propagated downstream from block to block like the normal -data streams. How blocks are actually moved depends on a specific +data streams. How tags are actually moved depends on a specific propagation policy. We defined three types of policies: \li All-to-All: all tags from any input port are replicated to all @@ -175,7 +171,7 @@ output ports \li One-to-One: tags from input port \a i are only copied to output port \a i (depends on num inputs = num outputs). \li Dont: Does not propagate tags. Tags are either stopped here or the -work function propagates them itself. +work function recreates them in some manner. The default behavior of a block is the 'All-to-All' method of propagation. @@ -217,9 +213,9 @@ block. This becomes relevant when using \ref page_tagged_stream_blocks. Tags can be very useful to an application, and their use is spreading. USRP sources generate tag information on the time, sample rate, and frequency of the board if anything changes. We have a meta -data file source/sink that use tags to store information about the -data stream. But there are things to think about when using tags in a -block. +data file source/sink (see \ref page_metadata) that use tags to store +information about the data stream. But there are things to think about +when using tags in a block. First, when tags are not being used, there is almost no effect on the scheduler. However, when we use tags, we add overhead by getting and @@ -233,7 +229,7 @@ necessary and try to provide some control over how tags are generated to control their frequency. A good example is the USRP source, which generates a time tag. If it generated a tag with every sample, we would have thousands of tags per second, which would add a significant -amount of overhead. Conversely, if we started at time <em>t0</em> at +amount of overhead. This is because if we started at time <em>t0</em> at sample rate <em>sr</em>, then after <em>N</em> samples, we know that we are now at time <em>t0 + N/sr</em>. So continuously producing new tags adds no information. @@ -243,7 +239,7 @@ there is a discontinuity in the packets received from the USRP. Since we have no way of knowing in the flowgraph how many samples were potentially lost, we have lost track of the timing information. The USRP driver recognizes when packets have been dropped and uses this to -queue another tag, which allows us to resync. Likewise, any time the +queue another tag, which allows us to resync. Likewise, any point the sample rate or frequency changes, a new tag is issued. */ diff --git a/gnuradio-runtime/lib/hier_block2_detail.cc b/gnuradio-runtime/lib/hier_block2_detail.cc index 49eb34a6d1..82a40fe211 100644 --- a/gnuradio-runtime/lib/hier_block2_detail.cc +++ b/gnuradio-runtime/lib/hier_block2_detail.cc @@ -787,13 +787,6 @@ namespace gr { std::vector<basic_block_sptr>::const_iterator b; // Because flatten_aux is const for(b = d_blocks.begin(); b != d_blocks.end(); b++) { tmp.push_back(*b); - // for every block, attempt to setup RPC - if(ctrlport_on) { - if(!(*b)->is_rpc_set()) { - (*b)->setup_rpc(); - (*b)->rpc_set(); - } - } } // Now add the list of connected input blocks @@ -901,6 +894,16 @@ namespace gr { std::cout << "flatten_aux finished in top_block" << std::endl; sfg->dump(); } + + // if ctrlport is enabled, call setup RPC for all blocks in the flowgraph + if(ctrlport_on) { + for(b = blocks.begin(); b != blocks.end(); b++) { + if(!(*b)->is_rpc_set()) { + (*b)->setup_rpc(); + (*b)->rpc_set(); + } + } + } } void diff --git a/gr-blocks/examples/ctrlport/simple_copy.grc b/gr-blocks/examples/ctrlport/simple_copy.grc index a52c350ded..f61c6cd18a 100644 --- a/gr-blocks/examples/ctrlport/simple_copy.grc +++ b/gr-blocks/examples/ctrlport/simple_copy.grc @@ -1,5 +1,5 @@ <?xml version='1.0' encoding='utf-8'?> -<?grc format='1' created='3.7.11'?> +<?grc format='1' created='3.7.12'?> <flow_graph> <timestamp>Sat Mar 16 22:03:14 2013</timestamp> <block> @@ -393,48 +393,6 @@ to enable/disablethis block</value> </param> </block> <block> - <key>epy_block</key> - <param> - <key>alias</key> - <value></value> - </param> - <param> - <key>_io_cache</key> - <value>('Null Msg Source', 'blk', [], [], [('fake_output', 'message', 1)], '', [])</value> - </param> - <param> - <key>_source_code</key> - <value># Block that doesn't do anything, just used to get a msg input port on another block exposed to ControlPort -from gnuradio import gr -import pmt -class blk(gr.basic_block): - def __init__(self): - gr.basic_block.__init__(self,name='Null Msg Source',in_sig=[],out_sig=[]) - self.message_port_register_out(pmt.intern("fake_output")) -</value> - </param> - <param> - <key>comment</key> - <value></value> - </param> - <param> - <key>_enabled</key> - <value>True</value> - </param> - <param> - <key>_coordinate</key> - <value>(357, 218)</value> - </param> - <param> - <key>_rotation</key> - <value>0</value> - </param> - <param> - <key>id</key> - <value>epy_block_0</value> - </param> - </block> - <block> <key>note</key> <param> <key>alias</key> @@ -861,10 +819,4 @@ python simple_copy_controller.py 127.0.0.1 <PORT> true</value> <source_key>0</source_key> <sink_key>0</sink_key> </connection> - <connection> - <source_block_id>epy_block_0</source_block_id> - <sink_block_id>blocks_copy_0</sink_block_id> - <source_key>fake_output</source_key> - <sink_key>en</sink_key> - </connection> </flow_graph> diff --git a/gr-blocks/grc/blocks_moving_average_xx.xml b/gr-blocks/grc/blocks_moving_average_xx.xml index e90903ad85..00c5e1d486 100644 --- a/gr-blocks/grc/blocks_moving_average_xx.xml +++ b/gr-blocks/grc/blocks_moving_average_xx.xml @@ -8,7 +8,7 @@ <name>Moving Average</name> <key>blocks_moving_average_xx</key> <import>from gnuradio import blocks</import> - <make>blocks.moving_average_$(type.fcn)($length, $scale, $max_iter)</make> + <make>blocks.moving_average_$(type.fcn)($length, $scale, $max_iter, $vlen)</make> <callback>set_length_and_scale($length, $scale)</callback> <param> <name>Type</name> @@ -57,12 +57,21 @@ <value>4000</value> <type>int</type> </param> + <param> + <name>Length of Vectors</name> + <key>vlen</key> + <value>1</value> + <type>int</type> + </param> + <check>$vlen > 0</check> <sink> <name>in</name> <type>$type</type> + <vlen>$vlen</vlen> </sink> <source> <name>out</name> <type>$type</type> + <vlen>$vlen</vlen> </source> </block> diff --git a/gr-blocks/include/gnuradio/blocks/moving_average_XX.h.t b/gr-blocks/include/gnuradio/blocks/moving_average_XX.h.t index da5968240c..deabf31832 100644 --- a/gr-blocks/include/gnuradio/blocks/moving_average_XX.h.t +++ b/gr-blocks/include/gnuradio/blocks/moving_average_XX.h.t @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2013 Free Software Foundation, Inc. + * Copyright 2008,2013,2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -48,9 +48,11 @@ namespace gr { * \param scale scale factor for the result. * \param max_iter limits how long we go without flushing the accumulator * This is necessary to avoid numerical instability for float and complex. + * \param vlen When > 1, do a per-vector-element moving average */ static sptr make(int length, @O_TYPE@ scale, - int max_iter = 4096); + int max_iter = 4096, + unsigned int vlen = 1); /*! * Get the length used in the avaraging calculation. diff --git a/gr-blocks/lib/moving_average_XX_impl.cc.t b/gr-blocks/lib/moving_average_XX_impl.cc.t index 089166971f..fe0a2bdebd 100644 --- a/gr-blocks/lib/moving_average_XX_impl.cc.t +++ b/gr-blocks/lib/moving_average_XX_impl.cc.t @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2010,2013 Free Software Foundation, Inc. + * Copyright 2008,2010,2013,2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -33,24 +33,30 @@ namespace gr { namespace blocks { @NAME@::sptr - @NAME@::make(int length, @O_TYPE@ scale, int max_iter) + @NAME@::make(int length, @O_TYPE@ scale, int max_iter, unsigned int vlen) { return gnuradio::get_initial_sptr - (new @NAME_IMPL@(length, scale, max_iter)); + (new @NAME_IMPL@(length, scale, max_iter, vlen)); } - @NAME_IMPL@::@NAME_IMPL@(int length, @O_TYPE@ scale, int max_iter) + @NAME_IMPL@::@NAME_IMPL@(int length, @O_TYPE@ scale, int max_iter, unsigned int vlen) : sync_block("@NAME@", - io_signature::make(1, 1, sizeof(@I_TYPE@)), - io_signature::make(1, 1, sizeof(@O_TYPE@))), + io_signature::make(1, 1, sizeof(@I_TYPE@)*vlen), + io_signature::make(1, 1, sizeof(@O_TYPE@)*vlen)), d_length(length), d_scale(scale), d_max_iter(max_iter), + d_vlen(vlen), d_new_length(length), d_new_scale(scale), d_updated(false) { set_history(length); + //we don't have C++11's <array>, so initialize the stored vector instead + //we store this vector so that work() doesn't spend its time allocating and freeing vector storage + if(d_vlen > 1) { + d_sum = std::vector<@I_TYPE@>(d_vlen); + } } @NAME_IMPL@::~@NAME_IMPL@() @@ -95,18 +101,39 @@ namespace gr { const @I_TYPE@ *in = (const @I_TYPE@ *)input_items[0]; @O_TYPE@ *out = (@O_TYPE@ *)output_items[0]; - @I_TYPE@ sum = 0; - int num_iter = (noutput_items>d_max_iter) ? d_max_iter : noutput_items; - for(int i = 0; i < d_length-1; i++) { - sum += in[i]; - } - - for(int i = 0; i < num_iter; i++) { - sum += in[i+d_length-1]; - out[i] = sum * d_scale; - sum -= in[i]; + unsigned int num_iter = (unsigned int)((noutput_items>d_max_iter) ? d_max_iter : noutput_items); + if(d_vlen == 1) { + @I_TYPE@ sum = in[0]; + for(int i = 1; i < d_length-1; i++) { + sum += in[i]; + } + + for(unsigned int i = 0; i < num_iter; i++) { + sum += in[i+d_length-1]; + out[i] = sum * d_scale; + sum -= in[i]; + } + + } else { // d_vlen > 1 + //gets automatically optimized well + for(unsigned int elem = 0; elem < d_vlen; elem++) { + d_sum[elem] = in[elem]; + } + + for(int i = 1; i < d_length - 1; i++) { + for(unsigned int elem = 0; elem < d_vlen; elem++) { + d_sum[elem] += in[i*d_vlen + elem]; + } + } + + for(unsigned int i = 0; i < num_iter; i++) { + for(unsigned int elem = 0; elem < d_vlen; elem++) { + d_sum[elem] += in[(i+d_length-1)*d_vlen + elem]; + out[i*d_vlen + elem] = d_sum[elem] * d_scale; + d_sum[elem] -= in[i*d_vlen + elem]; + } + } } - return num_iter; } diff --git a/gr-blocks/lib/moving_average_XX_impl.h.t b/gr-blocks/lib/moving_average_XX_impl.h.t index 17e8ef5da9..f90a666bbf 100644 --- a/gr-blocks/lib/moving_average_XX_impl.h.t +++ b/gr-blocks/lib/moving_average_XX_impl.h.t @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2008,2013 Free Software Foundation, Inc. + * Copyright 2008,2013,2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,6 +26,8 @@ #define @GUARD_NAME_IMPL@ #include <gnuradio/blocks/@NAME@.h> +#include <vector> +#include <algorithm> namespace gr { namespace blocks { @@ -36,6 +38,9 @@ namespace gr { int d_length; @O_TYPE@ d_scale; int d_max_iter; + unsigned int d_vlen; + std::vector<@I_TYPE@> d_sum; + int d_new_length; @O_TYPE@ d_new_scale; @@ -43,11 +48,13 @@ namespace gr { public: @NAME_IMPL@(int length, @O_TYPE@ scale, - int max_iter = 4096); + int max_iter = 4096, + unsigned int vlen = 1); ~@NAME_IMPL@(); int length() const { return d_new_length; } @O_TYPE@ scale() const { return d_new_scale; } + unsigned int vlen() const { return d_vlen; } void set_length_and_scale(int length, @O_TYPE@ scale); void set_length(int length); diff --git a/gr-blocks/python/blocks/qa_moving_average.py b/gr-blocks/python/blocks/qa_moving_average.py index 2c58805925..b76f81392d 100644 --- a/gr-blocks/python/blocks/qa_moving_average.py +++ b/gr-blocks/python/blocks/qa_moving_average.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2013 Free Software Foundation, Inc. +# Copyright 2013,2017 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -45,6 +45,10 @@ class test_moving_average(gr_unittest.TestCase): def tearDown(self): self.tb = None + # These tests will always pass and are therefore useless. 100 random numbers [-1,1) are + # getting summed up and scaled with 0.001. Then, an assertion verifies a result near 0, + # which is the case even if the block is malfunctioning. + def test_01(self): tb = self.tb @@ -87,5 +91,67 @@ class test_moving_average(gr_unittest.TestCase): # make sure result is close to zero self.assertComplexTuplesAlmostEqual(expected_result, dst_data, 1) + # This tests implement own moving average to verify correct behaviour of the block + + def test_03(self): + tb = self.tb + + vlen = 5 + N = 10*vlen + seed = 0 + data = make_random_float_tuple(N, 2**10) + data = [int(d*1000) for d in data] + src = blocks.vector_source_i(data, False) + one_to_many = blocks.stream_to_streams(gr.sizeof_int, vlen) + one_to_vector = blocks.stream_to_vector(gr.sizeof_int, vlen) + many_to_vector = blocks.streams_to_vector(gr.sizeof_int, vlen) + isolated = [ blocks.moving_average_ii(100, 1) for i in range(vlen)] + dut = blocks.moving_average_ii(100, 1, vlen=vlen) + dut_dst = blocks.vector_sink_i(vlen=vlen) + ref_dst = blocks.vector_sink_i(vlen=vlen) + + tb.connect(src, one_to_many) + tb.connect(src, one_to_vector, dut, dut_dst) + tb.connect(many_to_vector, ref_dst) + for idx, single in enumerate(isolated): + tb.connect((one_to_many,idx), single, (many_to_vector,idx)) + + tb.run() + + dut_data = dut_dst.data() + ref_data = ref_dst.data() + + # make sure result is close to zero + self.assertTupleEqual(dut_data, ref_data) + + def test_04(self): + tb = self.tb + + N = 10000 # number of samples + history = 100 # num of samples to average + data = make_random_complex_tuple(N, 1) # generate random data + + # pythonic MA filter + data_padded = (history-1)*[0.0+1j*0.0]+list(data) # history + expected_result = [] + moving_sum = sum(data_padded[:history-1]) + for i in range(N): + moving_sum += data_padded[i+history-1] + expected_result.append(moving_sum) + moving_sum -= data_padded[i] + + src = blocks.vector_source_c(data, False) + op = blocks.moving_average_cc(history, 1) + dst = blocks.vector_sink_c() + + tb.connect(src, op) + tb.connect(op, dst) + tb.run() + + dst_data = dst.data() + + # make sure result is close to zero + self.assertComplexTuplesAlmostEqual(expected_result, dst_data, 4) + if __name__ == '__main__': gr_unittest.run(test_moving_average, "test_moving_average.xml") diff --git a/gr-digital/swig/digital_swig1.i b/gr-digital/swig/digital_swig1.i index 972ddf5cb2..6d19da0328 100644 --- a/gr-digital/swig/digital_swig1.i +++ b/gr-digital/swig/digital_swig1.i @@ -1,5 +1,5 @@ /* - * Copyright 2016 Free Software Foundation, Inc. + * Copyright 2016,2017 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -53,6 +53,7 @@ #include "gnuradio/digital/diff_phasor_cc.h" #include "gnuradio/digital/fll_band_edge_cc.h" #include "gnuradio/digital/framer_sink_1.h" +#include "gnuradio/digital/glfsr.h" #include "gnuradio/digital/glfsr_source_b.h" #include "gnuradio/digital/glfsr_source_f.h" #include "gnuradio/digital/hdlc_deframer_bp.h" @@ -82,6 +83,7 @@ %include "gnuradio/digital/diff_phasor_cc.h" %include "gnuradio/digital/fll_band_edge_cc.h" %include "gnuradio/digital/framer_sink_1.h" +%include "gnuradio/digital/glfsr.h" %include "gnuradio/digital/glfsr_source_b.h" %include "gnuradio/digital/glfsr_source_f.h" %include "gnuradio/digital/hdlc_deframer_bp.h" diff --git a/gr-uhd/doc/uhd.dox b/gr-uhd/doc/uhd.dox index a3de8e3a24..69381610cd 100644 --- a/gr-uhd/doc/uhd.dox +++ b/gr-uhd/doc/uhd.dox @@ -93,6 +93,7 @@ Command name | Value Type | Description `tune` | tune_request | Like freq, but sets a full tune request (i.e. center frequency and DSP offset). Defaults to all channels. `lo_freq` | double | For fully manual tuning: Set the LO frequency (RF frequency). Conflicts with `freq`, `lo_offset`, and `tune`. `dsp_freq` | double | For fully manual tuning: Set the DSP frequency (CORDIC frequency). Conflicts with `freq`, `lo_offset`, and `tune`. +`direction` | string | Used for timed transceiver tuning to ensure tuning order is maintained. Values other than 'TX' or 'RX' will be ignored. `rate` | double | See usrp_block::set_samp_rate(). *Always* affects all channels. `bandwidth` | double | See usrp_block::set_bandwidth(). Defaults to all channels. `time` | timestamp | Sets a command time. See usrp_block::set_command_time(). A value of PMT_NIL will clear the command time. diff --git a/gr-uhd/include/gnuradio/uhd/usrp_source.h b/gr-uhd/include/gnuradio/uhd/usrp_source.h index a0022b3876..027202e764 100644 --- a/gr-uhd/include/gnuradio/uhd/usrp_source.h +++ b/gr-uhd/include/gnuradio/uhd/usrp_source.h @@ -131,6 +131,33 @@ namespace gr { */ virtual void issue_stream_cmd(const ::uhd::stream_cmd_t &cmd) = 0; + /*! Configure the timeout value on the UHD recv() call + * + * This is an advanced use parameter. Changing the timeout value affects + * the latency of this block; a high timeout value can be more optimal + * for high-throughput applications (e.g., 1 second) and setting it to + * zero will have the best latency. Changing the timeout value may also + * be useful for custom FPGA modifications, where traffic might not be + * continuously streaming. + * For specialized high-performance use cases, twiddling these knobs may + * improve performance, but changes are not generally applicable. + * + * Note that UHD's recv() call may block until the timeout is over, which + * means this block might also block for up to the timeout value. + * + * \param timeout Timeout parameter in seconds. Cf. the UHD manual for + * uhd::rx_streamer::recv() for more details. A lower + * value will mean lower latency, but higher CPU load. + * \param one_packet If true, only receive one packet at a time. Cf. the + * UHD manual for uhd::rx_streamer::recv() for more + * details. A value of true will mean lower latency, + * but higher CPU load. + */ + virtual void set_recv_timeout( + const double timeout, + const bool one_packet=true + ) = 0; + /*! * Returns identifying information about this USRP's configuration. * Returns motherboard ID, name, and serial. diff --git a/gr-uhd/lib/usrp_block_impl.cc b/gr-uhd/lib/usrp_block_impl.cc index 7f3478c1a5..803cfd71ec 100644 --- a/gr-uhd/lib/usrp_block_impl.cc +++ b/gr-uhd/lib/usrp_block_impl.cc @@ -39,6 +39,7 @@ const pmt::pmt_t CMD_BANDWIDTH_KEY = pmt::mp("bandwidth"); const pmt::pmt_t CMD_TIME_KEY = pmt::mp("time"); const pmt::pmt_t CMD_MBOARD_KEY = pmt::mp("mboard"); const pmt::pmt_t CMD_ANTENNA_KEY = pmt::mp("antenna"); +const pmt::pmt_t CMD_DIRECTION_KEY = pmt::mp("direction"); /********************************************************************** @@ -209,11 +210,11 @@ bool usrp_block_impl::_check_mboard_sensors_locked() } void -usrp_block_impl::_set_center_freq_from_internals_allchans() +usrp_block_impl::_set_center_freq_from_internals_allchans(pmt::pmt_t direction) { while (_chans_to_tune.any()) { // This resets() bits, so this loop should not run indefinitely - _set_center_freq_from_internals(_chans_to_tune.find_first()); + _set_center_freq_from_internals(_chans_to_tune.find_first(), direction); } } @@ -539,8 +540,14 @@ void usrp_block_impl::msg_handler_command(pmt::pmt_t msg) } } - /// 4) Check if we need to re-tune - _set_center_freq_from_internals_allchans(); + /// 4) See if a direction was specified + pmt::pmt_t direction = pmt::dict_ref( + msg, CMD_DIRECTION_KEY, + pmt::PMT_NIL // Anything except "TX" or "RX will default to the messaged block direction" + ); + + /// 5) Check if we need to re-tune + _set_center_freq_from_internals_allchans(direction); } @@ -699,4 +706,3 @@ void usrp_block_impl::_cmd_handler_dspfreq(const pmt::pmt_t &dspfreq, int chan, _update_curr_tune_req(new_tune_request, chan); } - diff --git a/gr-uhd/lib/usrp_block_impl.h b/gr-uhd/lib/usrp_block_impl.h index a1bbf97826..6b097505e1 100644 --- a/gr-uhd/lib/usrp_block_impl.h +++ b/gr-uhd/lib/usrp_block_impl.h @@ -29,13 +29,6 @@ #include <boost/dynamic_bitset.hpp> #include <boost/bind.hpp> -#define SET_CENTER_FREQ_FROM_INTERNALS(usrp_class, tune_method) \ - ::uhd::tune_result_t \ - usrp_class::_set_center_freq_from_internals(size_t chan) \ - { \ - _chans_to_tune.reset(chan); \ - return _dev->tune_method(_curr_tune_req[chan], _stream_args.channels[chan]); \ - } namespace gr { namespace uhd { @@ -213,10 +206,10 @@ namespace gr { } //! Like set_center_freq(), but uses _curr_freq and _curr_lo_offset - virtual ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan) = 0; + virtual ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan, pmt::pmt_t direction) = 0; //! Calls _set_center_freq_from_internals() on all channels - void _set_center_freq_from_internals_allchans(); + void _set_center_freq_from_internals_allchans(pmt::pmt_t direction); /********************************************************************** * Members @@ -247,4 +240,3 @@ namespace gr { } /* namespace gr */ #endif /* INCLUDED_GR_UHD_BLOCK_IMPL_H */ - diff --git a/gr-uhd/lib/usrp_sink_impl.cc b/gr-uhd/lib/usrp_sink_impl.cc index 2d347e3823..117b6e0a7d 100644 --- a/gr-uhd/lib/usrp_sink_impl.cc +++ b/gr-uhd/lib/usrp_sink_impl.cc @@ -135,7 +135,17 @@ namespace gr { return _dev->set_tx_freq(tune_request, chan); } - SET_CENTER_FREQ_FROM_INTERNALS(usrp_sink_impl, set_tx_freq); + ::uhd::tune_result_t + usrp_sink_impl::_set_center_freq_from_internals(size_t chan, pmt::pmt_t direction) + { + _chans_to_tune.reset(chan); + if (pmt::eqv(direction, pmt::mp("RX"))) { + // TODO: what happens if the RX device is not instantiated? Catch error? + return _dev->set_rx_freq(_curr_tune_req[chan], _stream_args.channels[chan]); + } else { + return _dev->set_tx_freq(_curr_tune_req[chan], _stream_args.channels[chan]); + } + } double usrp_sink_impl::get_center_freq(size_t chan) diff --git a/gr-uhd/lib/usrp_sink_impl.h b/gr-uhd/lib/usrp_sink_impl.h index d509baef90..f46d57cc0c 100644 --- a/gr-uhd/lib/usrp_sink_impl.h +++ b/gr-uhd/lib/usrp_sink_impl.h @@ -107,7 +107,7 @@ namespace gr { private: //! Like set_center_freq(), but uses _curr_freq and _curr_lo_offset - ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan); + ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan, pmt::pmt_t direction); #ifdef GR_UHD_USE_STREAM_API ::uhd::tx_streamer::sptr _tx_stream; diff --git a/gr-uhd/lib/usrp_source_impl.cc b/gr-uhd/lib/usrp_source_impl.cc index 23b94c684f..894a6d849b 100644 --- a/gr-uhd/lib/usrp_source_impl.cc +++ b/gr-uhd/lib/usrp_source_impl.cc @@ -71,6 +71,7 @@ namespace gr { args_to_io_sig(stream_args)), usrp_block_impl(device_addr, stream_args, ""), _recv_timeout(0.1), // seconds + _recv_one_packet(true), _tag_now(false), _issue_stream_cmd_on_start(issue_stream_cmd_on_start) { @@ -149,7 +150,17 @@ namespace gr { return res; } - SET_CENTER_FREQ_FROM_INTERNALS(usrp_source_impl, set_rx_freq); + ::uhd::tune_result_t + usrp_source_impl::_set_center_freq_from_internals(size_t chan, pmt::pmt_t direction) + { + _chans_to_tune.reset(chan); + if (pmt::eqv(direction, pmt::mp("TX"))) { + // TODO: what happens if the TX device is not instantiated? Catch error? + return _dev->set_tx_freq(_curr_tune_req[chan], _stream_args.channels[chan]); + } else { + return _dev->set_rx_freq(_curr_tune_req[chan], _stream_args.channels[chan]); + } + } double usrp_source_impl::get_center_freq(size_t chan) @@ -489,6 +500,15 @@ namespace gr { _tag_now = true; } + void + usrp_source_impl::set_recv_timeout( + const double timeout, + const bool one_packet + ) { + _recv_timeout = timeout; + _recv_one_packet = one_packet; + } + bool usrp_source_impl::start(void) { @@ -534,7 +554,7 @@ namespace gr { _rx_stream->recv(outputs, nbytes/bpi, _metadata, 0.0); else // no rx streamer -- nothing to flush - break; + break; #else _dev->get_device()->recv (outputs, nbytes/_type->size, _metadata, *_type, @@ -625,7 +645,7 @@ namespace gr { noutput_items, _metadata, _recv_timeout, - true /* one packet -> minimize latency */ + _recv_one_packet ); #else size_t num_samps = _dev->get_device()->recv diff --git a/gr-uhd/lib/usrp_source_impl.h b/gr-uhd/lib/usrp_source_impl.h index 7ee5a138d4..d91fcec754 100644 --- a/gr-uhd/lib/usrp_source_impl.h +++ b/gr-uhd/lib/usrp_source_impl.h @@ -107,6 +107,7 @@ namespace gr { double set_lo_freq(double freq, const std::string &name, size_t chan); void issue_stream_cmd(const ::uhd::stream_cmd_t &cmd); + void set_recv_timeout(const double timeout, const bool one_packet); void flush(void); bool start(void); bool stop(void); @@ -120,13 +121,16 @@ namespace gr { private: //! Like set_center_freq(), but uses _curr_freq and _curr_lo_offset - ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan); + ::uhd::tune_result_t _set_center_freq_from_internals(size_t chan, pmt::pmt_t direction); void _cmd_handler_tag(const pmt::pmt_t &tag); #ifdef GR_UHD_USE_STREAM_API ::uhd::rx_streamer::sptr _rx_stream; size_t _samps_per_packet; + //! Timeout value for UHD's recv() call. Lower values mean lower latency. double _recv_timeout; + //! one_packet value for UHD's recv() call. 'true' is lower latency. + bool _recv_one_packet; #endif bool _tag_now; ::uhd::rx_metadata_t _metadata; |