diff options
Diffstat (limited to 'gnuradio-core/src/lib/runtime')
-rw-r--r-- | gnuradio-core/src/lib/runtime/Makefile.am | 8 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block.cc | 68 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block.h | 85 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block.i | 3 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block_detail.cc | 92 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block_detail.h | 73 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block_executor.cc | 87 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_block_executor.h | 3 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_buffer.cc | 71 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_buffer.h | 50 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc | 7 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_preferences.cc | 24 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_tag_info.cc | 38 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/gr_tag_info.h | 87 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/qa_block_tags.cc | 450 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/qa_block_tags.h | 52 | ||||
-rw-r--r-- | gnuradio-core/src/lib/runtime/qa_runtime.cc | 2 |
17 files changed, 1170 insertions, 30 deletions
diff --git a/gnuradio-core/src/lib/runtime/Makefile.am b/gnuradio-core/src/lib/runtime/Makefile.am index f67e8843d..dd9a8ea64 100644 --- a/gnuradio-core/src/lib/runtime/Makefile.am +++ b/gnuradio-core/src/lib/runtime/Makefile.am @@ -68,7 +68,8 @@ libruntime_la_SOURCES = \ gr_vmcircbuf_mmap_tmpfile.cc \ gr_vmcircbuf_createfilemapping.cc \ gr_vmcircbuf_sysv_shm.cc \ - gr_select_handler.cc + gr_select_handler.cc \ + gr_tag_info.cc libruntime_qa_la_SOURCES = \ qa_gr_block.cc \ @@ -79,6 +80,7 @@ libruntime_qa_la_SOURCES = \ qa_gr_top_block.cc \ qa_gr_io_signature.cc \ qa_gr_vmcircbuf.cc \ + qa_block_tags.cc \ qa_runtime.cc grinclude_HEADERS = \ @@ -121,7 +123,8 @@ grinclude_HEADERS = \ gr_tmp_path.h \ gr_types.h \ gr_unittests.h \ - gr_vmcircbuf.h + gr_vmcircbuf.h \ + gr_tag_info.h noinst_HEADERS = \ gr_vmcircbuf_mmap_shm_open.h \ @@ -136,6 +139,7 @@ noinst_HEADERS = \ qa_gr_io_signature.h \ qa_gr_top_block.h \ qa_gr_vmcircbuf.h \ + qa_block_tags.h \ qa_runtime.h if PYTHON diff --git a/gnuradio-core/src/lib/runtime/gr_block.cc b/gnuradio-core/src/lib/runtime/gr_block.cc index 8915f3360..81a55af9d 100644 --- a/gnuradio-core/src/lib/runtime/gr_block.cc +++ b/gnuradio-core/src/lib/runtime/gr_block.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2009 Free Software Foundation, Inc. + * Copyright 2004,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -36,7 +36,8 @@ gr_block::gr_block (const std::string &name, d_output_multiple (1), d_relative_rate (1.0), d_history(1), - d_fixed_rate(false) + d_fixed_rate(false), + d_tag_propagation_policy(TPP_ALL_TO_ALL) { } @@ -117,6 +118,69 @@ gr_block::fixed_rate_noutput_to_ninput(int noutput) throw std::runtime_error("Unimplemented"); } +uint64_t +gr_block::nitems_read(unsigned int which_input) +{ + if(d_detail) { + return d_detail->nitems_read(which_input); + } + else { + //throw std::runtime_error("No block_detail associated with block yet"); + return 0; + } +} + +uint64_t +gr_block::nitems_written(unsigned int which_output) +{ + if(d_detail) { + return d_detail->nitems_written(which_output); + } + else { + //throw std::runtime_error("No block_detail associated with block yet"); + return 0; + } +} + +void +gr_block::add_item_tag(unsigned int which_output, + uint64_t offset, + const pmt::pmt_t &key, + const pmt::pmt_t &value, + const pmt::pmt_t &srcid) +{ + d_detail->add_item_tag(which_output, offset, key, value, srcid); +} + +void +gr_block::get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_output, + uint64_t start, uint64_t end) +{ + d_detail->get_tags_in_range(v, which_output, start, end); +} + +void +gr_block::get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_output, + uint64_t start, uint64_t end, + const pmt::pmt_t &key) +{ + d_detail->get_tags_in_range(v, which_output, start, end, key); +} + +gr_block::tag_propagation_policy_t +gr_block::tag_propagation_policy() +{ + return d_tag_propagation_policy; +} + +void +gr_block::set_tag_propagation_policy(tag_propagation_policy_t p) +{ + d_tag_propagation_policy = p; +} + std::ostream& operator << (std::ostream& os, const gr_block *m) { diff --git a/gnuradio-core/src/lib/runtime/gr_block.h b/gnuradio-core/src/lib/runtime/gr_block.h index b6f724dde..ad7fa9555 100644 --- a/gnuradio-core/src/lib/runtime/gr_block.h +++ b/gnuradio-core/src/lib/runtime/gr_block.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2007,2009 Free Software Foundation, Inc. + * Copyright 2004,2007,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -63,6 +63,12 @@ class gr_block : public gr_basic_block { WORK_DONE = -1 }; + enum tag_propagation_policy_t { + TPP_DONT = 0, + TPP_ALL_TO_ALL = 1, + TPP_ONE_TO_ONE = 2 + }; + virtual ~gr_block (); /*! @@ -198,6 +204,26 @@ class gr_block : public gr_basic_block { */ virtual int fixed_rate_noutput_to_ninput(int noutput); + /*! + * \brief Return the number of items read on input stream which_input + */ + uint64_t nitems_read(unsigned int which_input); + + /*! + * \brief Return the number of items written on output stream which_output + */ + uint64_t nitems_written(unsigned int which_output); + + /*! + * \brief Asks for the policy used by the scheduler to moved tags downstream. + */ + tag_propagation_policy_t tag_propagation_policy(); + + /*! + * \brief Set the policy by the scheduler to determine how tags are moved downstream. + */ + void set_tag_propagation_policy(tag_propagation_policy_t p); + // ---------------------------------------------------------------------------- private: @@ -207,6 +233,7 @@ class gr_block : public gr_basic_block { gr_block_detail_sptr d_detail; // implementation details unsigned d_history; bool d_fixed_rate; + tag_propagation_policy_t d_tag_propagation_policy; // policy for moving tags downstream protected: @@ -216,6 +243,62 @@ class gr_block : public gr_basic_block { void set_fixed_rate(bool fixed_rate){ d_fixed_rate = fixed_rate; } + + /*! + * \brief Adds a new tag onto the given output buffer. + * + * \param which_output an integer of which output stream to attach the tag + * \param abs_offset a uint64 number of the absolute item number + * assicated with the tag. Can get from nitems_written. + * \param key the tag key as a PMT symbol + * \param value any PMT holding any value for the given key + * \param srcid optional source ID specifier; defaults to PMT_F + */ + void add_item_tag(unsigned int which_output, + uint64_t abs_offset, + const pmt::pmt_t &key, + const pmt::pmt_t &value, + const pmt::pmt_t &srcid=pmt::PMT_F); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range. + * + * Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + */ + void get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range + * with a given key. + * + * Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + * \param key a PMT symbol key to filter only tags of this key + */ + void get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt::pmt_t &key); + // These are really only for internal use, but leaving them public avoids // having to work up an ever-varying list of friends diff --git a/gnuradio-core/src/lib/runtime/gr_block.i b/gnuradio-core/src/lib/runtime/gr_block.i index f4ee43477..bb0c5f221 100644 --- a/gnuradio-core/src/lib/runtime/gr_block.i +++ b/gnuradio-core/src/lib/runtime/gr_block.i @@ -49,6 +49,9 @@ class gr_block : public gr_basic_block { bool start(); bool stop(); + uint64_t nitems_read(unsigned int which_input); + uint64_t nitems_written(unsigned int which_output); + // internal use gr_block_detail_sptr detail () const { return d_detail; } void set_detail (gr_block_detail_sptr detail) { d_detail = detail; } diff --git a/gnuradio-core/src/lib/runtime/gr_block_detail.cc b/gnuradio-core/src/lib/runtime/gr_block_detail.cc index 38d4a13ca..a360240c0 100644 --- a/gnuradio-core/src/lib/runtime/gr_block_detail.cc +++ b/gnuradio-core/src/lib/runtime/gr_block_detail.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2009 Free Software Foundation, Inc. + * Copyright 2004,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -27,6 +27,8 @@ #include <gr_block_detail.h> #include <gr_buffer.h> +using namespace pmt; + static long s_ncurrently_allocated = 0; long @@ -88,16 +90,20 @@ gr_block_detail::set_done (bool done) void gr_block_detail::consume (int which_input, int how_many_items) { - if (how_many_items > 0) + if (how_many_items > 0) { input (which_input)->update_read_pointer (how_many_items); + } } + void gr_block_detail::consume_each (int how_many_items) { - if (how_many_items > 0) - for (int i = 0; i < ninputs (); i++) + if (how_many_items > 0) { + for (int i = 0; i < ninputs (); i++) { d_input[i]->update_read_pointer (how_many_items); + } + } } void @@ -112,16 +118,88 @@ gr_block_detail::produce (int which_output, int how_many_items) void gr_block_detail::produce_each (int how_many_items) { - if (how_many_items > 0){ - for (int i = 0; i < noutputs (); i++) + if (how_many_items > 0) { + for (int i = 0; i < noutputs (); i++) { d_output[i]->update_write_pointer (how_many_items); + } d_produce_or |= how_many_items; } } void -gr_block_detail::_post(pmt::pmt_t msg) +gr_block_detail::_post(pmt_t msg) { d_tpb.insert_tail(msg); } + +uint64_t +gr_block_detail::nitems_read(unsigned int which_input) +{ + if(which_input >= d_ninputs) + throw std::invalid_argument ("gr_block_detail::n_input_items"); + return d_input[which_input]->nitems_read(); +} + +uint64_t +gr_block_detail::nitems_written(unsigned int which_output) +{ + if(which_output >= d_noutputs) + throw std::invalid_argument ("gr_block_detail::n_output_items"); + return d_output[which_output]->nitems_written(); +} + +void +gr_block_detail::add_item_tag(unsigned int which_output, + uint64_t abs_offset, + const pmt_t &key, + const pmt_t &value, + const pmt_t &srcid) +{ + if(!pmt_is_symbol(key)) { + throw pmt_wrong_type("gr_block_detail::add_item_tag key", key); + } + else { + // build tag tuple + pmt_t nitem = pmt_from_uint64(abs_offset); + pmt_t tuple = pmt_make_tuple(nitem, srcid, key, value); + + // Add tag to gr_buffer's deque tags + d_output[which_output]->add_item_tag(tuple); + } +} + +void +gr_block_detail::get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end) +{ + // get from gr_buffer_reader's deque of tags + d_input[which_input]->get_tags_in_range(v, abs_start, abs_end); +} + +void +gr_block_detail::get_tags_in_range(std::vector<pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt_t &key) +{ + std::vector<pmt_t> found_items; + + v.resize(0); + + // get from gr_buffer_reader's deque of tags + d_input[which_input]->get_tags_in_range(found_items, abs_start, abs_end); + + // Filter further by key name + pmt_t itemkey; + std::vector<pmt_t>::iterator itr; + for(itr = found_items.begin(); itr != found_items.end(); itr++) { + itemkey = pmt_tuple_ref(*itr, 2); + if(pmt_eqv(key, itemkey)) { + v.push_back(*itr); + } + } +} diff --git a/gnuradio-core/src/lib/runtime/gr_block_detail.h b/gnuradio-core/src/lib/runtime/gr_block_detail.h index c5787a5ad..d7ec3b136 100644 --- a/gnuradio-core/src/lib/runtime/gr_block_detail.h +++ b/gnuradio-core/src/lib/runtime/gr_block_detail.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2009 Free Software Foundation, Inc. + * Copyright 2004,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -25,6 +25,7 @@ #include <gr_runtime_types.h> #include <gr_tpb_detail.h> +#include <gr_tag_info.h> #include <stdexcept> /*! @@ -88,6 +89,75 @@ class gr_block_detail { */ void _post(pmt::pmt_t msg); + // Return the number of items read on input stream which_input + uint64_t nitems_read(unsigned int which_input); + + // Return the number of items written on output stream which_output + uint64_t nitems_written(unsigned int which_output); + + + /*! + * \brief Adds a new tag to the given output stream. + * + * This takes the input parameters and builds a PMT tuple + * from it. It then calls gr_buffer::add_item_tag(pmt::pmt_t t), + * which appends the tag onto its deque. + * + * \param which_output an integer of which output stream to attach the tag + * \param abs_offset a uint64 number of the absolute item number + * assicated with the tag. Can get from nitems_written. + * \param key the tag key as a PMT symbol + * \param value any PMT holding any value for the given key + * \param srcid a PMT source ID specifier + */ + void add_item_tag(unsigned int which_output, + uint64_t abs_offset, + const pmt::pmt_t &key, + const pmt::pmt_t &value, + const pmt::pmt_t &srcid); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range. + * + * Pass-through function to gr_buffer_reader to get a vector of tags + * in given range. Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + */ + void get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end); + + /*! + * \brief Given a [start,end), returns a vector of all tags in the range + * with a given key. + * + * Calls get_tags_in_range(which_input, abs_start, abs_end) to get a vector of + * tags from the buffers. This function then provides a secondary filter to + * the tags to extract only tags with the given 'key'. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param which_input an integer of which input stream to pull from + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + * \param key a PMT symbol to select only tags of this key + */ + void get_tags_in_range(std::vector<pmt::pmt_t> &v, + unsigned int which_input, + uint64_t abs_start, + uint64_t abs_end, + const pmt::pmt_t &key); + gr_tpb_detail d_tpb; // used by thread-per-block scheduler int d_produce_or; @@ -100,7 +170,6 @@ class gr_block_detail { std::vector<gr_buffer_sptr> d_output; bool d_done; - gr_block_detail (unsigned int ninputs, unsigned int noutputs); friend class gr_tpb_detail; diff --git a/gnuradio-core/src/lib/runtime/gr_block_executor.cc b/gnuradio-core/src/lib/runtime/gr_block_executor.cc index 2c21a0b0f..112150235 100644 --- a/gnuradio-core/src/lib/runtime/gr_block_executor.cc +++ b/gnuradio-core/src/lib/runtime/gr_block_executor.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2008,2009 Free Software Foundation, Inc. + * Copyright 2004,2008,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -87,7 +87,80 @@ min_available_space (gr_block_detail *d, int output_multiple) return min_space; } +static bool +propagate_tags(gr_block::tag_propagation_policy_t policy, gr_block_detail *d, + const std::vector<uint64_t> &start_nitems_read, double rrate, + std::vector<pmt::pmt_t> &rtags) +{ + // Move tags downstream + // if a sink, we don't need to move downstream; + // and do not bother if block uses TAGS_NONE attribute + if(d->sink_p()) { + return true; + } + switch(policy) { + case gr_block::TPP_DONT: + return true; + break; + case gr_block::TPP_ALL_TO_ALL: + // every tag on every input propogates to everyone downstream + for(int i = 0; i < d->ninputs(); i++) { + d->get_tags_in_range(rtags, i, start_nitems_read[i], + d->nitems_read(i)); + + std::vector<pmt::pmt_t>::iterator t; + if(rrate == 1.0) { + for(t = rtags.begin(); t != rtags.end(); t++) { + for(int o = 0; o < d->noutputs(); o++) + d->output(o)->add_item_tag(*t); + } + } + else { + for(t = rtags.begin(); t != rtags.end(); t++) { + uint64_t newcount = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(*t, 0)); + pmt::pmt_t newtup = pmt::mp(pmt::pmt_from_uint64(newcount * rrate), + pmt::pmt_tuple_ref(*t, 1), + pmt::pmt_tuple_ref(*t, 2), + pmt::pmt_tuple_ref(*t, 3)); + + for(int o = 0; o < d->noutputs(); o++) + d->output(o)->add_item_tag(newtup); + } + } + } + break; + case gr_block::TPP_ONE_TO_ONE: + // tags from input i only go to output i + // this requires d->ninputs() == d->noutputs; this is checked when this + // type of tag-propagation system is selected in gr_block_detail + if(d->ninputs() == d->noutputs()) { + for(int i = 0; i < d->ninputs(); i++) { + d->get_tags_in_range(rtags, i, start_nitems_read[i], + d->nitems_read(i)); + + std::vector<pmt::pmt_t>::iterator t; + for(t = rtags.begin(); t != rtags.end(); t++) { + uint64_t newcount = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(*t, 0)); + pmt::pmt_t newtup = pmt::mp(pmt::pmt_from_uint64(newcount * rrate), + pmt::pmt_tuple_ref(*t, 1), + pmt::pmt_tuple_ref(*t, 2), + pmt::pmt_tuple_ref(*t, 3)); + d->output(i)->add_item_tag(newtup); + } + } + } + else { + std::cerr << "Error: gr_block_executor: propagation_policy 'ONE-TO-ONE' requires ninputs == noutputs" << std::endl; + return false; + } + + break; + default: + return true; + } + return true; +} gr_block_executor::gr_block_executor (gr_block_sptr block) : d_block(block), d_log(0) @@ -134,6 +207,7 @@ gr_block_executor::run_one_iteration() d_input_items.resize (0); d_input_done.resize(0); d_output_items.resize (d->noutputs ()); + d_start_nitems_read.resize(0); // determine the minimum available output space noutput_items = min_available_space (d, m->output_multiple ()); @@ -155,6 +229,7 @@ gr_block_executor::run_one_iteration() d_input_items.resize (d->ninputs ()); d_input_done.resize(d->ninputs()); d_output_items.resize (0); + d_start_nitems_read.resize(d->ninputs()); LOG(*d_log << " sink\n"); max_items_avail = 0; @@ -198,6 +273,7 @@ gr_block_executor::run_one_iteration() d_input_items.resize (d->ninputs ()); d_input_done.resize(d->ninputs()); d_output_items.resize (d->noutputs ()); + d_start_nitems_read.resize(d->ninputs()); max_items_avail = 0; for (int i = 0; i < d->ninputs (); i++){ @@ -294,12 +370,21 @@ gr_block_executor::run_one_iteration() for (int i = 0; i < d->noutputs (); i++) d_output_items[i] = d->output(i)->write_pointer(); + // determine where to start looking for new tags + for (int i = 0; i < d->ninputs(); i++) + d_start_nitems_read[i] = d->nitems_read(i); + // Do the actual work of the block int n = m->general_work (noutput_items, d_ninput_items, d_input_items, d_output_items); LOG(*d_log << " general_work: noutput_items = " << noutput_items << " result = " << n << std::endl); + if(!propagate_tags(m->tag_propagation_policy(), d, + d_start_nitems_read, m->relative_rate(), + d_returned_tags)) + goto were_done; + if (n == gr_block::WORK_DONE) goto were_done; diff --git a/gnuradio-core/src/lib/runtime/gr_block_executor.h b/gnuradio-core/src/lib/runtime/gr_block_executor.h index 41b5ede7c..77ace5522 100644 --- a/gnuradio-core/src/lib/runtime/gr_block_executor.h +++ b/gnuradio-core/src/lib/runtime/gr_block_executor.h @@ -25,6 +25,7 @@ #include <gr_runtime_types.h> #include <fstream> +#include <gruel/pmt.h> //class gr_block_executor; //typedef boost::shared_ptr<gr_block_executor> gr_block_executor_sptr; @@ -47,6 +48,8 @@ protected: gr_vector_const_void_star d_input_items; std::vector<bool> d_input_done; gr_vector_void_star d_output_items; + std::vector<uint64_t> d_start_nitems_read; //stores where tag counts are before work + std::vector<pmt::pmt_t> d_returned_tags; public: gr_block_executor(gr_block_sptr block); diff --git a/gnuradio-core/src/lib/runtime/gr_buffer.cc b/gnuradio-core/src/lib/runtime/gr_buffer.cc index db2db5d6d..0b2eb52a0 100644 --- a/gnuradio-core/src/lib/runtime/gr_buffer.cc +++ b/gnuradio-core/src/lib/runtime/gr_buffer.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2009 Free Software Foundation, Inc. + * Copyright 2004,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -80,7 +80,7 @@ minimum_buffer_items (long type_size, long page_size) gr_buffer::gr_buffer (int nitems, size_t sizeof_item, gr_block_sptr link) : d_base (0), d_bufsize (0), d_vmcircbuf (0), d_sizeof_item (sizeof_item), d_link(link), - d_write_index (0), d_done (false) + d_write_index (0), d_abs_write_offset(0), d_done (false) { if (!allocate_buffer (nitems, sizeof_item)) throw std::bad_alloc (); @@ -156,8 +156,13 @@ gr_buffer::space_available () // Find out the maximum amount of data available to our readers int most_data = d_readers[0]->items_available (); - for (unsigned int i = 1; i < d_readers.size (); i++) + uint64_t min_items_read = d_readers[0]->nitems_read(); + for (size_t i = 1; i < d_readers.size (); i++) { most_data = std::max (most_data, d_readers[i]->items_available ()); + min_items_read = std::min(min_items_read, d_readers[i]->nitems_read()); + } + + prune_tags(min_items_read); // The -1 ensures that the case d_write_index == d_read_index is // unambiguous. It indicates that there is no data for the reader @@ -177,6 +182,7 @@ gr_buffer::update_write_pointer (int nitems) { gruel::scoped_lock guard(*mutex()); d_write_index = index_add (d_write_index, nitems); + d_abs_write_offset += nitems; } void @@ -215,6 +221,40 @@ gr_buffer::drop_reader (gr_buffer_reader *reader) d_readers.erase (result); } +void +gr_buffer::add_item_tag(const pmt::pmt_t &tag) +{ + gruel::scoped_lock guard(*mutex()); + d_item_tags.push_back(tag); +} + +void +gr_buffer::prune_tags(uint64_t max_time) +{ + /* NOTE: this function _should_ lock the mutex before editing + d_item_tags. In practice, this function is only called at + runtime by min_available_space in gr_block_executor.cc, + which locks the mutex itself. + + If this function is used elsewhere, remember to lock the + buffer's mutex al la the scoped_lock line below. + */ + //gruel::scoped_lock guard(*mutex()); + + int n = 0; + uint64_t item_time; + std::deque<pmt::pmt_t>::iterator itr = d_item_tags.begin(); + + while(itr != d_item_tags.end()) { + item_time = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(*itr, 0)); + if(item_time < max_time) { + d_item_tags.pop_front(); + n++; + } + itr++; + } +} + long gr_buffer_ncurrently_allocated () { @@ -225,7 +265,7 @@ gr_buffer_ncurrently_allocated () gr_buffer_reader::gr_buffer_reader(gr_buffer_sptr buffer, unsigned int read_index, gr_block_sptr link) - : d_buffer(buffer), d_read_index(read_index), d_link(link) + : d_buffer(buffer), d_read_index(read_index), d_abs_read_offset(0), d_link(link) { s_buffer_reader_count++; } @@ -253,6 +293,29 @@ gr_buffer_reader::update_read_pointer (int nitems) { gruel::scoped_lock guard(*mutex()); d_read_index = d_buffer->index_add (d_read_index, nitems); + d_abs_read_offset += nitems; +} + +void +gr_buffer_reader::get_tags_in_range(std::vector<pmt::pmt_t> &v, + uint64_t abs_start, + uint64_t abs_end) +{ + gruel::scoped_lock guard(*mutex()); + + v.resize(0); + std::deque<pmt::pmt_t>::iterator itr = d_buffer->get_tags_begin(); + + uint64_t item_time; + while(itr != d_buffer->get_tags_end()) { + item_time = pmt::pmt_to_uint64(pmt::pmt_tuple_ref(*itr, 0)); + + if((item_time >= abs_start) && (item_time <= abs_end)) { + v.push_back(*itr); + } + + itr++; + } } long diff --git a/gnuradio-core/src/lib/runtime/gr_buffer.h b/gnuradio-core/src/lib/runtime/gr_buffer.h index 207bfe7c5..fe0e7585d 100644 --- a/gnuradio-core/src/lib/runtime/gr_buffer.h +++ b/gnuradio-core/src/lib/runtime/gr_buffer.h @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2004,2009 Free Software Foundation, Inc. + * Copyright 2004,2009,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -26,6 +26,7 @@ #include <gr_runtime_types.h> #include <boost/weak_ptr.hpp> #include <gruel/thread.h> +#include <gruel/pmt.h> class gr_vmcircbuf; @@ -88,6 +89,26 @@ class gr_buffer { gruel::mutex *mutex() { return &d_mutex; } + uint64_t nitems_written() { return d_abs_write_offset; } + + + /*! + * \brief Adds a new tag to the buffer. + * + * \param tag a PMT tuple containing the new tag + */ + void add_item_tag(const pmt::pmt_t &tag); + + /*! + * \brief Removes all tags before \p max_time from buffer + * + * \param max_time the time (item number) to trim up until. + */ + void prune_tags(uint64_t max_time); + + std::deque<pmt::pmt_t>::iterator get_tags_begin() { return d_item_tags.begin(); } + std::deque<pmt::pmt_t>::iterator get_tags_end() { return d_item_tags.end(); } + // ------------------------------------------------------------------------- private: @@ -106,11 +127,15 @@ class gr_buffer { boost::weak_ptr<gr_block> d_link; // block that writes to this buffer // - // The mutex protects d_write_index, d_done and the d_read_index's in the buffer readers. + // The mutex protects d_write_index, d_abs_write_offset, d_done, d_item_tags + // and the d_read_index's and d_abs_read_offset's in the buffer readers. // gruel::mutex d_mutex; unsigned int d_write_index; // in items [0,d_bufsize) + uint64_t d_abs_write_offset; // num items written since the start bool d_done; + std::deque<pmt::pmt_t> d_item_tags; + unsigned index_add (unsigned a, unsigned b) @@ -220,11 +245,31 @@ class gr_buffer_reader { gruel::mutex *mutex() { return d_buffer->mutex(); } + uint64_t nitems_read() { return d_abs_read_offset; } + /*! * \brief Return the block that reads via this reader. + * */ gr_block_sptr link() { return gr_block_sptr(d_link); } + + /*! + * \brief Given a [start,end), returns a vector all tags in the range. + * + * Get a vector of tags in given range. Range of counts is from start to end-1. + * + * Tags are tuples of: + * (item count, source id, key, value) + * + * \param v a vector reference to return tags into + * \param abs_start a uint64 count of the start of the range of interest + * \param abs_end a uint64 count of the end of the range of interest + */ + void get_tags_in_range(std::vector<pmt::pmt_t> &v, + uint64_t abs_start, + uint64_t abs_end); + // ------------------------------------------------------------------------- private: @@ -236,6 +281,7 @@ class gr_buffer_reader { gr_buffer_sptr d_buffer; unsigned int d_read_index; // in items [0,d->buffer.d_bufsize) + uint64_t d_abs_read_offset; // num items seen since the start boost::weak_ptr<gr_block> d_link; // block that reads via this buffer reader //! constructor is private. Use gr_buffer::add_reader to create instances diff --git a/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc index 031eb6dfd..5d1057e0f 100644 --- a/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc +++ b/gnuradio-core/src/lib/runtime/gr_flat_flowgraph.cc @@ -264,16 +264,13 @@ void gr_flat_flowgraph::dump() int no = detail->noutputs(); for (int i = 0; i < no; i++) { gr_buffer_sptr buffer = detail->output(i); - std::cout << " output " << i << ": " << buffer - << " space=" << buffer->space_available() << std::endl; + std::cout << " output " << i << ": " << buffer << std::endl; } for (int i = 0; i < ni; i++) { gr_buffer_reader_sptr reader = detail->input(i); std::cout << " reader " << i << ": " << reader - << " reading from buffer=" << reader->buffer() - << " avail=" << reader->items_available() << " items" - << std::endl; + << " reading from buffer=" << reader->buffer() << std::endl; } } diff --git a/gnuradio-core/src/lib/runtime/gr_preferences.cc b/gnuradio-core/src/lib/runtime/gr_preferences.cc index e0be2db62..5f7412248 100644 --- a/gnuradio-core/src/lib/runtime/gr_preferences.cc +++ b/gnuradio-core/src/lib/runtime/gr_preferences.cc @@ -1,6 +1,6 @@ /* -*- c++ -*- */ /* - * Copyright 2003 Free Software Foundation, Inc. + * Copyright 2003,2010 Free Software Foundation, Inc. * * This file is part of GNU Radio * @@ -77,11 +77,20 @@ gr_preferences::get (const char *key) static char buf[1024]; FILE *fp = fopen (pathname (key), "r"); - if (fp == 0) + if (fp == 0) { + perror (pathname (key)); return 0; + } memset (buf, 0, sizeof (buf)); - fread (buf, 1, sizeof (buf) - 1, fp); + size_t ret = fread (buf, 1, sizeof (buf) - 1, fp); + if(ret == 0) { + if(ferror(fp) != 0) { + perror (pathname (key)); + fclose (fp); + return 0; + } + } fclose (fp); return buf; } @@ -97,6 +106,13 @@ gr_preferences::set (const char *key, const char *value) return; } - fwrite (value, 1, strlen (value), fp); + size_t ret = fwrite (value, 1, strlen (value), fp); + if(ret == 0) { + if(ferror(fp) != 0) { + perror (pathname (key)); + fclose (fp); + return; + } + } fclose (fp); }; diff --git a/gnuradio-core/src/lib/runtime/gr_tag_info.cc b/gnuradio-core/src/lib/runtime/gr_tag_info.cc new file mode 100644 index 000000000..f15329f9b --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tag_info.cc @@ -0,0 +1,38 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gr_tag_info.h> +#include <gruel/pmt.h> + +namespace gr_tags { +/* + const pmt::pmt_t key_time = pmt::pmt_string_to_symbol("time"); + const pmt::pmt_t key_sample_rate = pmt::pmt_string_to_symbol("sample_rate"); + const pmt::pmt_t key_frequency = pmt::pmt_string_to_symbol("frequency"); + const pmt::pmt_t key_rssi = pmt::pmt_string_to_symbol("rssi"); + const pmt::pmt_t key_rx_gain = pmt::pmt_string_to_symbol("gain"); +*/ +} diff --git a/gnuradio-core/src/lib/runtime/gr_tag_info.h b/gnuradio-core/src/lib/runtime/gr_tag_info.h new file mode 100644 index 000000000..5a1e1555a --- /dev/null +++ b/gnuradio-core/src/lib/runtime/gr_tag_info.h @@ -0,0 +1,87 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_GR_TAG_INFO_H +#define INCLUDED_GR_TAG_INFO_H + +#include <gruel/pmt.h> + +namespace gr_tags { + + enum { + TAG_NITEM_REF = 0, + TAG_SRCID_REF, + TAG_KEY_REF, + TAG_VALUE_REF + }; + + /* + extern const pmt::pmt_t key_time; + extern const pmt::pmt_t key_sample_rate; + extern const pmt::pmt_t key_frequency; + extern const pmt::pmt_t key_rssi; + extern const pmt::pmt_t key_gain; + */ + + /*! + * \brief Returns the item \p tag occurred at (as a uint64_t) + */ + static inline uint64_t + get_nitems(const pmt::pmt_t &tag) { + return pmt::pmt_to_uint64(pmt::pmt_tuple_ref(tag, TAG_NITEM_REF)); + } + + /*! + * \brief Returns the source ID of \p tag (as a PMT) + */ + static inline pmt::pmt_t + get_srcid(const pmt::pmt_t &tag) { + return pmt::pmt_tuple_ref(tag, TAG_SRCID_REF); + } + + /*! + * \brief Returns the key of \p tag (as a PMT symbol) + */ + static inline pmt::pmt_t + get_key(const pmt::pmt_t &tag) { + return pmt::pmt_tuple_ref(tag, TAG_KEY_REF); + } + + /*! + * \brief Returns the value of \p tag (as a PMT) + */ + static inline pmt::pmt_t + get_value(const pmt::pmt_t &tag) { + return pmt::pmt_tuple_ref(tag, TAG_VALUE_REF); + } + + /*! + * \brief Comparison function to test which tag, \p x or \p y, came first in time + */ + static inline bool + nitems_compare(pmt::pmt_t x, pmt::pmt_t y) { + return get_nitems(x) < get_nitems(y); + } + +}; /* namespace tags */ + +#endif /* GR_TAG_INFO */ diff --git a/gnuradio-core/src/lib/runtime/qa_block_tags.cc b/gnuradio-core/src/lib/runtime/qa_block_tags.cc new file mode 100644 index 000000000..07ce5c276 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_block_tags.cc @@ -0,0 +1,450 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <qa_block_tags.h> +#include <gr_block.h> +#include <gr_top_block.h> +#include <gr_null_source.h> +#include <gr_null_sink.h> +#include <gr_head.h> +#include <gr_annotator_alltoall.h> +#include <gr_annotator_1to1.h> +#include <gr_keep_one_in_n.h> +#include <gr_firdes.h> +#include <gruel/pmt.h> + + +// ---------------------------------------------------------------- + +using namespace pmt; + +// set to 1 to turn on debug output +// The debug output fully checks that the tags seen are what are expected. While +// this behavior currently works with our implementation, there is no guarentee +// that the tags will be coming in this specific order, so it's dangerous to +// rely on this as a test of the tag system working. We would really want to +// tags we know we should see and then test that they all occur once, but in no +// particular order. +#define QA_TAGS_DEBUG 0 + +void +qa_block_tags::t0 () +{ + unsigned int N = 1000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_block_sptr snk (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, snk, 0); + + //CPPUNIT_ASSERT_THROW(src->nitems_read(0), std::runtime_error); + //CPPUNIT_ASSERT_THROW(src->nitems_written(0), std::runtime_error); + CPPUNIT_ASSERT_EQUAL(src->nitems_read(0), (uint64_t)0); + CPPUNIT_ASSERT_EQUAL(src->nitems_written(0), (uint64_t)0); + + tb->run(); + + CPPUNIT_ASSERT_THROW(src->nitems_read(0), std::invalid_argument); + CPPUNIT_ASSERT(src->nitems_written(0) >= N); + CPPUNIT_ASSERT_EQUAL(snk->nitems_read(0), (uint64_t)1000); + CPPUNIT_ASSERT_THROW(snk->nitems_written(0), std::invalid_argument); +} + + +void +qa_block_tags::t1 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann3 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann4 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, ann3, 0); + tb->connect(ann2, 0, ann4, 0); + + tb->connect(ann3, 0, snk0, 0); + tb->connect(ann4, 0, snk1, 0); + + tb->run(); + + std::vector<pmt::pmt_t> tags0 = ann0->data(); + std::vector<pmt::pmt_t> tags3 = ann3->data(); + std::vector<pmt::pmt_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)8); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)8); + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags3[8]; + expected_tags3[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags3[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags3[2] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags3[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags3[4] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags3[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags3[6] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags3[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + + pmt_t expected_tags4[8]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str2.str()), mp("seq"), mp(0)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[2] = mp(pmt_from_uint64(10000), mp(str2.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[4] = mp(pmt_from_uint64(20000), mp(str2.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[6] = mp(pmt_from_uint64(30000), mp(str2.str()), mp("seq"), mp(3)); + expected_tags4[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t1" << std::endl; + + // For annotator 3, we know it gets tags from ann0 and ann1, test this + for(size_t i = 0; i < tags3.size(); i++) { + std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags3[i]), pmt_write_string(expected_tags3[i])); + } + + // For annotator 4, we know it gets tags from ann0 and ann2, test this + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + +void +qa_block_tags::t2 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann3 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann4 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk2 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann1, 1); + tb->connect(ann1, 0, ann2, 0); + tb->connect(ann1, 1, ann3, 0); + tb->connect(ann1, 2, ann4, 0); + + tb->connect(ann2, 0, snk0, 0); + tb->connect(ann3, 0, snk1, 0); + tb->connect(ann4, 0, snk2, 0); + + tb->run(); + + std::vector<pmt::pmt_t> tags0 = ann0->data(); + std::vector<pmt::pmt_t> tags1 = ann1->data(); + std::vector<pmt::pmt_t> tags2 = ann2->data(); + std::vector<pmt::pmt_t> tags3 = ann4->data(); + std::vector<pmt::pmt_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags1.size(), (size_t)8); + + // Make sure the rest all have 12 tags + CPPUNIT_ASSERT_EQUAL(tags2.size(), (size_t)12); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)12); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)12); + + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + + pmt_t expected_tags2[12]; + expected_tags2[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags2[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags2[2] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags2[3] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags2[4] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags2[5] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags2[6] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(6)); + expected_tags2[7] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags2[8] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags2[9] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(9)); + expected_tags2[10] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + expected_tags2[11] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + pmt_t expected_tags4[12]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(2)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags4[2] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(5)); + expected_tags4[4] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[6] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(8)); + expected_tags4[7] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags4[8] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[9] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(11)); + expected_tags4[10] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + expected_tags4[11] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t2" << std::endl; + + // For annotator[2-4], we know it gets tags from ann0 and ann1 + // but the tags from the different outputs of ann1 are different for each. + // Just testing ann2 and ann4; if they are correct it would be + // inconceivable for ann3 to have it wrong. + for(size_t i = 0; i < tags2.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags2[i]), pmt_write_string(expected_tags2[i])); + } + + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + + +void +qa_block_tags::t3 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_1to1_sptr ann0 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(10000, sizeof(int))); + gr_annotator_1to1_sptr ann3 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann4 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(head, 0, ann0, 1); + + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, ann3, 0); + tb->connect(ann2, 0, ann4, 0); + + tb->connect(ann3, 0, snk0, 0); + tb->connect(ann4, 0, snk1, 0); + + tb->run(); + + + std::vector<pmt::pmt_t> tags0 = ann0->data(); + std::vector<pmt::pmt_t> tags3 = ann3->data(); + std::vector<pmt::pmt_t> tags4 = ann4->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags3.size(), (size_t)8); + CPPUNIT_ASSERT_EQUAL(tags4.size(), (size_t)8); + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags3[8]; + expected_tags3[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags3[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags3[2] = mp(pmt_from_uint64(10000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags3[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags3[4] = mp(pmt_from_uint64(20000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags3[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(4)); + expected_tags3[6] = mp(pmt_from_uint64(30000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags3[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(6)); + + pmt_t expected_tags4[8]; + expected_tags4[0] = mp(pmt_from_uint64(0), mp(str2.str()), mp("seq"), mp(0)); + expected_tags4[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(1)); + expected_tags4[2] = mp(pmt_from_uint64(10000), mp(str2.str()), mp("seq"), mp(1)); + expected_tags4[3] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags4[4] = mp(pmt_from_uint64(20000), mp(str2.str()), mp("seq"), mp(2)); + expected_tags4[5] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(5)); + expected_tags4[6] = mp(pmt_from_uint64(30000), mp(str2.str()), mp("seq"), mp(3)); + expected_tags4[7] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(7)); + + std::cout << std::endl << "qa_block_tags::t3" << std::endl; + + // For annotator 3, we know it gets tags from ann0 and ann1, test this + for(size_t i = 0; i < tags3.size(); i++) { + std::cout << "tags3[" << i << "] = " << tags3[i] << "\t\t" << expected_tags3[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags3[i]), pmt_write_string(expected_tags3[i])); + } + + // For annotator 4, we know it gets tags from ann0 and ann2, test this + std::cout << std::endl; + for(size_t i = 0; i < tags4.size(); i++) { + std::cout << "tags4[" << i << "] = " << tags4[i] << "\t\t" << expected_tags4[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags4[i]), pmt_write_string(expected_tags4[i])); + } +#endif +} + + +void +qa_block_tags::t4 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(int))); + gr_block_sptr head (gr_make_head(sizeof(int), N)); + gr_annotator_1to1_sptr ann0 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann1 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_annotator_1to1_sptr ann2 (gr_make_annotator_1to1(10000, sizeof(int))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(int))); + gr_block_sptr snk1 (gr_make_null_sink(sizeof(int))); + + // using 1-to-1 tag propagation without having equal number of + // ins and outs. Make sure this works; will just exit run early. + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann0, 1, ann2, 0); + tb->connect(ann1, 0, snk0, 0); + tb->connect(ann2, 0, snk1, 0); + + std::cerr << std::endl + << "NOTE: This is supposed to produce an error from gr_block_executor" + << std::endl; + tb->run(); +} + + +void +qa_block_tags::t5 () +{ + int N = 40000; + gr_top_block_sptr tb = gr_make_top_block("top"); + gr_block_sptr src (gr_make_null_source(sizeof(float))); + gr_block_sptr head (gr_make_head(sizeof(float), N)); + gr_annotator_alltoall_sptr ann0 (gr_make_annotator_alltoall(10000, sizeof(float))); + gr_annotator_alltoall_sptr ann1 (gr_make_annotator_alltoall(10000, sizeof(float))); + gr_annotator_alltoall_sptr ann2 (gr_make_annotator_alltoall(1000, sizeof(float))); + gr_block_sptr snk0 (gr_make_null_sink(sizeof(float))); + + // Rate change blocks + gr_keep_one_in_n_sptr dec10 (gr_make_keep_one_in_n(sizeof(float), 10)); + + tb->connect(src, 0, head, 0); + tb->connect(head, 0, ann0, 0); + tb->connect(ann0, 0, ann1, 0); + tb->connect(ann1, 0, dec10, 0); + tb->connect(dec10, 0, ann2, 0); + tb->connect(ann2, 0, snk0, 0); + + tb->run(); + + std::vector<pmt::pmt_t> tags0 = ann0->data(); + std::vector<pmt::pmt_t> tags1 = ann1->data(); + std::vector<pmt::pmt_t> tags2 = ann2->data(); + + // The first annotator does not receive any tags from the null sink upstream + CPPUNIT_ASSERT_EQUAL(tags0.size(), (size_t)0); + CPPUNIT_ASSERT_EQUAL(tags1.size(), (size_t)4); + CPPUNIT_ASSERT_EQUAL(tags2.size(), (size_t)8); + + +#if QA_TAGS_DEBUG + // Kludge together the tags that we know should result from the above graph + std::stringstream str0, str1, str2; + str0 << ann0->name() << ann0->unique_id(); + str1 << ann1->name() << ann1->unique_id(); + str2 << ann2->name() << ann2->unique_id(); + + pmt_t expected_tags1[5]; + expected_tags1[0] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags1[1] = mp(pmt_from_uint64(10000), mp(str0.str()), mp("seq"), mp(1)); + expected_tags1[2] = mp(pmt_from_uint64(20000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags1[3] = mp(pmt_from_uint64(30000), mp(str0.str()), mp("seq"), mp(3)); + + pmt_t expected_tags2[10]; + expected_tags2[0] = mp(pmt_from_uint64(0), mp(str1.str()), mp("seq"), mp(0)); + expected_tags2[1] = mp(pmt_from_uint64(0), mp(str0.str()), mp("seq"), mp(0)); + expected_tags2[2] = mp(pmt_from_uint64(1000), mp(str1.str()), mp("seq"), mp(1)); + expected_tags2[3] = mp(pmt_from_uint64(1000), mp(str0.str()), mp("seq"), mp(1)); + expected_tags2[4] = mp(pmt_from_uint64(2000), mp(str1.str()), mp("seq"), mp(2)); + expected_tags2[5] = mp(pmt_from_uint64(2000), mp(str0.str()), mp("seq"), mp(2)); + expected_tags2[6] = mp(pmt_from_uint64(3000), mp(str1.str()), mp("seq"), mp(3)); + expected_tags2[7] = mp(pmt_from_uint64(3000), mp(str0.str()), mp("seq"), mp(3)); + expected_tags2[8] = mp(pmt_from_uint64(4000), mp(str1.str()), mp("seq"), mp(4)); + expected_tags2[9] = mp(pmt_from_uint64(4000), mp(str0.str()), mp("seq"), mp(4)); + + std::cout << std::endl << "qa_block_tags::t5" << std::endl; + + // annotator 1 gets tags from annotator 0 + std::cout << "tags1.size(): " << tags1.size() << std::endl; + for(size_t i = 0; i < tags1.size(); i++) { + std::cout << "tags1[" << i << "] = " << tags1[i] << "\t\t" << expected_tags1[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags1[i]), pmt_write_string(expected_tags1[i])); + } + + // annotator 2 gets tags from annotators 0 and 1 + std::cout << std::endl; + std::cout << "tags2.size(): " << tags2.size() << std::endl; + for(size_t i = 0; i < tags2.size(); i++) { + std::cout << "tags2[" << i << "] = " << tags2[i] << "\t\t" << expected_tags2[i] << std::endl; + CPPUNIT_ASSERT_EQUAL(pmt_write_string(tags2[i]), pmt_write_string(expected_tags2[i])); + } +#endif +} + diff --git a/gnuradio-core/src/lib/runtime/qa_block_tags.h b/gnuradio-core/src/lib/runtime/qa_block_tags.h new file mode 100644 index 000000000..b0d211390 --- /dev/null +++ b/gnuradio-core/src/lib/runtime/qa_block_tags.h @@ -0,0 +1,52 @@ +/* -*- c++ -*- */ +/* + * Copyright 2010 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * 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 + * the Free Software Foundation; either version 3, or (at your option) + * any later version. + * + * GNU Radio is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef INCLUDED_QA_BLOCK_TAGS_H +#define INCLUDED_QA_BLOCK_TAGS_H + +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/TestCase.h> +#include <stdexcept> + +class qa_block_tags : public CppUnit::TestCase { + + CPPUNIT_TEST_SUITE (qa_block_tags); + CPPUNIT_TEST (t0); + CPPUNIT_TEST (t1); + CPPUNIT_TEST (t2); + CPPUNIT_TEST (t3); + CPPUNIT_TEST (t4); + CPPUNIT_TEST (t5); + CPPUNIT_TEST_SUITE_END (); + + private: + void t0 (); + void t1 (); + void t2 (); + void t3 (); + void t4 (); + void t5 (); + +}; + + +#endif /* INCLUDED_QA_BLOCK_TAGS_H */ diff --git a/gnuradio-core/src/lib/runtime/qa_runtime.cc b/gnuradio-core/src/lib/runtime/qa_runtime.cc index 31e3a82d6..967d4bfa8 100644 --- a/gnuradio-core/src/lib/runtime/qa_runtime.cc +++ b/gnuradio-core/src/lib/runtime/qa_runtime.cc @@ -38,6 +38,7 @@ #include <qa_gr_hier_block2.h> #include <qa_gr_hier_block2_derived.h> #include <qa_gr_buffer.h> +#include <qa_block_tags.h> CppUnit::TestSuite * qa_runtime::suite () @@ -52,6 +53,7 @@ qa_runtime::suite () s->addTest (qa_gr_hier_block2::suite ()); s->addTest (qa_gr_hier_block2_derived::suite ()); s->addTest (qa_gr_buffer::suite ()); + s->addTest (qa_block_tags::suite ()); return s; } |