diff options
author | Sean Nowlan <sean.nowlan@gtri.gatech.edu> | 2015-04-25 19:38:33 -0400 |
---|---|---|
committer | Sean Nowlan <sean.nowlan@gtri.gatech.edu> | 2015-04-25 19:38:33 -0400 |
commit | 3b1b6d3fd6faa7bd1cbeb8b0ebfe42e6b9196bf7 (patch) | |
tree | 56ccb409c1ec105184a6b5d7cea2ba0cc9de3f82 | |
parent | 06ec3aa4e9bd5152b2bd21f4d4cabd26c4631e0d (diff) |
digital: added tag propagation to burst_shaper blocks
-rwxr-xr-x | gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py | 32 | ||||
-rw-r--r-- | gnuradio-runtime/python/gnuradio/gr/tag_utils.py | 33 | ||||
-rw-r--r-- | gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t | 10 | ||||
-rw-r--r-- | gr-digital/lib/burst_shaper_XX_impl.cc.t | 78 | ||||
-rw-r--r-- | gr-digital/lib/burst_shaper_XX_impl.h.t | 3 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_burst_shaper.py | 71 |
6 files changed, 193 insertions, 34 deletions
diff --git a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py index 1a08ac5ae6..55b62a12ac 100755 --- a/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/qa_tag_utils.py @@ -76,6 +76,38 @@ class test_tag_utils (gr_unittest.TestCase): self.assertTrue(pmt.equal(t_tuple.value, value)) self.assertEqual(t_tuple.offset, offset) + def test_003(self): + offsets = (6, 3, 8) + key = pmt.string_to_symbol('key') + srcid = pmt.string_to_symbol('qa_tag_utils') + tags = [] + + for k in offsets: + t = gr.tag_t() + t.offset = k + t.key = key + t.value = pmt.from_long(k) + t.srcid = srcid + tags.append(t) + + for k, t in zip(sorted(offsets), + sorted(tags, key=gr.tag_t_offset_compare_key())): + self.assertEqual(t.offset, k) + self.assertTrue(pmt.equal(t.key, key)) + self.assertTrue(pmt.equal(t.value, pmt.from_long(k))) + self.assertTrue(pmt.equal(t.srcid, srcid)) + + tmin = min(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmin.offset, min(offsets)) + self.assertTrue(pmt.equal(tmin.key, key)) + self.assertTrue(pmt.equal(tmin.value, pmt.from_long(min(offsets)))) + self.assertTrue(pmt.equal(tmin.srcid, srcid)) + + tmax = max(tags, key=gr.tag_t_offset_compare_key()) + self.assertEqual(tmax.offset, max(offsets)) + self.assertTrue(pmt.equal(tmax.key, key)) + self.assertTrue(pmt.equal(tmax.value, pmt.from_long(max(offsets)))) + self.assertTrue(pmt.equal(tmax.srcid, srcid)) if __name__ == '__main__': diff --git a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py index ba46e3f4d9..872a5b01be 100644 --- a/gnuradio-runtime/python/gnuradio/gr/tag_utils.py +++ b/gnuradio-runtime/python/gnuradio/gr/tag_utils.py @@ -108,3 +108,36 @@ def python_to_tag(tag_struct): return tag else: return None + +def tag_t_offset_compare_key(): + """ + Convert a tag_t_offset_compare function into a key=function + This method is modeled after functools.cmp_to_key(_func_). + It can be used by functions that accept a key function, such as + sorted(), min(), max(), etc. to compare tags by their offsets, + e.g., sorted(tag_list, key=gr.tag_t_offset_compare_key()). + """ + class K(object): + def __init__(self, obj, *args): + self.obj = obj + def __lt__(self, other): + # x.offset < y.offset + return gr.tag_t_offset_compare(self.obj, other.obj) + def __gt__(self, other): + # y.offset < x.offset + return gr.tag_t_offset_compare(other.obj, self.obj) + def __eq__(self, other): + # not (x.offset < y.offset) and not (y.offset < x.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) and \ + not gr.tag_t_offset_compare(other.obj, self.obj) + def __le__(self, other): + # not (y.offset < x.offset) + return not gr.tag_t_offset_compare(other.obj, self.obj) + def __ge__(self, other): + # not (x.offset < y.offset) + return not gr.tag_t_offset_compare(self.obj, other.obj) + def __ne__(self, other): + # (x.offset < y.offset) or (y.offset < x.offset) + return gr.tag_t_offset_compare(self.obj, other.obj) or \ + gr.tag_t_offset_compare(other.obj, self.obj) + return K diff --git a/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t index 43d422ba80..fd7b69060e 100644 --- a/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t +++ b/gr-digital/include/gnuradio/digital/burst_shaper_XX.h.t @@ -49,7 +49,15 @@ namespace gr { * directly to the head and tail of each burst. * * Length tags will be updated to include the length of any added - * zero padding or phasing symbols. + * zero padding or phasing symbols and will be placed at the + * beginning of the modified tagged stream. Any other tags found at + * the same offset as a length tag will also be placed at the + * beginning of the modified tagged stream, since these tags are + * assumed to be associated with the burst rather than a specific + * sample. For example, if "tx_time" tags are used to control + * bursts, their offsets should be consistent with their associated + * burst's length tags. Tags at other offsets will be placed with + * the samples on which they were found. * * \li input: stream of @I_TYPE@ * \li output: stream of @O_TYPE@ diff --git a/gr-digital/lib/burst_shaper_XX_impl.cc.t b/gr-digital/lib/burst_shaper_XX_impl.cc.t index 398011adfb..85add49115 100644 --- a/gr-digital/lib/burst_shaper_XX_impl.cc.t +++ b/gr-digital/lib/burst_shaper_XX_impl.cc.t @@ -68,6 +68,7 @@ namespace gr { d_ncopy(0), d_limit(0), d_index(0), + d_length_tag_offset(0), d_finished(false), d_state(STATE_WAIT) { @@ -93,8 +94,8 @@ namespace gr { void @IMPL_NAME@::forecast(int noutput_items, - gr_vector_int &ninput_items_required) { - //if(d_state == STATE_COPY + gr_vector_int &ninput_items_required) + { ninput_items_required[0] = noutput_items; } @@ -111,13 +112,11 @@ namespace gr { int nread = 0; int nspace = 0; int nskip = 0; - uint64_t curr_tag_index = nitems_read(0); + int curr_tag_index = 0; - std::vector<tag_t> length_tags, tags; + std::vector<tag_t> length_tags; get_tags_in_window(length_tags, 0, 0, ninput_items[0], d_length_tag_key); - get_tags_in_window(tags, 0, 0, ninput_items[0]); std::sort(length_tags.rbegin(), length_tags.rend(), tag_t::offset_compare); - std::sort(tags.begin(), tags.end(), tag_t::offset_compare); while((nwritten < noutput_items) && (nread < ninput_items[0])) { if(d_finished) { @@ -128,11 +127,13 @@ namespace gr { switch(d_state) { case(STATE_WAIT): if(!length_tags.empty()) { - curr_tag_index = length_tags.back().offset; + d_length_tag_offset = length_tags.back().offset; + curr_tag_index = (int)(d_length_tag_offset - nitems_read(0)); d_ncopy = pmt::to_long(length_tags.back().value); length_tags.pop_back(); - nskip = (int)(curr_tag_index - nread - nitems_read(0)); + nskip = curr_tag_index - nread; add_length_tag(nwritten); + propagate_tags(curr_tag_index, nwritten, 1, false); enter_prepad(); } else { @@ -188,19 +189,22 @@ namespace gr { } int - @IMPL_NAME@::prefix_length() const { + @IMPL_NAME@::prefix_length() const + { return (d_insert_phasing) ? d_nprepad + d_up_ramp.size() : d_nprepad; } int - @IMPL_NAME@::suffix_length() const { + @IMPL_NAME@::suffix_length() const + { return (d_insert_phasing) ? d_npostpad + d_down_ramp.size() : d_npostpad; } void - @IMPL_NAME@::write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace) { + @IMPL_NAME@::write_padding(@O_TYPE@ *&dst, int &nwritten, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); std::memset(dst, 0x00, nprocess * sizeof(@O_TYPE@)); dst += nprocess; @@ -210,8 +214,10 @@ namespace gr { void @IMPL_NAME@::copy_items(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, - int &nread, int nspace) { + int &nread, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); + propagate_tags(nread, nwritten, nprocess); std::memcpy(dst, src, nprocess * sizeof(@O_TYPE@)); dst += nprocess; nwritten += nprocess; @@ -222,7 +228,8 @@ namespace gr { void @IMPL_NAME@::apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, - int &nread, int nspace) { + int &nread, int nspace) + { int nprocess = std::min(d_limit - d_index, nspace); @O_TYPE@ *phasing; const @O_TYPE@ *ramp; @@ -239,6 +246,7 @@ namespace gr { if(d_insert_phasing) std::memcpy(dst, phasing, nprocess * sizeof(@O_TYPE@)); else { + propagate_tags(nread, nwritten, nprocess); VOLK_MULT_@O_TYPE@(dst, src, ramp, nprocess); src += nprocess; nread += nprocess; @@ -259,33 +267,48 @@ namespace gr { } void - @IMPL_NAME@::propagate_tags(std::vector<tag_t> &tags, int offset) + @IMPL_NAME@::propagate_tags(int in_offset, int out_offset, int count, bool skip) { - // FIXME: need to handle offsets correctly - std::vector<tag_t>::iterator tag; - for(tag = tags.begin(); tag != tags.end(); tag++) { - tag_t new_tag = *tag; - new_tag.offset = nitems_written(0) + offset; - add_item_tag(0, new_tag); + uint64_t abs_start = nitems_read(0) + in_offset; + uint64_t abs_end = abs_start + count; + uint64_t abs_offset = nitems_written(0) + out_offset; + tag_t temp_tag; + + std::vector<tag_t> tags; + std::vector<tag_t>::iterator it; + + get_tags_in_range(tags, 0, abs_start, abs_end); + + for(it = tags.begin(); it != tags.end(); it++) { + if(!pmt::equal(it->key, d_length_tag_key)) { + if(skip && (it->offset == d_length_tag_offset)) + continue; + temp_tag = *it; + temp_tag.offset = abs_offset + it->offset - abs_start; + add_item_tag(0, temp_tag); + } } } void - @IMPL_NAME@::enter_wait() { + @IMPL_NAME@::enter_wait() + { d_finished = true; d_index = 0; d_state = STATE_WAIT; } void - @IMPL_NAME@::enter_prepad() { + @IMPL_NAME@::enter_prepad() + { d_limit = d_nprepad; d_index = 0; d_state = STATE_PREPAD; } void - @IMPL_NAME@::enter_rampup() { + @IMPL_NAME@::enter_rampup() + { if(d_insert_phasing) d_limit = d_up_ramp.size(); else @@ -295,7 +318,8 @@ namespace gr { } void - @IMPL_NAME@::enter_copy() { + @IMPL_NAME@::enter_copy() + { if(d_insert_phasing) d_limit = d_ncopy; else @@ -307,7 +331,8 @@ namespace gr { } void - @IMPL_NAME@::enter_rampdown() { + @IMPL_NAME@::enter_rampdown() + { if(d_insert_phasing) d_limit = d_down_ramp.size(); else @@ -317,7 +342,8 @@ namespace gr { } void - @IMPL_NAME@::enter_postpad() { + @IMPL_NAME@::enter_postpad() + { d_limit = d_npostpad; d_index = 0; d_state = STATE_POSTPAD; diff --git a/gr-digital/lib/burst_shaper_XX_impl.h.t b/gr-digital/lib/burst_shaper_XX_impl.h.t index 4fa1cad9ea..99ad7fb08a 100644 --- a/gr-digital/lib/burst_shaper_XX_impl.h.t +++ b/gr-digital/lib/burst_shaper_XX_impl.h.t @@ -48,6 +48,7 @@ namespace gr { int d_ncopy; int d_limit; int d_index; + uint64_t d_length_tag_offset; bool d_finished; state_t d_state; @@ -57,7 +58,7 @@ namespace gr { void apply_ramp(@O_TYPE@ *&dst, const @I_TYPE@ *&src, int &nwritten, int &nread, int nspace); void add_length_tag(int offset); - void propagate_tags(std::vector<tag_t> &tags, int offset); + void propagate_tags(int in_offset, int out_offset, int count, bool skip=true); void enter_wait(); void enter_prepad(); void enter_rampup(); diff --git a/gr-digital/python/digital/qa_burst_shaper.py b/gr-digital/python/digital/qa_burst_shaper.py index b5aecd4614..f85b79ceec 100755 --- a/gr-digital/python/digital/qa_burst_shaper.py +++ b/gr-digital/python/digital/qa_burst_shaper.py @@ -25,6 +25,7 @@ from gnuradio import gr, gr_unittest from gnuradio import blocks, digital import pmt import numpy as np +import sys def make_length_tag(offset, length): return gr.python_to_tag({'offset' : offset, @@ -32,13 +33,15 @@ def make_length_tag(offset, length): 'value' : pmt.from_long(length), 'srcid' : pmt.intern('qa_burst_shaper')}) +def make_tag(offset, key, value): + return gr.python_to_tag({'offset' : offset, + 'key' : pmt.intern(key), + 'value' : value, + 'srcid' : pmt.intern('qa_burst_shaper')}) + def compare_tags(a, b): - a = gr.tag_to_python(a) - b = gr.tag_to_python(b) - return a.key == b.key and a.offset == b.offset and \ - a.value == b.value - #return a.key == b.key and a.offset == b.offset and \ - # a.srcid == b.srcid and a.value == b.value + return a.offset == b.offset and pmt.equal(a.key, b.key) and \ + pmt.equal(a.value, b.value) class qa_burst_shaper (gr_unittest.TestCase): @@ -276,6 +279,62 @@ class qa_burst_shaper (gr_unittest.TestCase): for i in xrange(len(etags)): self.assertTrue(compare_tags(sink.tags()[i], etags[i])) + def test_tag_propagation (self): + prepad = 10 + postpad = 10 + length1 = 15 + length2 = 25 + gap_len = 5 + lentag1_offset = 0 + lentag2_offset = length1 + gap_len + tag1_offset = 0 # accompanies first length tag + tag2_offset = length1 + gap_len # accompanies second length tag + tag3_offset = 2 # in ramp-up state + tag4_offset = length1 + 2 # in gap; tag will be dropped + tag5_offset = length1 + gap_len + 7 # in copy state + + data = np.concatenate((np.ones(length1), np.zeros(gap_len), + -1.0*np.ones(length2), np.zeros(10))) + window = np.concatenate((-2.0*np.ones(5), -4.0*np.ones(5))) + tags = (make_length_tag(lentag1_offset, length1), + make_length_tag(lentag2_offset, length2), + make_tag(tag1_offset, 'head', pmt.intern('tag1')), + make_tag(tag2_offset, 'head', pmt.intern('tag2')), + make_tag(tag3_offset, 'body', pmt.intern('tag3')), + make_tag(tag4_offset, 'body', pmt.intern('tag4')), + make_tag(tag5_offset, 'body', pmt.intern('tag5'))) + expected = np.concatenate((np.zeros(prepad), window[0:5], + np.ones(length1 - len(window)), window[5:10], + np.zeros(postpad + prepad), -1.0*window[0:5], + -1.0*np.ones(length2 - len(window)), + -1.0*window[5:10], np.zeros(postpad))) + elentag1_offset = 0 + elentag2_offset = length1 + prepad + postpad + etag1_offset = 0 + etag2_offset = elentag2_offset + etag3_offset = prepad + tag3_offset + etag5_offset = 2*prepad + postpad + tag5_offset - gap_len + etags = (make_length_tag(elentag1_offset, length1 + prepad + postpad), + make_length_tag(elentag2_offset, length2 + prepad + postpad), + make_tag(etag1_offset, 'head', pmt.intern('tag1')), + make_tag(etag2_offset, 'head', pmt.intern('tag2')), + make_tag(etag3_offset, 'body', pmt.intern('tag3')), + make_tag(etag5_offset, 'body', pmt.intern('tag5'))) + + # flowgraph + source = blocks.vector_source_f(data, tags=tags) + shaper = digital.burst_shaper_ff(window, pre_padding=prepad, + post_padding=postpad) + sink = blocks.vector_sink_f() + self.tb.connect(source, shaper, sink) + self.tb.run () + + # checks + self.assertFloatTuplesAlmostEqual(sink.data(), expected, 6) + for x, y in zip(sorted(sink.tags(), key=gr.tag_t_offset_compare_key()), + sorted(etags, key=gr.tag_t_offset_compare_key())): + self.assertTrue(compare_tags(x, y)) + if __name__ == '__main__': gr_unittest.run(qa_burst_shaper, "qa_burst_shaper.xml") |